Merge branch 'jk/send-email-complete-aliases'
authorJunio C Hamano <gitster@pobox.com>
Tue, 15 Dec 2015 17:33:19 +0000 (09:33 -0800)
committerJunio C Hamano <gitster@pobox.com>
Tue, 15 Dec 2015 17:33:19 +0000 (09:33 -0800)
A fix-up for recent topic.

* jk/send-email-complete-aliases:
completion: fix completing unstuck email alias arguments

147 files changed:
.travis.yml [new file with mode: 0644]
Documentation/RelNotes/2.6.4.txt [new file with mode: 0644]
Documentation/RelNotes/2.6.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.7.0.txt
Documentation/config.txt
Documentation/diff-options.txt
Documentation/git-check-ignore.txt
Documentation/git-check-ref-format.txt
Documentation/git-p4.txt
Documentation/git-submodule.txt
Documentation/git-update-index.txt
Documentation/git.txt
GIT-VERSION-GEN
Makefile
archive.c
bisect.c
block-sha1/sha1.h
branch.c
builtin/am.c
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/clone.c
builtin/commit-tree.c
builtin/commit.c
builtin/describe.c
builtin/diff-tree.c
builtin/diff.c
builtin/fast-export.c
builtin/fetch-pack.c
builtin/fetch.c
builtin/fmt-merge-msg.c
builtin/fsck.c
builtin/grep.c
builtin/index-pack.c
builtin/init-db.c
builtin/log.c
builtin/ls-remote.c
builtin/merge-base.c
builtin/merge-tree.c
builtin/merge.c
builtin/name-rev.c
builtin/notes.c
builtin/pack-objects.c
builtin/pull.c
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/shortlog.c
builtin/show-branch.c
builtin/show-ref.c
builtin/unpack-objects.c
builtin/worktree.c
bundle.c
cache-tree.c
cache.h
combine-diff.c
commit.c
compat/apple-common-crypto.h
compat/sha1-chunked.c [new file with mode: 0644]
compat/sha1-chunked.h [new file with mode: 0644]
config.c
configure.ac
connect.c
contrib/completion/git-prompt.sh
contrib/rerere-train.sh
contrib/subtree/git-subtree.sh
contrib/subtree/t/Makefile
contrib/subtree/t/t7900-subtree.sh
contrib/subtree/todo
credential-cache--daemon.c
credential-store.c
decorate.c
diff-lib.c
fast-import.c
fetch-pack.c
fsck.c
git-filter-branch.sh
git-p4.py
git-rebase--interactive.sh
git-send-email.perl
http-backend.c
http-push.c
http.c
ident.c
line-log.c
list-objects.c
log-tree.c
merge-blobs.c
merge-recursive.c
merge.c
notes-merge.c
object.c
object.h
pack-bitmap-write.c
pack-bitmap.c
pack-check.c
parse-options.c
parse-options.h
patch-ids.c
path.c
ppc/sha1.h
pretty.c
ref-filter.c
refs.c
refs/files-backend.c [new file with mode: 0644]
refs/refs-internal.h [new file with mode: 0644]
remote-curl.c
remote.c
remote.h
revision.c
send-pack.c
sequencer.c
server-info.c
sha1_file.c
sha1_name.c
shallow.c
submodule.c
t/annotate-tests.sh
t/lib-git-p4.sh
t/perf/p7000-filter-branch.sh [new file with mode: 0755]
t/t1450-fsck.sh
t/t3404-rebase-interactive.sh
t/t5504-fetch-receive-strict.sh
t/t5516-fetch-push.sh
t/t5571-pre-push-hook.sh
t/t5813-proto-disable-ssh.sh
t/t7003-filter-branch.sh
t/t9300-fast-import.sh
t/t9800-git-p4-basic.sh
t/t9807-git-p4-submit.sh
t/t9826-git-p4-keep-empty-commits.sh [new file with mode: 0755]
t/t9903-bash-prompt.sh
t/test-lib-functions.sh
tag.c
test-match-trees.c
transport-helper.c
transport.c
transport.h
tree.c
upload-pack.c
walker.c
wt-status.c
diff --git a/.travis.yml b/.travis.yml
new file mode 100644 (file)
index 0000000..c3bf9c6
--- /dev/null
@@ -0,0 +1,91 @@
+language: c
+
+os:
+  - linux
+  - osx
+
+compiler:
+  - clang
+  - gcc
+
+addons:
+  apt:
+    packages:
+    - language-pack-is
+
+env:
+  global:
+    - P4_VERSION="15.2"
+    - GIT_LFS_VERSION="1.1.0"
+    - DEFAULT_TEST_TARGET=prove
+    - GIT_PROVE_OPTS="--timer --jobs 3"
+    - GIT_TEST_OPTS="--verbose --tee"
+    - CFLAGS="-g -O2 -Wall -Werror"
+    - GIT_TEST_CLONE_2GB=YesPlease
+    # t9810 occasionally fails on Travis CI OS X
+    # t9816 occasionally fails with "TAP out of sequence errors" on Travis CI OS X
+    - GIT_SKIP_TESTS="t9810 t9816"
+
+before_install:
+  - >
+    case "${TRAVIS_OS_NAME:-linux}" in
+    linux)
+      mkdir --parents custom/p4
+      pushd custom/p4
+        wget --quiet http://filehost.perforce.com/perforce/r$P4_VERSION/bin.linux26x86_64/p4d
+        wget --quiet http://filehost.perforce.com/perforce/r$P4_VERSION/bin.linux26x86_64/p4
+        chmod u+x p4d
+        chmod u+x p4
+        export PATH="$(pwd):$PATH"
+      popd
+      mkdir --parents custom/git-lfs
+      pushd custom/git-lfs
+        wget --quiet https://github.com/github/git-lfs/releases/download/v$GIT_LFS_VERSION/git-lfs-linux-amd64-$GIT_LFS_VERSION.tar.gz
+        tar --extract --gunzip --file "git-lfs-linux-amd64-$GIT_LFS_VERSION.tar.gz"
+        cp git-lfs-$GIT_LFS_VERSION/git-lfs .
+        export PATH="$(pwd):$PATH"
+      popd
+      ;;
+    osx)
+      brew_force_set_latest_binary_hash () {
+        FORMULA=$1
+        SHA=$(brew fetch --force $FORMULA 2>&1 | grep ^SHA256: | cut -d ' ' -f 2)
+        sed -E -i.bak "s/sha256 \"[0-9a-f]{64}\"/sha256 \"$SHA\"/g" \
+          /usr/local/Library/Taps/homebrew/homebrew-binary/$FORMULA.rb
+      }
+      brew update --quiet
+      brew tap homebrew/binary --quiet
+      brew_force_set_latest_binary_hash perforce
+      brew_force_set_latest_binary_hash perforce-server
+      brew install git-lfs perforce-server perforce gettext
+      brew link --force gettext
+      ;;
+    esac;
+    echo "$(tput setaf 6)Perforce Server Version$(tput sgr0)";
+    p4d -V | grep Rev.;
+    echo "$(tput setaf 6)Perforce Client Version$(tput sgr0)";
+    p4 -V | grep Rev.;
+    echo "$(tput setaf 6)Git-LFS Version$(tput sgr0)";
+    git-lfs version;
+
+before_script: make --jobs=2
+
+script: make --quiet test
+
+after_failure:
+  - >
+    : '<-- Click here to see detailed test output!                                                        ';
+    for TEST_EXIT in t/test-results/*.exit;
+    do
+      if [ "$(cat "$TEST_EXIT")" != "0" ];
+      then
+        TEST_OUT="${TEST_EXIT%exit}out";
+        echo "------------------------------------------------------------------------";
+        echo "$(tput setaf 1)${TEST_OUT}...$(tput sgr0)";
+        echo "------------------------------------------------------------------------";
+        cat "${TEST_OUT}";
+      fi;
+    done;
+
+notifications:
+  email: false
diff --git a/Documentation/RelNotes/2.6.4.txt b/Documentation/RelNotes/2.6.4.txt
new file mode 100644 (file)
index 0000000..b0256a2
--- /dev/null
@@ -0,0 +1,63 @@
+Git v2.6.4 Release Notes
+========================
+
+Fixes since v2.6.3
+------------------
+
+ * The "configure" script did not test for -lpthread correctly, which
+   upset some linkers.
+
+ * Add support for talking http/https over socks proxy.
+
+ * Portability fix for Windows, which may rewrite $SHELL variable using
+   non-POSIX paths.
+
+ * We now consistently allow all hooks to ignore their standard input,
+   rather than having git complain of SIGPIPE.
+
+ * Fix shell quoting in contrib script.
+
+ * Test portability fix for a topic in v2.6.1.
+
+ * Allow tilde-expansion in some http config variables.
+
+ * Give a useful special case "diff/show --word-diff-regex=." as an
+   example in the documentation.
+
+ * Fix for a corner case in filter-branch.
+
+ * Make git-p4 work on a detached head.
+
+ * Documentation clarification for "check-ignore" without "--verbose".
+
+ * Just like the working tree is cleaned up when the user cancelled
+   submission in P4Submit.applyCommit(), clean up the mess if "p4
+   submit" fails.
+
+ * Having a leftover .idx file without corresponding .pack file in
+   the repository hurts performance; "git gc" learned to prune them.
+
+ * The code to prepare the working tree side of temporary directory
+   for the "dir-diff" feature forgot that symbolic links need not be
+   copied (or symlinked) to the temporary area, as the code already
+   special cases and overwrites them.  Besides, it was wrong to try
+   computing the object name of the target of symbolic link, which may
+   not even exist or may be a directory.
+
+ * There was no way to defeat a configured rebase.autostash variable
+   from the command line, as "git rebase --no-autostash" was missing.
+
+ * Allow "git interpret-trailers" to run outside of a Git repository.
+
+ * Produce correct "dirty" marker for shell prompts, even when we
+   are on an orphan or an unborn branch.
+
+ * Some corner cases have been fixed in string-matching done in "git
+   status".
+
+ * Apple's common crypto implementation of SHA1_Update() does not take
+   more than 4GB at a time, and we now have a compile-time workaround
+   for it.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
diff --git a/Documentation/RelNotes/2.6.5.txt b/Documentation/RelNotes/2.6.5.txt
new file mode 100644 (file)
index 0000000..e1b75fb
--- /dev/null
@@ -0,0 +1,25 @@
+Git v2.6.5 Release Notes
+========================
+
+Fixes since v2.6.4
+------------------
+
+ * Because "test_when_finished" in our test framework queues the
+   clean-up tasks to be done in a shell variable, it should not be
+   used inside a subshell.  Add a mechanism to allow 'bash' to catch
+   such uses, and fix the ones that were found.
+
+ * Update "git subtree" (in contrib/) so that it can take whitespaces
+   in the pathnames, not only in the in-tree pathname but the name of
+   the directory that the repository is in.
+
+ * Cosmetic improvement to lock-file error messages.
+
+ * mark_tree_uninteresting() has code to handle the case where it gets
+   passed a NULL pointer in its 'tree' parameter, but the function had
+   'object = &tree->object' assignment before checking if tree is
+   NULL.  This gives a compiler an excuse to declare that tree will
+   never be NULL and apply a wrong optimization.  Avoid it.
+
+Also contains typofixes, documentation updates and trivial code
+clean-ups.
index ca2c24bea259ba531449829f5a6fec80c0bf53b6..29b4cfe0bfa244536c8ca8c32c1c952d5aaf68d1 100644 (file)
@@ -51,7 +51,6 @@ UI, Workflows & Features
  * Prepare for Git on-disk repository representation to undergo
    backward incompatible changes by introducing a new repository
    format version "1", with an extension mechanism.
-   (merge 067fbd4 jk/repository-extension later to maint).
 
  * "git worktree" learned a "list" subcommand.
 
@@ -66,6 +65,20 @@ UI, Workflows & Features
    a superset of "--no-progress".  Extend the command to support the
    usual "--[no-]progress".
 
+ * The semantics of tranfer.hideRefs configuration variable have been
+   extended to work better with the ref "namespace" feature that lets
+   you throw unrelated bunches of repositories in a single physical
+   repository and virtually serve them as separate ones.
+
+ * send-email config variables whose values are pathnames now go
+   through the ~username/ expansion.
+
+ * bash completion learnt to TAB-complete recipient addresses given
+   to send-email.
+
+ * The credential-cache daemon can be told to ignore SIGHUP to work
+   around issue when running Git from inside emacs.
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -84,7 +97,6 @@ Performance, Internal Implementation, Development Support etc.
    clean-up tasks to be done in a shell variable, it should not be
    used inside a subshell.  Add a mechanism to allow 'bash' to catch
    such uses, and fix the ones that were found.
-   (merge 0968f12 jk/test-lint-forbid-when-finished-in-subshell later to maint).
 
  * The debugging infrastructure for pkt-line based communication has
    been improved to mark the side-band communication specifically.
@@ -106,7 +118,6 @@ Performance, Internal Implementation, Development Support etc.
  * The internal stripspace() function has been moved to where it
    logically belongs to, i.e. strbuf API, and the command line parser
    of "git stripspace" has been updated to use the parse_options API.
-   (merge bed4452 tk/stripspace later to maint).
 
  * "git am" used to spawn "git mailinfo" via run_command() API once
    per each patch, but learned to make a direct call to mailinfo()
@@ -118,6 +129,39 @@ Performance, Internal Implementation, Development Support etc.
  * With a "debug" helper, debugging of a single "git" invocation in
    our test scripts has become a lot easier.
 
+ * The "configure" script did not test for -lpthread correctly, which
+   upset some linkers.
+
+ * Cross completed task off of subtree project's todo list.
+
+ * Test cleanups for the subtree project.
+
+ * Clean up style in an ancient test t9300.
+
+ * Work around some test flakiness with p4d.
+
+ * Fsck did not correctly detect a NUL-truncated header in a tag.
+
+ * Use a safer behavior when we hit errors verifying remote certificates.
+
+ * Speed up filter-branch for cases where we only care about rewriting
+   commits, not tree data.
+
+ * The parse-options API has been updated to make "-h" command line
+   option work more consistently in all commands.
+
+ * "git svn rebase/mkdirs" got optimized by keeping track of empty
+   directories better.
+
+ * Fix some racy client/server tests by treating SIGPIPE the same as a
+   normal non-zero exit.
+
+ * The necessary infrastructure to build topics using the free Travis
+   CI has been added. Developers forking from this topic (and enabling
+   Travis) can do their own builds, and we can turn on auto-builds for
+   git/git (including build-status for pull requests that people
+   open).
+
 
 Also contains various documentation updates and code clean-ups.
 
@@ -133,63 +177,51 @@ notes for details).
    (which is not a great UI element as they can only appear at the end
    of the command line).  Add notice to documentation of each and
    every one of them.
-   (merge 2b594bf mm/keyid-docs later to maint).
 
  * "git blame --first-parent v1.0..v2.0" was not rejected but did not
    limit the blame to commits on the first parent chain.
-   (merge 95a4fb0 jk/blame-first-parent later to maint).
 
  * "git subtree" (in contrib/) now can take whitespaces in the
    pathnames, not only in the in-tree pathname but the name of the
-   directory that the repository is in.  (merge 5b6ab38
-   as/subtree-with-spaces later to maint).
+   directory that the repository is in.
 
  * The ssh transport, just like any other transport over the network,
    did not clear GIT_* environment variables, but it is possible to
    use SendEnv and AcceptEnv to leak them to the remote invocation of
    Git, which is not a good idea at all.  Explicitly clear them just
    like we do for the local transport.
-   (merge a48b409 jk/connect-clear-env later to maint).
 
  * Correct "git p4 --detect-labels" so that it does not fail to create
    a tag that points at a commit that is also being imported.
-   (merge b43702a ld/p4-import-labels later to maint).
 
  * The Makefile always runs the library archiver with hardcoded "crs"
    options, which was inconvenient for exotic platforms on which
    people want to use programs with totally different set of command
    line options.
-   (merge ac179b4 jw/make-arflags-customizable later to maint).
 
  * Customization to change the behaviour with "make -w" and "make -s"
    in our Makefile was broken when they were used together.
-   (merge ef49e05 jk/make-findstring-makeflags-fix later to maint).
 
  * Allocation related functions and stdio are unsafe things to call
    inside a signal handler, and indeed killing the pager can cause
    glibc to deadlock waiting on allocation mutex as our signal handler
    tries to free() some data structures in wait_for_pager().  Reduce
    these unsafe calls.
-   (merge 507d780 ti/glibc-stdio-mutex-from-signal-handler later to maint).
 
  * The way how --ref/--notes to specify the notes tree reference are
    DWIMmed was not clearly documented.
-   (merge e14c92e jk/notes-dwim-doc later to maint).
 
  * "git gc" used to barf when a symbolic ref has gone dangling
    (e.g. the branch that used to be your upstream's default when you
    cloned from it is now gone, and you did "fetch --prune").
-   (merge 14886b4 js/gc-with-stale-symref later to maint).
 
  * "git clone --dissociate" runs a big "git repack" process at the
    end, and it helps to close file descriptors that are open on the
    packs and their idx files before doing so on filesystems that
    cannot remove a file that is still open.
-   (merge 786b150 js/clone-dissociate later to maint).
 
  * Description of the "log.follow" configuration variable in "git log"
    documentation is now also copied to "git config" documentation.
-   (merge fd8d07e dt/log-follow-config later to maint).
 
  * "git rebase -i" had a minor regression recently, which stopped
    considering a line that begins with an indented '#' in its insn
@@ -197,93 +229,73 @@ notes for details).
    Windows where CRLF left by the editor is turned into a trailing CR
    on the line read via the "read" built-in command of bash.  Both of
    these issues are now fixed.
-   (merge 39743cf gr/rebase-i-drop-warn later to maint).
 
  * After "git checkout --detach", "git status" reported a fairly
    useless "HEAD detached at HEAD", instead of saying at which exact
    commit.
-   (merge 0eb8548 mm/detach-at-HEAD-reflog later to maint).
 
  * When "git send-email" wanted to talk over Net::SMTP::SSL,
    Net::Cmd::datasend() did not like to be fed too many bytes at the
    same time and failed to send messages.  Send the payload one line
    at a time to work around the problem.
-   (merge f60c483 sa/send-email-smtp-batch-data-limit later to maint).
 
  * When "git am" was rewritten as a built-in, it stopped paying
    attention to user.signingkey, which was fixed.
-   (merge 434c64d pt/am-builtin later to maint).
 
  * It was not possible to use a repository-lookalike created by "git
    worktree add" as a local source of "git clone".
-   (merge d78db84 nd/clone-linked-checkout later to maint).
 
  * On a case insensitive filesystems, setting GIT_WORK_TREE variable
    using a random cases that does not agree with what the filesystem
    thinks confused Git that it wasn't inside the working tree.
-   (merge 63ec5e1 js/icase-wt-detection later to maint).
 
  * Performance-measurement tests did not work without an installed Git.
-   (merge 31cd128 sb/perf-without-installed-git later to maint).
 
  * A test script for the HTTP service had a timing dependent bug,
    which was fixed.
-   (merge 362d8b6 sb/http-flaky-test-fix later to maint).
 
  * There were some classes of errors that "git fsck" diagnosed to its
    standard error that did not cause it to exit with non-zero status.
-   (merge 122f76f jc/fsck-dropped-errors later to maint).
 
  * Work around "git p4" failing when the P4 depot records the contents
    in UTF-16 without UTF-16 BOM.
-   (merge 1f5f390 ls/p4-translation-failure later to maint).
 
  * When "git gc --auto" is backgrounded, its diagnosis message is
    lost.  Save it to a file in $GIT_DIR and show it next time the "gc
    --auto" is run.
-   (merge 329e6e8 nd/gc-auto-background-fix later to maint).
 
  * The submodule code has been taught to work better with separate
    work trees created via "git worktree add".
-   (merge 11f9dd7 mk/submodule-gitdir-path later to maint).
 
  * "git gc" is safe to run anytime only because it has the built-in
    grace period to protect young objects.  In order to run with no
    grace period, the user must make sure that the repository is
    quiescent.
-   (merge fae1a90 jc/doc-gc-prune-now later to maint).
 
  * A recent "filter-branch --msg-filter" broke skipping of the commit
    object header, which is fixed.
-   (merge a5a4b3f jk/filter-branch-use-of-sed-on-incomplete-line later to maint).
 
  * The normalize_ceiling_entry() function does not muck with the end
    of the path it accepts, and the real world callers do rely on that,
    but a test insisted that the function drops a trailing slash.
-   (merge b2a7123 rd/test-path-utils later to maint).
 
  * A test for interaction between untracked cache and sparse checkout
    added in Git 2.5 days were flaky.
-   (merge 9b680fb dt/t7063-fix-flaky-test later to maint).
 
  * A couple of commands still showed "[options]" in their usage string
    to note where options should come on their command line, but we
    spell that "[<options>]" in most places these days.
-   (merge d96a031 rt/placeholder-in-usage later to maint).
 
  * The synopsis text and the usage string of subcommands that read
    list of things from the standard input are often shown as if they
    only take input from a file on a filesystem, which was misleading.
-   (merge 33e8fc8 jc/usage-stdin later to maint).
 
  * "git am -3" had a small regression where it is aborted in its error
    handling codepath when underlying merge-recursive failed in certain
    ways, as it assumed that the internal call to merge-recursive will
    never die, which is not the case (yet).
-   (merge c63d4b2 jc/am-3-fallback-regression-fix later to maint).
 
  * The linkage order of libraries was wrong in places around libcurl.
-   (merge 7e91e8d rp/link-curl-before-ssl later to maint).
 
  * The name-hash subsystem that is used to cope with case insensitive
    filesystems keeps track of directories and their on-filesystem
@@ -293,40 +305,31 @@ notes for details).
    cache entry was removed from the index, leading to use after free.
    This was fixed by recording the path for each directory instead of
    borrowing cache entries and restructuring the API somewhat.
-   (merge 41284eb dt/name-hash-dir-entry-fix later to maint).
 
  * "git merge-file" tried to signal how many conflicts it found, which
    obviously would not work well when there are too many of them.
-   (merge e34f802 jk/merge-file-exit-code later to maint).
 
  * The error message from "git blame --contents --reverse" incorrectly
    talked about "--contents --children".
-   (merge 9526197 mk/blame-error-message later to maint).
 
  * "git imap-send" did not compile well with older version of cURL library.
-   (merge 71d9257 js/imap-send-curl-compilation-fix later to maint).
 
  * Merging a branch that removes a path and another that changes the
    mode bits on the same path should have conflicted at the path, but
    it didn't and silently favoured the removal.
-   (merge 72fac66 jk/delete-modechange-conflict later to maint).
 
  * "git --literal-pathspecs add -u/-A" without any command line
    argument misbehaved ever since Git 2.0.
-   (merge 29abb33 jc/add-u-A-default-to-top later to maint).
 
  * "git daemon" uses "run_command()" without "finish_command()", so it
    needs to release resources itself, which it forgot to do.
-   (merge b1b49ff rs/daemon-plug-child-leak later to maint).
 
  * "git status --branch --short" accessed beyond the constant string
    "HEAD", which has been corrected.
-   (merge c72b49d rs/wt-status-detached-branch-fix later to maint).
 
  * We peek objects from submodule's object store by linking it to the
    list of alternate object databases, but the code to do so forgot to
    correctly initialize the list.
-   (merge 9a6e4f0 jk/initialization-fix-to-add-submodule-odb later to maint).
 
  * The code to prepare the working tree side of temporary directory
    for the "dir-diff" feature forgot that symbolic links need not be
@@ -334,28 +337,31 @@ notes for details).
    special cases and overwrites them.  Besides, it was wrong to try
    computing the object name of the target of symbolic link, which may
    not even exist or may be a directory.
-   (merge cfe2d4b da/difftool later to maint).
 
  * A Range: request can be responded with a full response and when
    asked properly libcurl knows how to strip the result down to the
    requested range.  However, we were hand-crafting a range request
    and it did not kick in.
 
+ * Having a leftover .idx file without corresponding .pack file in
+   the repository hurts performance; "git gc" learned to prune them.
+
+ * Apple's common crypto implementation of SHA1_Update() does not take
+   more than 4GB at a time, and we now have a compile-time workaround
+   for it.
+
+ * Produce correct "dirty" marker for shell prompts, even when we
+   are on an orphan or an unborn branch.
+
+ * A build without NO_IPv6 used to use gethostbyname() when guessing
+   user's hostname, instead of getaddrinfo() that is used in other
+   codepaths in such a build.
+   (merge 00bce77 ep/ident-with-getaddrinfo later to maint).
+
+ * The exit code of git-fsck didnot reflect some types of errors found
+   in packed objects, which has been corrected.
+   (merge 8c24d83 dt/fsck-verify-pack-error later to maint).
+
  * Code clean-up, minor fixes etc.
    (merge 15ed07d jc/rerere later to maint).
-   (merge e7a7401 pt/pull-builtin later to maint).
-   (merge 29bc480 nd/ls-remote-does-not-have-u-option later to maint).
-   (merge be510e0 jk/asciidoctor-section-heading-markup-fix later to maint).
-   (merge 83e6bda tk/typofix-connect-unknown-proto-error later to maint).
-   (merge a43eb67 tk/doc-interpret-trailers-grammo later to maint).
-   (merge ba128e2 es/worktree-add-cleanup later to maint).
-   (merge 44cd91e cc/quote-comments later to maint).
    (merge 147875f sb/submodule-config-parse later to maint).
-   (merge ae9f274 es/worktree-add later to maint).
-   (merge 3b19dba jc/em-dash-in-doc later to maint).
-   (merge f3f38c7 jc/everyday-markup later to maint).
-   (merge 77d5f71 xf/user-manual-markup later to maint).
-   (merge b2af482 xf/user-manual-ff later to maint).
-   (merge e510ab8 rs/pop-commit later to maint).
-   (merge fdcdb77 js/misc-fixes later to maint).
-   (merge c949b00 rs/show-branch-argv-array later to maint).
index b4b01948d0c52a42a322ed2cdeb33f475bf08c22..2d06b11f25e66779e84d9762c825a5d9c0a39ee0 100644 (file)
@@ -1122,6 +1122,9 @@ credential.<url>.*::
        example.com. See linkgit:gitcredentials[7] for details on how URLs are
        matched.
 
+credentialCache.ignoreSIGHUP::
+       Tell git-credential-cache--daemon to ignore SIGHUP, instead of quitting.
+
 include::diff-config.txt[]
 
 difftool.<tool>.path::
index d56ca90998a740dbe4cc5c242326023b4cc769b3..306b7e360409255a7aafaa3860d6d6c0412fb870 100644 (file)
@@ -267,6 +267,9 @@ expression to make sure that it matches all non-whitespace characters.
 A match that contains a newline is silently truncated(!) at the
 newline.
 +
+For example, `--word-diff-regex=.` will treat each character as a word
+and, correspondingly, show differences character by character.
++
 The regex can also be set via a diff driver or configuration option, see
 linkgit:gitattributes[1] or linkgit:git-config[1].  Giving it explicitly
 overrides any diff driver or configuration setting.  Diff drivers
index 59531abba481c4788357fc07342fcc7672727fbf..e94367a5ed8e8b94bca7036ff7616bc3cfa9ca97 100644 (file)
@@ -16,10 +16,9 @@ DESCRIPTION
 -----------
 
 For each pathname given via the command-line or from a file via
-`--stdin`, show the pattern from .gitignore (or other input files to
-the exclude mechanism) that decides if the pathname is excluded or
-included.  Later patterns within a file take precedence over earlier
-ones.
+`--stdin`, check whether the file is excluded by .gitignore (or other
+input files to the exclude mechanism) and output the path if it is
+excluded.
 
 By default, tracked files are not shown at all since they are not
 subject to exclude rules; but see `--no-index'.
@@ -32,7 +31,8 @@ OPTIONS
 
 -v, --verbose::
        Also output details about the matching pattern (if any)
-       for each given pathname.
+       for each given pathname. For precedence rules within and
+       between exclude sources, see linkgit:gitignore[5].
 
 --stdin::
        Read pathnames from the standard input, one per line,
index 9044dfaadae10047281dfdfd06a75e634e1309fa..91a3622ee4e270a11c699e310fb4d544e37845c9 100644 (file)
@@ -60,7 +60,7 @@ Git imposes the following rules on how references are named:
 
 These rules make it easy for shell script based tools to parse
 reference names, pathname expansion by the shell when a reference name is used
-unquoted (by mistake), and also avoids ambiguities in certain
+unquoted (by mistake), and also avoid ambiguities in certain
 reference name expressions (see linkgit:gitrevisions[7]):
 
 . A double-dot `..` is often used as in `ref1..ref2`, and in some
index c3ff7d0d9b1a62709128c985bcc7e8943756949a..738cfde10c84b606e2d341fc2ff597403d1b987b 100644 (file)
@@ -549,6 +549,10 @@ git-p4.largeFilePush::
        Boolean variable which defines if large files are automatically
        pushed to a server.
 
+git-p4.keepEmptyCommits::
+       A changelist that contains only excluded files will be imported
+       as an empty commit if this boolean option is set to true.
+
 Submit variables
 ~~~~~~~~~~~~~~~~
 git-p4.detectRenames::
index f17687e09d322f909ae4236c297e769d2e949b90..1572f058f59ad1f75c4b5ed6e34fae9110414bad 100644 (file)
@@ -237,6 +237,9 @@ sync::
 +
 "git submodule sync" synchronizes all submodules while
 "git submodule sync \-- A" synchronizes submodule "A" only.
++
+If `--recursive` is specified, this command will recurse into the
+registered submodules, and sync any nested submodules within.
 
 OPTIONS
 -------
@@ -364,7 +367,7 @@ the submodule itself.
 for linkgit:git-clone[1]'s `--reference` and `--shared` options carefully.
 
 --recursive::
-       This option is only valid for foreach, update and status commands.
+       This option is only valid for foreach, update, status and sync commands.
        Traverse submodules recursively. The operation is performed not
        only in the submodules of the current repo, but also
        in any nested submodules inside those submodules (and so on).
index 1a296bc29a16fcf4ee6b581310033861c1ec82bc..f4e5a85351d7a7fdda24ac497560f59ccdb6ef7b 100644 (file)
@@ -17,6 +17,8 @@ SYNOPSIS
             [--[no-]assume-unchanged]
             [--[no-]skip-worktree]
             [--ignore-submodules]
+            [--[no-]split-index]
+            [--[no-|force-]untracked-cache]
             [--really-refresh] [--unresolve] [--again | -g]
             [--info-only] [--index-info]
             [-z] [--stdin] [--index-version <n>]
index c2e2a94e754fb0f831f1604634901d4c155784af..cbf157be43ea50a5a49d66d16ee91ccb4b62e10c 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.6.3/git.html[documentation for release 2.6.3]
+* link:v2.6.4/git.html[documentation for release 2.6.4]
 
 * release notes for
+  link:RelNotes/2.6.4.txt[2.6.4],
   link:RelNotes/2.6.3.txt[2.6.3],
   link:RelNotes/2.6.2.txt[2.6.2],
   link:RelNotes/2.6.1.txt[2.6.1],
@@ -1056,7 +1057,7 @@ of clones and fetches.
        cloning of shallow repositories.
        See 'GIT_TRACE' for available trace output options.
 
-GIT_LITERAL_PATHSPECS::
+'GIT_LITERAL_PATHSPECS'::
        Setting this variable to `1` will cause Git to treat all
        pathspecs literally, rather than as glob patterns. For example,
        running `GIT_LITERAL_PATHSPECS=1 git log -- '*.c'` will search
@@ -1065,15 +1066,15 @@ GIT_LITERAL_PATHSPECS::
        literal paths to Git (e.g., paths previously given to you by
        `git ls-tree`, `--raw` diff output, etc).
 
-GIT_GLOB_PATHSPECS::
+'GIT_GLOB_PATHSPECS'::
        Setting this variable to `1` will cause Git to treat all
        pathspecs as glob patterns (aka "glob" magic).
 
-GIT_NOGLOB_PATHSPECS::
+'GIT_NOGLOB_PATHSPECS'::
        Setting this variable to `1` will cause Git to treat all
        pathspecs as literal (aka "literal" magic).
 
-GIT_ICASE_PATHSPECS::
+'GIT_ICASE_PATHSPECS'::
        Setting this variable to `1` will cause Git to treat all
        pathspecs as case-insensitive.
 
@@ -1087,7 +1088,7 @@ GIT_ICASE_PATHSPECS::
        variable when it is invoked as the top level command by the
        end user, to be recorded in the body of the reflog.
 
-`GIT_REF_PARANOIA`::
+'GIT_REF_PARANOIA'::
        If set to `1`, include broken or badly named refs when iterating
        over lists of refs. In a normal, non-corrupted repository, this
        does nothing. However, enabling it may help git to detect and
@@ -1098,7 +1099,7 @@ GIT_ICASE_PATHSPECS::
        an operation has touched every ref (e.g., because you are
        cloning a repository to make a backup).
 
-`GIT_ALLOW_PROTOCOL`::
+'GIT_ALLOW_PROTOCOL'::
        If set, provide a colon-separated list of protocols which are
        allowed to be used with fetch/push/clone. This is useful to
        restrict recursive submodule initialization from an untrusted
index 94e40c400078794f48d36236bd105a2d3be59d7c..29c43c21e1fd5a85a668ef3e795024609715ce14 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.6.0.GIT
+DEF_VER=v2.7.0-rc0
 
 LF='
 '
index 43ceeb966c51e4e0b684ac64c2c5cbf0aa943cbf..fd19b544c21ac4648adb8e86421ec5a8632dc0fb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -142,6 +142,10 @@ all::
 # Define PPC_SHA1 environment variable when running make to make use of
 # a bundled SHA1 routine optimized for PowerPC.
 #
+# 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.
+#
 # Define NEEDS_CRYPTO_WITH_SSL if you need -lcrypto when using -lssl (Darwin).
 #
 # Define NEEDS_SSL_WITH_CRYPTO if you need -lssl when using -lcrypto (Darwin).
@@ -768,6 +772,7 @@ LIB_OBJS += reachable.o
 LIB_OBJS += read-cache.o
 LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
+LIB_OBJS += refs/files-backend.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace_object.o
@@ -1338,6 +1343,11 @@ ifdef NO_POSIX_GOODIES
        BASIC_CFLAGS += -DNO_POSIX_GOODIES
 endif
 
+ifdef APPLE_COMMON_CRYPTO
+       # Apple CommonCrypto requires chunking
+       SHA1_MAX_BLOCK_SIZE = 1024L*1024L*1024L
+endif
+
 ifdef BLK_SHA1
        SHA1_HEADER = "block-sha1/sha1.h"
        LIB_OBJS += block-sha1/sha1.o
@@ -1356,6 +1366,10 @@ endif
 endif
 endif
 
+ifdef SHA1_MAX_BLOCK_SIZE
+       LIB_OBJS += compat/sha1-chunked.o
+       BASIC_CFLAGS += -DSHA1_MAX_BLOCK_SIZE="$(SHA1_MAX_BLOCK_SIZE)"
+endif
 ifdef NO_PERL_MAKEMAKER
        export NO_PERL_MAKEMAKER
 endif
@@ -2419,7 +2433,7 @@ profile-clean:
        $(RM) $(addsuffix *.gcno,$(addprefix $(PROFILE_DIR)/, $(object_dirs)))
 
 clean: profile-clean coverage-clean
-       $(RM) *.o *.res block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
+       $(RM) *.o *.res refs/*.o block-sha1/*.o ppc/*.o compat/*.o compat/*/*.o
        $(RM) xdiff/*.o vcs-svn/*.o ewah/*.o builtin/*.o
        $(RM) $(LIB_FILE) $(XDIFF_LIB) $(VCSSVN_LIB)
        $(RM) $(ALL_PROGRAMS) $(SCRIPT_LIB) $(BUILT_INS) git$X
index 4ac86c837384c45c33dae141c3ff53dd37eb0fd5..0687afae434e55d9b8a38aa5184e6e056b963dac 100644 (file)
--- a/archive.c
+++ b/archive.c
@@ -241,7 +241,7 @@ int write_archive_entries(struct archiver_args *args,
                        len--;
                if (args->verbose)
                        fprintf(stderr, "%.*s\n", (int)len, args->base);
-               err = write_entry(args, args->tree->object.sha1, args->base,
+               err = write_entry(args, args->tree->object.oid.hash, args->base,
                                  len, 040777);
                if (err)
                        return err;
@@ -374,7 +374,7 @@ static void parse_treeish_arg(const char **argv,
 
        commit = lookup_commit_reference_gently(oid.hash, 1);
        if (commit) {
-               commit_sha1 = commit->object.sha1;
+               commit_sha1 = commit->object.oid.hash;
                archive_time = commit->date;
        } else {
                commit_sha1 = NULL;
@@ -390,7 +390,7 @@ static void parse_treeish_arg(const char **argv,
                unsigned int mode;
                int err;
 
-               err = get_tree_entry(tree->object.sha1, prefix,
+               err = get_tree_entry(tree->object.oid.hash, prefix,
                                     tree_oid.hash, &mode);
                if (err || !S_ISDIR(mode))
                        die("current working directory is untracked");
index 053d1a2ab91a6c41a0fe957569ff905d12b00bee..42aa7aa6aa1e53e66bafd47d76b783514dcfabf1 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -193,7 +193,7 @@ static int compare_commit_dist(const void *a_, const void *b_)
        b = (struct commit_dist *)b_;
        if (a->distance != b->distance)
                return b->distance - a->distance; /* desc sort */
-       return hashcmp(a->commit->object.sha1, b->commit->object.sha1);
+       return oidcmp(&a->commit->object.oid, &b->commit->object.oid);
 }
 
 static struct commit_list *best_bisection_sorted(struct commit_list *list, int nr)
@@ -500,7 +500,7 @@ struct commit_list *filter_skipped(struct commit_list *list,
                struct commit_list *next = list->next;
                list->next = NULL;
                if (0 <= sha1_array_lookup(&skipped_revs,
-                                          list->item->object.sha1)) {
+                                          list->item->object.oid.hash)) {
                        if (skipped_first && !*skipped_first)
                                *skipped_first = 1;
                        /* Move current to tried list */
@@ -575,7 +575,7 @@ static struct commit_list *skip_away(struct commit_list *list, int count)
 
        for (i = 0; cur; cur = cur->next, i++) {
                if (i == index) {
-                       if (hashcmp(cur->item->object.sha1, current_bad_oid->hash))
+                       if (oidcmp(&cur->item->object.oid, current_bad_oid))
                                return cur;
                        if (previous)
                                return previous;
@@ -784,7 +784,7 @@ 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.sha1;
+               const unsigned char *mb = result->item->object.oid.hash;
                if (!hashcmp(mb, current_bad_oid->hash)) {
                        handle_bad_merge_base();
                } else if (0 <= sha1_array_lookup(&good_revs, mb)) {
@@ -973,7 +973,7 @@ int bisect_next_all(const char *prefix, int no_checkout)
                exit(4);
        }
 
-       bisect_rev = revs.commits->item->object.sha1;
+       bisect_rev = revs.commits->item->object.oid.hash;
 
        if (!hashcmp(bisect_rev, current_bad_oid->hash)) {
                exit_if_skipped_commits(tried, current_bad_oid);
index b864df623e3b89ad678a888dce4b0d4c997f1ac3..4df67477526a2891f7dcdac71ea7bffa75120a67 100644 (file)
@@ -16,7 +16,7 @@ void blk_SHA1_Init(blk_SHA_CTX *ctx);
 void blk_SHA1_Update(blk_SHA_CTX *ctx, const void *dataIn, unsigned long len);
 void blk_SHA1_Final(unsigned char hashout[20], blk_SHA_CTX *ctx);
 
-#define git_SHA_CTX    blk_SHA_CTX
-#define git_SHA1_Init  blk_SHA1_Init
-#define git_SHA1_Update        blk_SHA1_Update
-#define git_SHA1_Final blk_SHA1_Final
+#define platform_SHA_CTX       blk_SHA_CTX
+#define platform_SHA1_Init     blk_SHA1_Init
+#define platform_SHA1_Update   blk_SHA1_Update
+#define platform_SHA1_Final    blk_SHA1_Final
index 77d7f2a63ee38b81ffbbf4741cdf1ad205e0c054..7ff3f204964127374ae2a337819ce0bce7be9ab2 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -267,7 +267,7 @@ void create_branch(const char *head,
 
        if ((commit = lookup_commit_reference(sha1)) == NULL)
                die(_("Not a valid branch point: '%s'."), start_name);
-       hashcpy(sha1, commit->object.sha1);
+       hashcpy(sha1, commit->object.oid.hash);
 
        if (forcing)
                snprintf(msg, sizeof msg, "branch: Reset to %s",
index f1a25ab6ad881fc903a533595792b1a46b40ea91..9fb42fdd714a7785f6d75486db0d48fecceaacd6 100644 (file)
@@ -1441,7 +1441,7 @@ static void get_commit_info(struct am_state *state, struct commit *commit)
        assert(!state->msg);
        msg = strstr(buffer, "\n\n");
        if (!msg)
-               die(_("unable to parse commit %s"), sha1_to_hex(commit->object.sha1));
+               die(_("unable to parse commit %s"), oid_to_hex(&commit->object.oid));
        state->msg = xstrdup(msg + 2);
        state->msg_len = strlen(state->msg);
 }
index 83612f5b646f1db7232cf69cf5a2ea6b30a6e833..1df13cf7f4b501ed82dcbffdf4620706591a245e 100644 (file)
@@ -506,7 +506,7 @@ static int fill_blob_sha1_and_mode(struct origin *origin)
 {
        if (!is_null_sha1(origin->blob_sha1))
                return 0;
-       if (get_tree_entry(origin->commit->object.sha1,
+       if (get_tree_entry(origin->commit->object.oid.hash,
                           origin->path,
                           origin->blob_sha1, &origin->mode))
                goto error_out;
@@ -557,11 +557,11 @@ static struct origin *find_origin(struct scoreboard *sb,
                       PATHSPEC_LITERAL_PATH, "", paths);
        diff_setup_done(&diff_opts);
 
-       if (is_null_sha1(origin->commit->object.sha1))
-               do_diff_cache(parent->tree->object.sha1, &diff_opts);
+       if (is_null_oid(&origin->commit->object.oid))
+               do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
-               diff_tree_sha1(parent->tree->object.sha1,
-                              origin->commit->tree->object.sha1,
+               diff_tree_sha1(parent->tree->object.oid.hash,
+                              origin->commit->tree->object.oid.hash,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
 
@@ -627,11 +627,11 @@ static struct origin *find_rename(struct scoreboard *sb,
        diff_opts.single_follow = origin->path;
        diff_setup_done(&diff_opts);
 
-       if (is_null_sha1(origin->commit->object.sha1))
-               do_diff_cache(parent->tree->object.sha1, &diff_opts);
+       if (is_null_oid(&origin->commit->object.oid))
+               do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
-               diff_tree_sha1(parent->tree->object.sha1,
-                              origin->commit->tree->object.sha1,
+               diff_tree_sha1(parent->tree->object.oid.hash,
+                              origin->commit->tree->object.oid.hash,
                               "", &diff_opts);
        diffcore_std(&diff_opts);
 
@@ -977,8 +977,8 @@ static void pass_blame_to_parent(struct scoreboard *sb,
 
        if (diff_hunks(&file_p, &file_o, 0, blame_chunk_cb, &d))
                die("unable to generate diff (%s -> %s)",
-                   sha1_to_hex(parent->commit->object.sha1),
-                   sha1_to_hex(target->commit->object.sha1));
+                   oid_to_hex(&parent->commit->object.oid),
+                   oid_to_hex(&target->commit->object.oid));
        /* The rest are the same as the parent */
        blame_chunk(&d.dstq, &d.srcq, INT_MAX, d.offset, INT_MAX, parent);
        *d.dstq = NULL;
@@ -1126,7 +1126,7 @@ static void find_copy_in_blob(struct scoreboard *sb,
        memset(split, 0, sizeof(struct blame_entry [3]));
        if (diff_hunks(file_p, &file_o, 1, handle_split_cb, &d))
                die("unable to generate diff (%s)",
-                   sha1_to_hex(parent->commit->object.sha1));
+                   oid_to_hex(&parent->commit->object.oid));
        /* remainder, if any, all match the preimage */
        handle_split(sb, ent, d.tlno, d.plno, ent->num_lines, parent, split);
 }
@@ -1275,11 +1275,11 @@ static void find_copy_in_parent(struct scoreboard *sb,
                && (!porigin || strcmp(target->path, porigin->path))))
                DIFF_OPT_SET(&diff_opts, FIND_COPIES_HARDER);
 
-       if (is_null_sha1(target->commit->object.sha1))
-               do_diff_cache(parent->tree->object.sha1, &diff_opts);
+       if (is_null_oid(&target->commit->object.oid))
+               do_diff_cache(parent->tree->object.oid.hash, &diff_opts);
        else
-               diff_tree_sha1(parent->tree->object.sha1,
-                              target->commit->tree->object.sha1,
+               diff_tree_sha1(parent->tree->object.oid.hash,
+                              target->commit->tree->object.oid.hash,
                               "", &diff_opts);
 
        if (!DIFF_OPT_TST(&diff_opts, FIND_COPIES_HARDER))
@@ -1690,7 +1690,7 @@ static void get_commit_info(struct commit *commit,
        if (len)
                strbuf_add(&ret->summary, subject, len);
        else
-               strbuf_addf(&ret->summary, "(%s)", sha1_to_hex(commit->object.sha1));
+               strbuf_addf(&ret->summary, "(%s)", oid_to_hex(&commit->object.oid));
 
        unuse_commit_buffer(commit, message);
 }
@@ -1733,7 +1733,7 @@ static int emit_one_suspect_detail(struct origin *suspect, int repeat)
                printf("boundary\n");
        if (suspect->previous) {
                struct origin *prev = suspect->previous;
-               printf("previous %s ", sha1_to_hex(prev->commit->object.sha1));
+               printf("previous %s ", oid_to_hex(&prev->commit->object.oid));
                write_name_quoted(prev->path, stdout, '\n');
        }
 
@@ -1752,7 +1752,7 @@ static void found_guilty_entry(struct blame_entry *ent)
                struct origin *suspect = ent->suspect;
 
                printf("%s %d %d %d\n",
-                      sha1_to_hex(suspect->commit->object.sha1),
+                      oid_to_hex(&suspect->commit->object.oid),
                       ent->s_lno + 1, ent->lno + 1, ent->num_lines);
                emit_one_suspect_detail(suspect, 0);
                write_filename_info(suspect->path);
@@ -1882,7 +1882,7 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
        struct origin *suspect = ent->suspect;
        char hex[GIT_SHA1_HEXSZ + 1];
 
-       sha1_to_hex_r(hex, suspect->commit->object.sha1);
+       sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
        printf("%s %d %d %d\n",
               hex,
               ent->s_lno + 1,
@@ -1922,7 +1922,7 @@ static void emit_other(struct scoreboard *sb, struct blame_entry *ent, int opt)
        int show_raw_time = !!(opt & OUTPUT_RAW_TIMESTAMP);
 
        get_commit_info(suspect->commit, &ci, 1);
-       sha1_to_hex_r(hex, suspect->commit->object.sha1);
+       sha1_to_hex_r(hex, suspect->commit->object.oid.hash);
 
        cp = nth_line(sb, ent->lno);
        for (cnt = 0; cnt < ent->num_lines; cnt++) {
@@ -2077,7 +2077,7 @@ static int read_ancestry(const char *graft_file)
 
 static int update_auto_abbrev(int auto_abbrev, struct origin *suspect)
 {
-       const char *uniq = find_unique_abbrev(suspect->commit->object.sha1,
+       const char *uniq = find_unique_abbrev(suspect->commit->object.oid.hash,
                                              auto_abbrev);
        int len = strlen(uniq);
        if (auto_abbrev < len)
@@ -2153,7 +2153,7 @@ static void sanity_check_refcnt(struct scoreboard *sb)
                if (ent->suspect->refcnt <= 0) {
                        fprintf(stderr, "%s in %s has negative refcnt %d\n",
                                ent->suspect->path,
-                               sha1_to_hex(ent->suspect->commit->object.sha1),
+                               oid_to_hex(&ent->suspect->commit->object.oid),
                                ent->suspect->refcnt);
                        baa = 1;
                }
@@ -2216,7 +2216,7 @@ static void verify_working_tree_path(struct commit *work_tree, const char *path)
        struct commit_list *parents;
 
        for (parents = work_tree->parents; parents; parents = parents->next) {
-               const unsigned char *commit_sha1 = parents->item->object.sha1;
+               const unsigned char *commit_sha1 = parents->item->object.oid.hash;
                unsigned char blob_sha1[20];
                unsigned mode;
 
@@ -2310,7 +2310,7 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        strbuf_addstr(&msg, "tree 0000000000000000000000000000000000000000\n");
        for (parent = commit->parents; parent; parent = parent->next)
                strbuf_addf(&msg, "parent %s\n",
-                           sha1_to_hex(parent->item->object.sha1));
+                           oid_to_hex(&parent->item->object.oid));
        strbuf_addf(&msg,
                    "author %s\n"
                    "committer %s\n\n"
@@ -2402,10 +2402,12 @@ static struct commit *fake_working_tree_commit(struct diff_options *opt,
        return commit;
 }
 
-static struct object_array_entry *find_single_final(struct rev_info *revs)
+static struct commit *find_single_final(struct rev_info *revs,
+                                       const char **name_p)
 {
        int i;
-       struct object_array_entry *found = NULL;
+       struct commit *found = NULL;
+       const char *name = NULL;
 
        for (i = 0; i < revs->pending.nr; i++) {
                struct object *obj = revs->pending.objects[i].item;
@@ -2417,22 +2419,20 @@ static struct object_array_entry *find_single_final(struct rev_info *revs)
                        die("Non commit %s?", revs->pending.objects[i].name);
                if (found)
                        die("More than one commit to dig from %s and %s?",
-                           revs->pending.objects[i].name,
-                           found->name);
-               found = &(revs->pending.objects[i]);
+                           revs->pending.objects[i].name, name);
+               found = (struct commit *)obj;
+               name = revs->pending.objects[i].name;
        }
+       if (name_p)
+               *name_p = name;
        return found;
 }
 
 static char *prepare_final(struct scoreboard *sb)
 {
-       struct object_array_entry *found = find_single_final(sb->revs);
-       if (found) {
-               sb->final = (struct commit *) found->item;
-               return xstrdup(found->name);
-       } else {
-               return NULL;
-       }
+       const char *name;
+       sb->final = find_single_final(sb->revs, &name);
+       return xstrdup_or_null(name);
 }
 
 static char *prepare_initial(struct scoreboard *sb)
@@ -2720,11 +2720,9 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                die("Cannot use --contents with final commit object name");
 
        if (reverse && revs.first_parent_only) {
-               struct object_array_entry *entry = find_single_final(sb.revs);
-               if (!entry)
+               final_commit = find_single_final(sb.revs, NULL);
+               if (!final_commit)
                        die("--reverse and --first-parent together require specified latest commit");
-               else
-                       final_commit = (struct commit*) entry->item;
        }
 
        /*
@@ -2740,7 +2738,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
 
                sb.revs->children.name = "children";
                while (c->parents &&
-                      hashcmp(c->object.sha1, sb.final->object.sha1)) {
+                      oidcmp(&c->object.oid, &sb.final->object.oid)) {
                        struct commit_list *l = xcalloc(1, sizeof(*l));
 
                        l->item = c;
@@ -2750,11 +2748,11 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
                        c = c->parents->item;
                }
 
-               if (hashcmp(c->object.sha1, sb.final->object.sha1))
+               if (oidcmp(&c->object.oid, &sb.final->object.oid))
                        die("--reverse --first-parent together require range along first-parent chain");
        }
 
-       if (is_null_sha1(sb.final->object.sha1)) {
+       if (is_null_oid(&sb.final->object.oid)) {
                o = sb.final->util;
                sb.final_buf = xmemdupz(o->file.ptr, o->file.size);
                sb.final_buf_size = o->file.size;
index b99a436ef36ecfcc70636741642fdcf07b5a5810..3f6c825db1caf9c7cdd2ca7c1280e5ad157f9a27 100644 (file)
@@ -349,7 +349,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_array_item *item,
                fill_tracking_info(&stat, refname, filter->verbose > 1);
 
        strbuf_addf(out, " %s %s%s",
-               find_unique_abbrev(item->commit->object.sha1, filter->abbrev),
+               find_unique_abbrev(item->commit->object.oid.hash, filter->abbrev),
                stat.buf, sub);
        strbuf_release(&stat);
        strbuf_release(&subject);
index e346f521b0b1c197a3a883cb040080048b970ebb..e8110a9243f648129ffbea0de2af039ad7791aa1 100644 (file)
@@ -56,8 +56,8 @@ static int post_checkout_hook(struct commit *old, struct commit *new,
                              int changed)
 {
        return run_hook_le(NULL, "post-checkout",
-                          sha1_to_hex(old ? old->object.sha1 : null_sha1),
-                          sha1_to_hex(new ? new->object.sha1 : null_sha1),
+                          sha1_to_hex(old ? old->object.oid.hash : null_sha1),
+                          sha1_to_hex(new ? new->object.oid.hash : null_sha1),
                           changed ? "1" : "0", NULL);
        /* "new" can be NULL when checking out from the index before
           a commit exists. */
@@ -401,7 +401,7 @@ static void describe_detached_head(const char *msg, struct commit *commit)
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &sb);
        fprintf(stderr, "%s %s... %s\n", msg,
-               find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV), sb.buf);
+               find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV), sb.buf);
        strbuf_release(&sb);
 }
 
@@ -510,10 +510,10 @@ static int merge_working_tree(const struct checkout_opts *opts,
                        setup_standard_excludes(topts.dir);
                }
                tree = parse_tree_indirect(old->commit ?
-                                          old->commit->object.sha1 :
+                                          old->commit->object.oid.hash :
                                           EMPTY_TREE_SHA1_BIN);
                init_tree_desc(&trees[0], tree->buffer, tree->size);
-               tree = parse_tree_indirect(new->commit->object.sha1);
+               tree = parse_tree_indirect(new->commit->object.oid.hash);
                init_tree_desc(&trees[1], tree->buffer, tree->size);
 
                ret = unpack_trees(2, trees, &topts);
@@ -641,7 +641,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
 
        old_desc = old->name;
        if (!old_desc && old->commit)
-               old_desc = sha1_to_hex(old->commit->object.sha1);
+               old_desc = oid_to_hex(&old->commit->object.oid);
 
        reflog_msg = getenv("GIT_REFLOG_ACTION");
        if (!reflog_msg)
@@ -653,7 +653,7 @@ static void update_refs_for_switch(const struct checkout_opts *opts,
        if (!strcmp(new->name, "HEAD") && !new->path && !opts->force_detach) {
                /* Nothing to do. */
        } else if (opts->force_detach || !new->path) {  /* No longer on any branch. */
-               update_ref(msg.buf, "HEAD", new->commit->object.sha1, NULL,
+               update_ref(msg.buf, "HEAD", new->commit->object.oid.hash, NULL,
                           REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
                if (!opts->quiet) {
                        if (old->path && advice_detached_head)
@@ -704,7 +704,7 @@ static void describe_one_orphan(struct strbuf *sb, struct commit *commit)
 {
        strbuf_addstr(sb, "  ");
        strbuf_addstr(sb,
-               find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+               find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
        strbuf_addch(sb, ' ');
        if (!parse_commit(commit))
                pp_commit_easy(CMIT_FMT_ONELINE, commit, sb);
@@ -762,7 +762,7 @@ static void suggest_reattach(struct commit *commit, struct rev_info *revs)
                        " git branch <new-branch-name> %s\n\n",
                        /* Give ngettext() the count */
                        lost),
-                       find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+                       find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
 }
 
 /*
@@ -780,10 +780,10 @@ static void orphaned_commit_warning(struct commit *old, struct commit *new)
        setup_revisions(0, NULL, &revs, NULL);
 
        object->flags &= ~UNINTERESTING;
-       add_pending_object(&revs, object, sha1_to_hex(object->sha1));
+       add_pending_object(&revs, object, oid_to_hex(&object->oid));
 
        for_each_ref(add_pending_uninteresting_ref, &revs);
-       add_pending_sha1(&revs, "HEAD", new->object.sha1, UNINTERESTING);
+       add_pending_sha1(&revs, "HEAD", new->object.oid.hash, UNINTERESTING);
 
        refs = revs.pending;
        revs.leak_pending = 1;
index caae43e7a390d8666cda259e995c14079103d20d..a0b3cd9e5617b5a3a45dd01c8a2c7af0b2fd9668 100644 (file)
@@ -559,7 +559,7 @@ static void write_remote_refs(const struct ref *local_refs)
        for (r = local_refs; r; r = r->next) {
                if (!r->peer_ref)
                        continue;
-               if (ref_transaction_create(t, r->peer_ref->name, r->old_sha1,
+               if (ref_transaction_create(t, r->peer_ref->name, r->old_oid.hash,
                                           0, NULL, &err))
                        die("%s", err.buf);
        }
@@ -579,9 +579,9 @@ static void write_followtags(const struct ref *refs, const char *msg)
                        continue;
                if (ends_with(ref->name, "^{}"))
                        continue;
-               if (!has_sha1_file(ref->old_sha1))
+               if (!has_object_file(&ref->old_oid))
                        continue;
-               update_ref(msg, ref->name, ref->old_sha1,
+               update_ref(msg, ref->name, ref->old_oid.hash,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
        }
 }
@@ -601,7 +601,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1;
 
-       hashcpy(sha1, ref->old_sha1);
+       hashcpy(sha1, ref->old_oid.hash);
        *rm = ref->next;
        return 0;
 }
@@ -650,14 +650,14 @@ static void update_head(const struct ref *our, const struct ref *remote,
                /* Local default branch link */
                create_symref("HEAD", our->name, NULL);
                if (!option_bare) {
-                       update_ref(msg, "HEAD", our->old_sha1, NULL, 0,
+                       update_ref(msg, "HEAD", our->old_oid.hash, NULL, 0,
                                   UPDATE_REFS_DIE_ON_ERR);
                        install_branch_config(0, head, option_origin, our->name);
                }
        } else if (our) {
-               struct commit *c = lookup_commit_reference(our->old_sha1);
+               struct commit *c = lookup_commit_reference(our->old_oid.hash);
                /* --branch specifies a non-branch (i.e. tags), detach HEAD */
-               update_ref(msg, "HEAD", c->object.sha1,
+               update_ref(msg, "HEAD", c->object.oid.hash,
                           NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        } else if (remote) {
                /*
@@ -665,7 +665,7 @@ static void update_head(const struct ref *our, const struct ref *remote,
                 * HEAD points to a branch but we don't know which one.
                 * Detach HEAD in all these cases.
                 */
-               update_ref(msg, "HEAD", remote->old_sha1,
+               update_ref(msg, "HEAD", remote->old_oid.hash,
                           NULL, REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        }
 }
@@ -1016,7 +1016,7 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
                 * remote HEAD check.
                 */
                for (ref = refs; ref; ref = ref->next)
-                       if (is_null_sha1(ref->old_sha1)) {
+                       if (is_null_oid(&ref->old_oid)) {
                                complete_refs_before_fetch = 0;
                                break;
                        }
index 8747c0f2fbe6426b97571bd29df0892efd1fa810..3feeffeab1ccd51a5e30098c31b4daa62e7ef684 100644 (file)
@@ -16,11 +16,11 @@ static const char *sign_commit;
 
 static void new_parent(struct commit *parent, struct commit_list **parents_p)
 {
-       unsigned char *sha1 = parent->object.sha1;
+       struct object_id *oid = &parent->object.oid;
        struct commit_list *parents;
        for (parents = *parents_p; parents; parents = parents->next) {
                if (parents->item == parent) {
-                       error("duplicate parent %s ignored", sha1_to_hex(sha1));
+                       error("duplicate parent %s ignored", oid_to_hex(oid));
                        return;
                }
                parents_p = &parents->next;
index dca09e2c3bd8a839b27e279c427ebba71e88966e..d054f849606b248a82f7b29103308feab36ca9e9 100644 (file)
@@ -32,6 +32,7 @@
 #include "sequencer.h"
 #include "notes-utils.h"
 #include "mailmap.h"
+#include "sigchain.h"
 
 static const char * const builtin_commit_usage[] = {
        N_("git commit [<options>] [--] <pathspec>..."),
@@ -299,7 +300,7 @@ static void create_base_index(const struct commit *current_head)
        opts.dst_index = &the_index;
 
        opts.fn = oneway_merge;
-       tree = parse_tree_indirect(current_head->object.sha1);
+       tree = parse_tree_indirect(current_head->object.oid.hash);
        if (!tree)
                die(_("failed to unpack HEAD tree object"));
        parse_tree(tree);
@@ -1537,8 +1538,10 @@ static int run_rewrite_hook(const unsigned char *oldsha1,
                return code;
        n = snprintf(buf, sizeof(buf), "%s %s\n",
                     sha1_to_hex(oldsha1), sha1_to_hex(newsha1));
+       sigchain_push(SIGPIPE, SIG_IGN);
        write_in_full(proc.in, buf, n);
        close(proc.in);
+       sigchain_pop(SIGPIPE);
        return finish_command(&proc);
 }
 
@@ -1766,7 +1769,7 @@ int cmd_commit(int argc, const char **argv, const char *prefix)
        if (!transaction ||
            ref_transaction_update(transaction, "HEAD", sha1,
                                   current_head
-                                  ? current_head->object.sha1 : null_sha1,
+                                  ? current_head->object.oid.hash : null_sha1,
                                   0, sb.buf, &err) ||
            ref_transaction_commit(transaction, &err)) {
                rollback_index_files();
@@ -1793,10 +1796,10 @@ 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.sha1, sha1);
+                       copy_note_for_rewrite(cfg, current_head->object.oid.hash, sha1);
                        finish_copy_notes_for_rewrite(cfg, "Notes added by 'git commit --amend'");
                }
-               run_rewrite_hook(current_head->object.sha1, sha1);
+               run_rewrite_hook(current_head->object.oid.hash, sha1);
        }
        if (!quiet)
                print_summary(prefix, sha1, !current_head);
index 7df554326b08581c49f391d061be14cd1164ea88..8a25abe0a03e2b2463ac352daafc326c4322ac32 100644 (file)
@@ -252,14 +252,14 @@ static void describe(const char *arg, int last_one)
        if (!cmit)
                die(_("%s is not a valid '%s' object"), arg, commit_type);
 
-       n = find_commit_name(cmit->object.sha1);
+       n = find_commit_name(cmit->object.oid.hash);
        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->sha1 : sha1);
+                       show_suffix(0, n->tag ? n->tag->tagged->oid.hash : sha1);
                if (dirty)
                        printf("%s", dirty);
                printf("\n");
@@ -267,7 +267,7 @@ static void describe(const char *arg, int last_one)
        }
 
        if (!max_candidates)
-               die(_("no tag exactly matches '%s'"), sha1_to_hex(cmit->object.sha1));
+               die(_("no tag exactly matches '%s'"), oid_to_hex(&cmit->object.oid));
        if (debug)
                fprintf(stderr, _("searching to describe %s\n"), arg);
 
@@ -317,7 +317,7 @@ static void describe(const char *arg, int last_one)
                if (annotated_cnt && !list) {
                        if (debug)
                                fprintf(stderr, _("finished search at %s\n"),
-                                       sha1_to_hex(c->object.sha1));
+                                       oid_to_hex(&c->object.oid));
                        break;
                }
                while (parents) {
@@ -334,9 +334,9 @@ static void describe(const char *arg, int last_one)
        }
 
        if (!match_cnt) {
-               const unsigned char *sha1 = cmit->object.sha1;
+               struct object_id *oid = &cmit->object.oid;
                if (always) {
-                       printf("%s", find_unique_abbrev(sha1, abbrev));
+                       printf("%s", find_unique_abbrev(oid->hash, abbrev));
                        if (dirty)
                                printf("%s", dirty);
                        printf("\n");
@@ -345,11 +345,11 @@ static void describe(const char *arg, int last_one)
                if (unannotated_cnt)
                        die(_("No annotated tags can describe '%s'.\n"
                            "However, there were unannotated tags: try --tags."),
-                           sha1_to_hex(sha1));
+                           oid_to_hex(oid));
                else
                        die(_("No tags can describe '%s'.\n"
                            "Try --always, or create some tags."),
-                           sha1_to_hex(sha1));
+                           oid_to_hex(oid));
        }
 
        qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
@@ -374,13 +374,13 @@ static void describe(const char *arg, int last_one)
                                _("more than %i tags found; listed %i most recent\n"
                                "gave up search at %s\n"),
                                max_candidates, max_candidates,
-                               sha1_to_hex(gave_up_on->object.sha1));
+                               oid_to_hex(&gave_up_on->object.oid));
                }
        }
 
        display_name(all_matches[0].name);
        if (abbrev)
-               show_suffix(all_matches[0].depth, cmit->object.sha1);
+               show_suffix(all_matches[0].depth, cmit->object.oid.hash);
        if (dirty)
                printf("%s", dirty);
        printf("\n");
index 12b683d0217856a23cb4c3271a173ce1b7bfbe68..2a12b81e065bfb75f05baf6694e1a9d53129bc40 100644 (file)
@@ -49,9 +49,9 @@ static int stdin_diff_trees(struct tree *tree1, char *line, int len)
        tree2 = lookup_tree(sha1);
        if (!tree2 || parse_tree(tree2))
                return -1;
-       printf("%s %s\n", sha1_to_hex(tree1->object.sha1),
-                         sha1_to_hex(tree2->object.sha1));
-       diff_tree_sha1(tree1->object.sha1, tree2->object.sha1,
+       printf("%s %s\n", oid_to_hex(&tree1->object.oid),
+                         oid_to_hex(&tree2->object.oid));
+       diff_tree_sha1(tree1->object.oid.hash, tree2->object.oid.hash,
                       "", &log_tree_opt.diffopt);
        log_tree_diff_flush(&log_tree_opt);
        return 0;
@@ -139,7 +139,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->sha1);
+               diff_tree_commit_sha1(tree1->oid.hash);
                break;
        case 2:
                tree1 = opt->pending.objects[0].item;
@@ -149,8 +149,8 @@ int cmd_diff_tree(int argc, const char **argv, const char *prefix)
                        tree2 = tree1;
                        tree1 = tmp;
                }
-               diff_tree_sha1(tree1->sha1,
-                              tree2->sha1,
+               diff_tree_sha1(tree1->oid.hash,
+                              tree2->oid.hash,
                               "", &opt->diffopt);
                log_tree_diff_flush(opt);
                break;
index 4326fa56bfa2021c6518c225eaa3f89b8b852e11..ed0acca91fb870d3f773779b34f4e24c401945e5 100644 (file)
@@ -175,8 +175,8 @@ static int builtin_diff_tree(struct rev_info *revs,
         */
        if (ent1->item->flags & UNINTERESTING)
                swap = 1;
-       sha1[swap] = ent0->item->sha1;
-       sha1[1 - swap] = ent1->item->sha1;
+       sha1[swap] = ent0->item->oid.hash;
+       sha1[1 - swap] = ent1->item->oid.hash;
        diff_tree_sha1(sha1[0], sha1[1], "", &revs->diffopt);
        log_tree_diff_flush(revs);
        return 0;
@@ -196,8 +196,8 @@ 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->sha1);
-       diff_tree_combined(ent[0].item->sha1, &parents,
+               sha1_array_append(&parents, ent[i].item->oid.hash);
+       diff_tree_combined(ent[0].item->oid.hash, &parents,
                           revs->dense_combined_merges, revs);
        sha1_array_clear(&parents);
        return 0;
@@ -395,7 +395,7 @@ int cmd_diff(int argc, const char **argv, const char *prefix)
                const char *name = entry->name;
                int flags = (obj->flags & UNINTERESTING);
                if (!obj->parsed)
-                       obj = parse_object(obj->sha1);
+                       obj = parse_object(obj->oid.hash);
                obj = deref_tag(obj, NULL, 0);
                if (!obj)
                        die(_("invalid object '%s' given."), name);
@@ -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->sha1);
+                       hashcpy(blob[blobs].sha1, obj->oid.hash);
                        blob[blobs].name = name;
                        blob[blobs].mode = entry->mode;
                        blobs++;
index d23f3beba9b35a65c3de63d3843ca6158aef72f0..d9ac5d8410b30e02fbb8edb06a1991655c5be8f4 100644 (file)
@@ -544,13 +544,13 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
        author = strstr(commit_buffer, "\nauthor ");
        if (!author)
                die ("Could not find author in commit %s",
-                    sha1_to_hex(commit->object.sha1));
+                    oid_to_hex(&commit->object.oid));
        author++;
        author_end = strchrnul(author, '\n');
        committer = strstr(author_end, "\ncommitter ");
        if (!committer)
                die ("Could not find committer in commit %s",
-                    sha1_to_hex(commit->object.sha1));
+                    oid_to_hex(&commit->object.oid));
        committer++;
        committer_end = strchrnul(committer, '\n');
        message = strstr(committer_end, "\n\n");
@@ -562,11 +562,11 @@ static void handle_commit(struct commit *commit, struct rev_info *rev)
            get_object_mark(&commit->parents->item->object) != 0 &&
            !full_tree) {
                parse_commit_or_die(commit->parents->item);
-               diff_tree_sha1(commit->parents->item->tree->object.sha1,
-                              commit->tree->object.sha1, "", &rev->diffopt);
+               diff_tree_sha1(commit->parents->item->tree->object.oid.hash,
+                              commit->tree->object.oid.hash, "", &rev->diffopt);
        }
        else
-               diff_root_tree_sha1(commit->tree->object.sha1,
+               diff_root_tree_sha1(commit->tree->object.oid.hash,
                                    "", &rev->diffopt);
 
        /* Export the referenced blobs, and remember the marks. */
@@ -661,13 +661,13 @@ static void handle_tag(const char *name, struct tag *tag)
        }
        if (tagged->type == OBJ_TREE) {
                warning("Omitting tag %s,\nsince tags of trees (or tags of tags of trees, etc.) are not supported.",
-                       sha1_to_hex(tag->object.sha1));
+                       oid_to_hex(&tag->object.oid));
                return;
        }
 
-       buf = read_sha1_file(tag->object.sha1, &type, &size);
+       buf = read_sha1_file(tag->object.oid.hash, &type, &size);
        if (!buf)
-               die ("Could not read tag %s", sha1_to_hex(tag->object.sha1));
+               die ("Could not read tag %s", oid_to_hex(&tag->object.oid));
        message = memmem(buf, size, "\n\n", 2);
        if (message) {
                message += 2;
@@ -706,16 +706,16 @@ static void handle_tag(const char *name, struct tag *tag)
                        case ABORT:
                                die ("Encountered signed tag %s; use "
                                     "--signed-tags=<mode> to handle it.",
-                                    sha1_to_hex(tag->object.sha1));
+                                    oid_to_hex(&tag->object.oid));
                        case WARN:
                                warning ("Exporting signed tag %s",
-                                        sha1_to_hex(tag->object.sha1));
+                                        oid_to_hex(&tag->object.oid));
                                /* fallthru */
                        case VERBATIM:
                                break;
                        case WARN_STRIP:
                                warning ("Stripping signature from tag %s",
-                                        sha1_to_hex(tag->object.sha1));
+                                        oid_to_hex(&tag->object.oid));
                                /* fallthru */
                        case STRIP:
                                message_size = signature + 1 - message;
@@ -731,14 +731,14 @@ static void handle_tag(const char *name, struct tag *tag)
                case ABORT:
                        die ("Tag %s tags unexported object; use "
                             "--tag-of-filtered-object=<mode> to handle it.",
-                            sha1_to_hex(tag->object.sha1));
+                            oid_to_hex(&tag->object.oid));
                case DROP:
                        /* Ignore this tag altogether */
                        return;
                case REWRITE:
                        if (tagged->type != OBJ_COMMIT) {
                                die ("Tag %s tags unexported %s!",
-                                    sha1_to_hex(tag->object.sha1),
+                                    oid_to_hex(&tag->object.oid),
                                     typename(tagged->type));
                        }
                        p = (struct commit *)tagged;
@@ -751,7 +751,7 @@ static void handle_tag(const char *name, struct tag *tag)
                                        break;
                                if (!p->parents)
                                        die ("Can't find replacement commit for tag %s\n",
-                                            sha1_to_hex(tag->object.sha1));
+                                            oid_to_hex(&tag->object.oid));
                                p = p->parents->item;
                        }
                        tagged_mark = get_object_mark(&p->object);
@@ -777,7 +777,7 @@ static struct commit *get_commit(struct rev_cmdline_entry *e, char *full_name)
 
                /* handle nested tags */
                while (tag && tag->object.type == OBJ_TAG) {
-                       parse_object(tag->object.sha1);
+                       parse_object(tag->object.oid.hash);
                        string_list_append(&extra_refs, full_name)->util = tag;
                        tag = (struct tag *)tag->tagged;
                }
@@ -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.sha1);
+                       export_blob(commit->object.oid.hash);
                        continue;
                default: /* OBJ_TAG (nested tags) is already handled */
                        warning("Tag points to object of unexpected type %s, skipping.",
@@ -888,7 +888,7 @@ static void export_marks(char *file)
                if (deco->base && deco->base->type == 1) {
                        mark = ptr_to_mark(deco->decoration);
                        if (fprintf(f, ":%"PRIu32" %s\n", mark,
-                               sha1_to_hex(deco->base->sha1)) < 0) {
+                               oid_to_hex(&deco->base->oid)) < 0) {
                            e = 1;
                            break;
                        }
index 4a6b340ab67622a1e0c57a2bd38e2de4f1ad91a5..cf3019e05bdf45e1fa16a483621a6773976f98c5 100644 (file)
@@ -14,12 +14,14 @@ static void add_sought_entry_mem(struct ref ***sought, int *nr, int *alloc,
                                 const char *name, int namelen)
 {
        struct ref *ref = xcalloc(1, sizeof(*ref) + namelen + 1);
-       unsigned char sha1[20];
-
-       if (namelen > 41 && name[40] == ' ' && !get_sha1_hex(name, sha1)) {
-               hashcpy(ref->old_sha1, sha1);
-               name += 41;
-               namelen -= 41;
+       struct object_id oid;
+       const int chunksz = GIT_SHA1_HEXSZ + 1;
+
+       if (namelen > chunksz && name[chunksz - 1] == ' ' &&
+               !get_oid_hex(name, &oid)) {
+               oidcpy(&ref->old_oid, &oid);
+               name += chunksz;
+               namelen -= chunksz;
        }
 
        memcpy(ref->name, name, namelen);
@@ -210,7 +212,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
 
        while (ref) {
                printf("%s %s\n",
-                      sha1_to_hex(ref->old_sha1), ref->name);
+                      oid_to_hex(&ref->old_oid), ref->name);
                ref = ref->next;
        }
 
index ed84963a57b692ab36aecfed61606afcfaa6d2cd..c85f3471d4b53ce7db7662b5531ddebe5513b7b0 100644 (file)
@@ -196,7 +196,7 @@ static int will_fetch(struct ref **head, const unsigned char *sha1)
 {
        struct ref *rm = *head;
        while (rm) {
-               if (!hashcmp(rm->old_sha1, sha1))
+               if (!hashcmp(rm->old_oid.hash, sha1))
                        return 1;
                rm = rm->next;
        }
@@ -224,8 +224,8 @@ static void find_non_local_tags(struct transport *transport,
                 * as one to ignore by setting util to NULL.
                 */
                if (ends_with(ref->name, "^{}")) {
-                       if (item && !has_sha1_file(ref->old_sha1) &&
-                           !will_fetch(head, ref->old_sha1) &&
+                       if (item && !has_object_file(&ref->old_oid) &&
+                           !will_fetch(head, ref->old_oid.hash) &&
                            !has_sha1_file(item->util) &&
                            !will_fetch(head, item->util))
                                item->util = NULL;
@@ -251,7 +251,7 @@ static void find_non_local_tags(struct transport *transport,
                        continue;
 
                item = string_list_insert(&remote_refs, ref->name);
-               item->util = (void *)ref->old_sha1;
+               item->util = (void *)&ref->old_oid;
        }
        string_list_clear(&existing_refs, 1);
 
@@ -273,7 +273,7 @@ static void find_non_local_tags(struct transport *transport,
                {
                        struct ref *rm = alloc_ref(item->string);
                        rm->peer_ref = alloc_ref(item->string);
-                       hashcpy(rm->old_sha1, item->util);
+                       oidcpy(&rm->old_oid, item->util);
                        **tail = rm;
                        *tail = &rm->next;
                }
@@ -419,8 +419,8 @@ static int s_update_ref(const char *action,
        transaction = ref_transaction_begin(&err);
        if (!transaction ||
            ref_transaction_update(transaction, ref->name,
-                                  ref->new_sha1,
-                                  check_old ? ref->old_sha1 : NULL,
+                                  ref->new_oid.hash,
+                                  check_old ? ref->old_oid.hash : NULL,
                                   0, msg, &err))
                goto fail;
 
@@ -453,11 +453,11 @@ static int update_local_ref(struct ref *ref,
        struct branch *current_branch = branch_get(NULL);
        const char *pretty_ref = prettify_refname(ref->name);
 
-       type = sha1_object_info(ref->new_sha1, NULL);
+       type = sha1_object_info(ref->new_oid.hash, NULL);
        if (type < 0)
-               die(_("object %s not found"), sha1_to_hex(ref->new_sha1));
+               die(_("object %s not found"), oid_to_hex(&ref->new_oid));
 
-       if (!hashcmp(ref->old_sha1, ref->new_sha1)) {
+       if (!oidcmp(&ref->old_oid, &ref->new_oid)) {
                if (verbosity > 0)
                        strbuf_addf(display, "= %-*s %-*s -> %s",
                                    TRANSPORT_SUMMARY(_("[up to date]")),
@@ -468,7 +468,7 @@ static int update_local_ref(struct ref *ref,
        if (current_branch &&
            !strcmp(ref->name, current_branch->name) &&
            !(update_head_ok || is_bare_repository()) &&
-           !is_null_sha1(ref->old_sha1)) {
+           !is_null_oid(&ref->old_oid)) {
                /*
                 * If this is the head, and it's not okay to update
                 * the head, and the old value of the head isn't empty...
@@ -480,7 +480,7 @@ static int update_local_ref(struct ref *ref,
                return 1;
        }
 
-       if (!is_null_sha1(ref->old_sha1) &&
+       if (!is_null_oid(&ref->old_oid) &&
            starts_with(ref->name, "refs/tags/")) {
                int r;
                r = s_update_ref("updating tag", ref, 0);
@@ -492,8 +492,8 @@ static int update_local_ref(struct ref *ref,
                return r;
        }
 
-       current = lookup_commit_reference_gently(ref->old_sha1, 1);
-       updated = lookup_commit_reference_gently(ref->new_sha1, 1);
+       current = lookup_commit_reference_gently(ref->old_oid.hash, 1);
+       updated = lookup_commit_reference_gently(ref->new_oid.hash, 1);
        if (!current || !updated) {
                const char *msg;
                const char *what;
@@ -517,7 +517,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_sha1);
+                       check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref(msg, ref, 0);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : '*',
@@ -530,12 +530,12 @@ static int update_local_ref(struct ref *ref,
        if (in_merge_bases(current, updated)) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
-               strbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "..");
-               strbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);
+               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_sha1);
+                       check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref("fast-forward", ref, 1);
                strbuf_addf(display, "%c %-*s %-*s -> %s%s",
                            r ? '!' : ' ',
@@ -547,12 +547,12 @@ static int update_local_ref(struct ref *ref,
        } else if (force || ref->force) {
                struct strbuf quickref = STRBUF_INIT;
                int r;
-               strbuf_add_unique_abbrev(&quickref, current->object.sha1, DEFAULT_ABBREV);
+               strbuf_add_unique_abbrev(&quickref, current->object.oid.hash, DEFAULT_ABBREV);
                strbuf_addstr(&quickref, "...");
-               strbuf_add_unique_abbrev(&quickref, ref->new_sha1, DEFAULT_ABBREV);
+               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_sha1);
+                       check_for_new_submodule_commits(ref->new_oid.hash);
                r = s_update_ref("forced-update", ref, 1);
                strbuf_addf(display, "%c %-*s %-*s -> %s  (%s)",
                            r ? '!' : '+',
@@ -580,7 +580,7 @@ static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
        if (!ref)
                return -1; /* end of the list */
        *rm = ref->next;
-       hashcpy(sha1, ref->old_sha1);
+       hashcpy(sha1, ref->old_oid.hash);
        return 0;
 }
 
@@ -631,7 +631,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                continue;
                        }
 
-                       commit = lookup_commit_reference_gently(rm->old_sha1, 1);
+                       commit = lookup_commit_reference_gently(rm->old_oid.hash, 1);
                        if (!commit)
                                rm->fetch_head_status = FETCH_HEAD_NOT_FOR_MERGE;
 
@@ -640,8 +640,8 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
                        if (rm->peer_ref) {
                                ref = alloc_ref(rm->peer_ref->name);
-                               hashcpy(ref->old_sha1, rm->peer_ref->old_sha1);
-                               hashcpy(ref->new_sha1, rm->old_sha1);
+                               oidcpy(&ref->old_oid, &rm->peer_ref->old_oid);
+                               oidcpy(&ref->new_oid, &rm->old_oid);
                                ref->force = rm->peer_ref->force;
                        }
 
@@ -686,7 +686,7 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                /* fall-through */
                        case FETCH_HEAD_MERGE:
                                fprintf(fp, "%s\t%s\t%s",
-                                       sha1_to_hex(rm->old_sha1),
+                                       oid_to_hex(&rm->old_oid),
                                        merge_status_marker,
                                        note.buf);
                                for (i = 0; i < url_len; ++i)
@@ -928,7 +928,7 @@ static int do_fetch(struct transport *transport,
                                                   rm->peer_ref->name);
                        if (peer_item) {
                                struct object_id *old_oid = peer_item->util;
-                               hashcpy(rm->peer_ref->old_sha1, old_oid->hash);
+                               oidcpy(&rm->peer_ref->old_oid, old_oid);
                        }
                }
        }
index 846004b83358cd8adfd5f7de96b2d551700037a1..e5658c320ee45c208d4087e2b9761f38e0aeca92 100644 (file)
@@ -378,7 +378,7 @@ static void shortlog(const char *name,
 
                if (!sb.len)
                        string_list_append(&subjects,
-                                          sha1_to_hex(commit->object.sha1));
+                                          oid_to_hex(&commit->object.oid));
                else
                        string_list_append(&subjects, strbuf_detach(&sb, NULL));
        }
@@ -568,7 +568,7 @@ static void find_merge_parents(struct merge_parents *result,
                if (!parent)
                        continue;
                commit_list_insert(parent, &parents);
-               add_merge_parent(result, obj->sha1, parent->object.sha1);
+               add_merge_parent(result, obj->oid.hash, parent->object.oid.hash);
        }
        head_commit = lookup_commit(head);
        if (head_commit)
@@ -578,7 +578,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.sha1))
+                       if (!hashcmp(result->item[i].commit, cmit->object.oid.hash))
                                result->item[i].used = 1;
        }
 
index 8b8bb42c51d13ce9263c0a6fd30fa36810fef78c..55eac756f7c83e496daae7cb8874552caaa58ff5 100644 (file)
@@ -67,7 +67,7 @@ static void objreport(struct object *obj, const char *msg_type,
                        const char *err)
 {
        fprintf(stderr, "%s in %s %s: %s\n",
-               msg_type, typename(obj->type), sha1_to_hex(obj->sha1), err);
+               msg_type, typename(obj->type), oid_to_hex(&obj->oid), err);
 }
 
 static int objerror(struct object *obj, const char *err)
@@ -97,7 +97,7 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
        if (!obj) {
                /* ... these references to parent->fld are safe here */
                printf("broken link from %7s %s\n",
-                          typename(parent->type), sha1_to_hex(parent->sha1));
+                          typename(parent->type), oid_to_hex(&parent->oid));
                printf("broken link from %7s %s\n",
                           (type == OBJ_ANY ? "unknown" : typename(type)), "unknown");
                errors_found |= ERROR_REACHABLE;
@@ -112,11 +112,11 @@ static int mark_object(struct object *obj, int type, void *data, struct fsck_opt
                return 0;
        obj->flags |= REACHABLE;
        if (!(obj->flags & HAS_OBJ)) {
-               if (parent && !has_sha1_file(obj->sha1)) {
+               if (parent && !has_object_file(&obj->oid)) {
                        printf("broken link from %7s %s\n",
-                                typename(parent->type), sha1_to_hex(parent->sha1));
+                                typename(parent->type), oid_to_hex(&parent->oid));
                        printf("              to %7s %s\n",
-                                typename(obj->type), sha1_to_hex(obj->sha1));
+                                typename(obj->type), oid_to_hex(&obj->oid));
                        errors_found |= ERROR_REACHABLE;
                }
                return 1;
@@ -186,11 +186,11 @@ static void check_reachable_object(struct object *obj)
         * do a full fsck
         */
        if (!(obj->flags & HAS_OBJ)) {
-               if (has_sha1_pack(obj->sha1))
+               if (has_sha1_pack(obj->oid.hash))
                        return; /* it is in pack - forget about it */
-               if (connectivity_only && has_sha1_file(obj->sha1))
+               if (connectivity_only && has_object_file(&obj->oid))
                        return;
-               printf("missing %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+               printf("missing %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
                errors_found |= ERROR_REACHABLE;
                return;
        }
@@ -215,7 +215,7 @@ static void check_unreachable_object(struct object *obj)
         * since this is something that is prunable.
         */
        if (show_unreachable) {
-               printf("unreachable %s %s\n", typename(obj->type), sha1_to_hex(obj->sha1));
+               printf("unreachable %s %s\n", typename(obj->type), oid_to_hex(&obj->oid));
                return;
        }
 
@@ -234,11 +234,11 @@ static void check_unreachable_object(struct object *obj)
        if (!obj->used) {
                if (show_dangling)
                        printf("dangling %s %s\n", typename(obj->type),
-                              sha1_to_hex(obj->sha1));
+                              oid_to_hex(&obj->oid));
                if (write_lost_and_found) {
                        char *filename = git_pathdup("lost-found/%s/%s",
                                obj->type == OBJ_COMMIT ? "commit" : "other",
-                               sha1_to_hex(obj->sha1));
+                               oid_to_hex(&obj->oid));
                        FILE *f;
 
                        if (safe_create_leading_directories_const(filename)) {
@@ -249,10 +249,10 @@ static void check_unreachable_object(struct object *obj)
                        if (!(f = fopen(filename, "w")))
                                die_errno("Could not open '%s'", filename);
                        if (obj->type == OBJ_BLOB) {
-                               if (stream_blob_to_fd(fileno(f), obj->sha1, NULL, 1))
+                               if (stream_blob_to_fd(fileno(f), obj->oid.hash, NULL, 1))
                                        die_errno("Could not write '%s'", filename);
                        } else
-                               fprintf(f, "%s\n", sha1_to_hex(obj->sha1));
+                               fprintf(f, "%s\n", oid_to_hex(&obj->oid));
                        if (fclose(f))
                                die_errno("Could not finish '%s'",
                                          filename);
@@ -271,7 +271,7 @@ static void check_unreachable_object(struct object *obj)
 static void check_object(struct object *obj)
 {
        if (verbose)
-               fprintf(stderr, "Checking %s\n", sha1_to_hex(obj->sha1));
+               fprintf(stderr, "Checking %s\n", oid_to_hex(&obj->oid));
 
        if (obj->flags & REACHABLE)
                check_reachable_object(obj);
@@ -307,7 +307,7 @@ static int fsck_obj(struct object *obj)
 
        if (verbose)
                fprintf(stderr, "Checking %s %s\n",
-                       typename(obj->type), sha1_to_hex(obj->sha1));
+                       typename(obj->type), oid_to_hex(&obj->oid));
 
        if (fsck_walk(obj, NULL, &fsck_obj_options))
                objerror(obj, "broken links");
@@ -326,15 +326,15 @@ static int fsck_obj(struct object *obj)
                free_commit_buffer(commit);
 
                if (!commit->parents && show_root)
-                       printf("root %s\n", sha1_to_hex(commit->object.sha1));
+                       printf("root %s\n", oid_to_hex(&commit->object.oid));
        }
 
        if (obj->type == OBJ_TAG) {
                struct tag *tag = (struct tag *) obj;
 
                if (show_tags && tag->tagged) {
-                       printf("tagged %s %s", typename(tag->tagged->type), sha1_to_hex(tag->tagged->sha1));
-                       printf(" (%s) in %s\n", tag->tag, sha1_to_hex(tag->object.sha1));
+                       printf("tagged %s %s", typename(tag->tagged->type), oid_to_hex(&tag->tagged->oid));
+                       printf(" (%s) in %s\n", tag->tag, oid_to_hex(&tag->object.oid));
                }
        }
 
index d04f4400d9db8ef9f0b0dde0c8be46a11bd0e2f7..4229cae390ddddcc74f3506dc892ed02b45d7eae 100644 (file)
@@ -459,7 +459,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->sha1, name, 0, path);
+               return grep_sha1(opt, obj->oid.hash, name, 0, path);
        if (obj->type == OBJ_COMMIT || obj->type == OBJ_TREE) {
                struct tree_desc tree;
                void *data;
@@ -468,12 +468,12 @@ static int grep_object(struct grep_opt *opt, const struct pathspec *pathspec,
                int hit, len;
 
                grep_read_lock();
-               data = read_object_with_reference(obj->sha1, tree_type,
+               data = read_object_with_reference(obj->oid.hash, tree_type,
                                                  &size, NULL);
                grep_read_unlock();
 
                if (!data)
-                       die(_("unable to read tree (%s)"), sha1_to_hex(obj->sha1));
+                       die(_("unable to read tree (%s)"), oid_to_hex(&obj->oid));
 
                len = name ? strlen(name) : 0;
                strbuf_init(&base, PATH_MAX + len + 1);
@@ -612,11 +612,6 @@ static int pattern_callback(const struct option *opt, const char *arg,
        return 0;
 }
 
-static int help_callback(const struct option *opt, const char *arg, int unset)
-{
-       return -1;
-}
-
 int cmd_grep(int argc, const char **argv, const char *prefix)
 {
        int hit = 0;
@@ -738,18 +733,9 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
                        PARSE_OPT_OPTARG, NULL, (intptr_t)default_pager },
                OPT_BOOL(0, "ext-grep", &external_grep_allowed__ignored,
                         N_("allow calling of grep(1) (ignored by this build)")),
-               { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"),
-                 PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
                OPT_END()
        };
 
-       /*
-        * 'git grep -h', unlike 'git grep -h <pattern>', is a request
-        * to show usage information and exit.
-        */
-       if (argc == 2 && !strcmp(argv[1], "-h"))
-               usage_with_options(grep_usage, options);
-
        init_grep_defaults();
        git_config(grep_cmd_config, NULL);
        grep_init(&opt, prefix);
@@ -766,8 +752,7 @@ int cmd_grep(int argc, const char **argv, const char *prefix)
         */
        argc = parse_options(argc, argv, prefix, options, grep_usage,
                             PARSE_OPT_KEEP_DASHDASH |
-                            PARSE_OPT_STOP_AT_NON_OPTION |
-                            PARSE_OPT_NO_INTERNAL_HELP);
+                            PARSE_OPT_STOP_AT_NON_OPTION);
        grep_commit_pattern_type(pattern_type_arg, &opt);
 
        if (use_index && !startup_info->have_repository)
index 1ad1bde69600d3097d71ed67d230d2e2f916544e..6a015095877a8d201e68348ecd76ddc5331af357 100644 (file)
@@ -199,7 +199,7 @@ static int mark_link(struct object *obj, int type, void *data, struct fsck_optio
                return -1;
 
        if (type != OBJ_ANY && obj->type != type)
-               die(_("object type mismatch at %s"), sha1_to_hex(obj->sha1));
+               die(_("object type mismatch at %s"), oid_to_hex(&obj->oid));
 
        obj->flags |= FLAG_LINK;
        return 0;
@@ -217,13 +217,13 @@ static unsigned check_object(struct object *obj)
 
        if (!(obj->flags & FLAG_CHECKED)) {
                unsigned long size;
-               int type = sha1_object_info(obj->sha1, &size);
+               int type = sha1_object_info(obj->oid.hash, &size);
                if (type <= 0)
                        die(_("did not receive expected object %s"),
-                             sha1_to_hex(obj->sha1));
+                             oid_to_hex(&obj->oid));
                if (type != obj->type)
                        die(_("object %s: expected type %s, found %s"),
-                           sha1_to_hex(obj->sha1),
+                           oid_to_hex(&obj->oid),
                            typename(obj->type), typename(type));
                obj->flags |= FLAG_CHECKED;
                return 1;
@@ -842,7 +842,7 @@ static void sha1_object(const void *data, struct object_entry *obj_entry,
                            fsck_object(obj, buf, size, &fsck_options))
                                die(_("Error in object"));
                        if (fsck_walk(obj, NULL, &fsck_options))
-                               die(_("Not all child objects of %s are reachable"), sha1_to_hex(obj->sha1));
+                               die(_("Not all child objects of %s are reachable"), oid_to_hex(&obj->oid));
 
                        if (obj->type == OBJ_TREE) {
                                struct tree *item = (struct tree *) obj;
index f59f40768e90cae98cf6541f4c383d762b68b9d1..07229d60f1fd0d581c8cfae2bd0b1c21f06cbc95 100644 (file)
@@ -24,18 +24,6 @@ static int init_shared_repository = -1;
 static const char *init_db_template_dir;
 static const char *git_link;
 
-static void safe_create_dir(const char *dir, int share)
-{
-       if (mkdir(dir, 0777) < 0) {
-               if (errno != EEXIST) {
-                       perror(dir);
-                       exit(1);
-               }
-       }
-       else if (share && adjust_shared_perm(dir))
-               die(_("Could not make %s writable by group"), dir);
-}
-
 static void copy_templates_1(struct strbuf *path, struct strbuf *template,
                             DIR *dir)
 {
index dda671d975255494a6987e82e2ccf23f7f69f7ee..069bd3a909045af432213b21eb4b98bdd29a3443 100644 (file)
@@ -552,7 +552,7 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                const char *name = objects[i].name;
                switch (o->type) {
                case OBJ_BLOB:
-                       ret = show_blob_object(o->sha1, &rev, name);
+                       ret = show_blob_object(o->oid.hash, &rev, name);
                        break;
                case OBJ_TAG: {
                        struct tag *t = (struct tag *)o;
@@ -563,14 +563,14 @@ int cmd_show(int argc, const char **argv, const char *prefix)
                                        diff_get_color_opt(&rev.diffopt, DIFF_COMMIT),
                                        t->tag,
                                        diff_get_color_opt(&rev.diffopt, DIFF_RESET));
-                       ret = show_tag_object(o->sha1, &rev);
+                       ret = show_tag_object(o->oid.hash, &rev);
                        rev.shown_one = 1;
                        if (ret)
                                break;
-                       o = parse_object(t->tagged->sha1);
+                       o = parse_object(t->tagged->oid.hash);
                        if (!o)
                                ret = error(_("Could not read object %s"),
-                                           sha1_to_hex(t->tagged->sha1));
+                                           oid_to_hex(&t->tagged->oid));
                        objects[i].item = o;
                        i--;
                        break;
@@ -830,8 +830,8 @@ static void get_patch_ids(struct rev_info *rev, struct patch_ids *ids)
        o2 = rev->pending.objects[1].item;
        flags1 = o1->flags;
        flags2 = o2->flags;
-       c1 = lookup_commit_reference(o1->sha1);
-       c2 = lookup_commit_reference(o2->sha1);
+       c1 = lookup_commit_reference(o1->oid.hash);
+       c2 = lookup_commit_reference(o2->oid.hash);
 
        if ((flags1 & UNINTERESTING) == (flags2 & UNINTERESTING))
                die(_("Not a range."));
@@ -896,8 +896,8 @@ static void add_branch_description(struct strbuf *buf, const char *branch_name)
 static char *find_branch_name(struct rev_info *rev)
 {
        int i, positive = -1;
-       unsigned char branch_sha1[20];
-       const unsigned char *tip_sha1;
+       struct object_id branch_oid;
+       const struct object_id *tip_oid;
        const char *ref, *v;
        char *full_ref, *branch = NULL;
 
@@ -912,10 +912,10 @@ static char *find_branch_name(struct rev_info *rev)
        if (positive < 0)
                return NULL;
        ref = rev->cmdline.rev[positive].name;
-       tip_sha1 = rev->cmdline.rev[positive].item->sha1;
-       if (dwim_ref(ref, strlen(ref), branch_sha1, &full_ref) &&
+       tip_oid = &rev->cmdline.rev[positive].item->oid;
+       if (dwim_ref(ref, strlen(ref), branch_oid.hash, &full_ref) &&
            skip_prefix(full_ref, "refs/heads/", &v) &&
-           !hashcmp(tip_sha1, branch_sha1))
+           !oidcmp(tip_oid, &branch_oid))
                branch = xstrdup(v);
        free(full_ref);
        return branch;
@@ -993,8 +993,8 @@ static void make_cover_letter(struct rev_info *rev, int use_stdout,
 
        diff_setup_done(&opts);
 
-       diff_tree_sha1(origin->tree->object.sha1,
-                      head->tree->object.sha1,
+       diff_tree_sha1(origin->tree->object.oid.hash,
+                      head->tree->object.oid.hash,
                       "", &opts);
        diffcore_std(&opts);
        diff_flush(&opts);
@@ -1443,7 +1443,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                /* Don't say anything if head and upstream are the same. */
                if (rev.pending.nr == 2) {
                        struct object_array_entry *o = rev.pending.objects;
-                       if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+                       if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
                                return 0;
                }
                get_patch_ids(&rev, &ids);
@@ -1550,7 +1550,7 @@ int cmd_format_patch(int argc, const char **argv, const char *prefix)
                                        string_list_append(rev.ref_message_ids,
                                                           rev.message_id);
                        }
-                       gen_message_id(&rev, sha1_to_hex(commit->object.sha1));
+                       gen_message_id(&rev, oid_to_hex(&commit->object.oid));
                }
 
                if (!use_stdout &&
@@ -1612,12 +1612,12 @@ static void print_commit(char sign, struct commit *commit, int verbose,
 {
        if (!verbose) {
                printf("%c %s\n", sign,
-                      find_unique_abbrev(commit->object.sha1, abbrev));
+                      find_unique_abbrev(commit->object.oid.hash, abbrev));
        } else {
                struct strbuf buf = STRBUF_INIT;
                pp_commit_easy(CMIT_FMT_ONELINE, commit, &buf);
                printf("%c %s %s\n", sign,
-                      find_unique_abbrev(commit->object.sha1, abbrev),
+                      find_unique_abbrev(commit->object.oid.hash, abbrev),
                       buf.buf);
                strbuf_release(&buf);
        }
@@ -1675,7 +1675,7 @@ int cmd_cherry(int argc, const char **argv, const char *prefix)
        /* Don't say anything if head and upstream are the same. */
        if (revs.pending.nr == 2) {
                struct object_array_entry *o = revs.pending.objects;
-               if (hashcmp(o[0].item->sha1, o[1].item->sha1) == 0)
+               if (oidcmp(&o[0].item->oid, &o[1].item->oid) == 0)
                        return 0;
        }
 
index a31024900b0a812c94dfa86024cdf45df36b96a1..fa65a8448aadcb2d26613867cab7822e02e4853c 100644 (file)
@@ -125,7 +125,7 @@ int cmd_ls_remote(int argc, const char **argv, const char *prefix)
                        continue;
                if (!tail_match(pattern, ref->name))
                        continue;
-               printf("%s      %s\n", sha1_to_hex(ref->old_sha1), ref->name);
+               printf("%s      %s\n", oid_to_hex(&ref->old_oid), ref->name);
                status = 0; /* we found something */
        }
        return status;
index 08a8217890df599100ad0da04dc2cfaabd3a11f6..a8911626c2ca9e178c8f4373d544c670afdb6aea 100644 (file)
@@ -16,7 +16,7 @@ static int show_merge_base(struct commit **rev, int rev_nr, int show_all)
                return 1;
 
        while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               printf("%s\n", oid_to_hex(&result->item->object.oid));
                if (!show_all)
                        return 0;
                result = result->next;
@@ -62,7 +62,7 @@ static int handle_independent(int count, const char **args)
                return 1;
 
        while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               printf("%s\n", oid_to_hex(&result->item->object.oid));
                result = result->next;
        }
        return 0;
@@ -83,7 +83,7 @@ static int handle_octopus(int count, const char **args, int show_all)
                return 1;
 
        while (result) {
-               printf("%s\n", sha1_to_hex(result->item->object.sha1));
+               printf("%s\n", oid_to_hex(&result->item->object.oid));
                if (!show_all)
                        return 0;
                result = result->next;
@@ -196,7 +196,7 @@ static int handle_fork_point(int argc, const char **argv)
                goto cleanup_return;
        }
 
-       printf("%s\n", sha1_to_hex(bases->item->object.sha1));
+       printf("%s\n", oid_to_hex(&bases->item->object.oid));
 
 cleanup_return:
        free_commit_list(bases);
index 2a4aafec6a906e22713b88d90f350071bfdb1d59..d4f0cbd45149eebe15f53ff8c34eefd9b3803d82 100644 (file)
@@ -60,7 +60,7 @@ static void *result(struct merge_list *entry, unsigned long *size)
        const char *path = entry->path;
 
        if (!entry->stage)
-               return read_sha1_file(entry->blob->object.sha1, &type, size);
+               return read_sha1_file(entry->blob->object.oid.hash, &type, size);
        base = NULL;
        if (entry->stage == 1) {
                base = entry->blob;
@@ -82,7 +82,7 @@ static void *origin(struct merge_list *entry, unsigned long *size)
        enum object_type type;
        while (entry) {
                if (entry->stage == 2)
-                       return read_sha1_file(entry->blob->object.sha1, &type, size);
+                       return read_sha1_file(entry->blob->object.oid.hash, &type, size);
                entry = entry->link;
        }
        return NULL;
@@ -130,7 +130,7 @@ static void show_result_list(struct merge_list *entry)
        do {
                struct merge_list *link = entry->link;
                static const char *desc[4] = { "result", "base", "our", "their" };
-               printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, sha1_to_hex(entry->blob->object.sha1), entry->path);
+               printf("  %-6s %o %s %s\n", desc[entry->stage], entry->mode, oid_to_hex(&entry->blob->object.oid), entry->path);
                entry = link;
        } while (entry);
 }
index bbf3110f88ae118065da4994de63f345cf414ba7..15bf95b3ac6c54727992103b1943515a1da32ff4 100644 (file)
@@ -365,7 +365,7 @@ static void squash_message(struct commit *commit, struct commit_list *remotehead
        while ((commit = get_revision(&rev)) != NULL) {
                strbuf_addch(&out, '\n');
                strbuf_addf(&out, "commit %s\n",
-                       sha1_to_hex(commit->object.sha1));
+                       oid_to_hex(&commit->object.oid));
                pretty_print_commit(&ctx, commit, &out);
        }
        if (write_in_full(fd, out.buf, out.len) != out.len)
@@ -380,7 +380,7 @@ static void finish(struct commit *head_commit,
                   const unsigned char *new_head, const char *msg)
 {
        struct strbuf reflog_message = STRBUF_INIT;
-       const unsigned char *head = head_commit->object.sha1;
+       const unsigned char *head = head_commit->object.oid.hash;
 
        if (!msg)
                strbuf_addstr(&reflog_message, getenv("GIT_REFLOG_ACTION"));
@@ -497,7 +497,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
                if (ref_exists(truname.buf)) {
                        strbuf_addf(msg,
                                    "%s\t\tbranch '%s'%s of .\n",
-                                   sha1_to_hex(remote_head->object.sha1),
+                                   sha1_to_hex(remote_head->object.oid.hash),
                                    truname.buf + 11,
                                    (early ? " (early part)" : ""));
                        strbuf_release(&truname);
@@ -511,7 +511,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
                desc = merge_remote_util(remote_head);
                if (desc && desc->obj && desc->obj->type == OBJ_TAG) {
                        strbuf_addf(msg, "%s\t\t%s '%s'\n",
-                                   sha1_to_hex(desc->obj->sha1),
+                                   sha1_to_hex(desc->obj->oid.hash),
                                    typename(desc->obj->type),
                                    remote);
                        goto cleanup;
@@ -519,7 +519,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        }
 
        strbuf_addf(msg, "%s\t\tcommit '%s'\n",
-               sha1_to_hex(remote_head->object.sha1), remote);
+               sha1_to_hex(remote_head->object.oid.hash), remote);
 cleanup:
        strbuf_release(&buf);
        strbuf_release(&bname);
@@ -892,7 +892,7 @@ static struct commit *is_old_style_invocation(int argc, const char **argv,
                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.sha1, head))
+               if (hashcmp(second_token->object.oid.hash, head))
                        return NULL;
        }
        return second_token;
@@ -958,14 +958,14 @@ static void write_merge_state(struct commit_list *remoteheads)
        struct strbuf buf = STRBUF_INIT;
 
        for (j = remoteheads; j; j = j->next) {
-               unsigned const char *sha1;
+               struct object_id *oid;
                struct commit *c = j->item;
                if (c->util && merge_remote_util(c)->obj) {
-                       sha1 = merge_remote_util(c)->obj->sha1;
+                       oid = &merge_remote_util(c)->obj->oid;
                } else {
-                       sha1 = c->object.sha1;
+                       oid = &c->object.oid;
                }
-               strbuf_addf(&buf, "%s\n", sha1_to_hex(sha1));
+               strbuf_addf(&buf, "%s\n", oid_to_hex(oid));
        }
        filename = git_path_merge_head();
        fd = open(filename, O_WRONLY | O_CREAT, 0666);
@@ -1274,8 +1274,8 @@ 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"));
-               read_empty(remote_head->object.sha1, 0);
-               update_ref("initial pull", "HEAD", remote_head->object.sha1,
+               read_empty(remote_head->object.oid.hash, 0);
+               update_ref("initial pull", "HEAD", remote_head->object.oid.hash,
                           NULL, 0, UPDATE_REFS_DIE_ON_ERR);
                goto done;
        }
@@ -1289,7 +1289,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * additional safety measure to check for it.
         */
        if (!have_message &&
-           is_old_style_invocation(argc, argv, head_commit->object.sha1)) {
+           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];
@@ -1323,7 +1323,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
 
                        check_commit_signature(commit, &signature_check);
 
-                       find_unique_abbrev_r(hex, commit->object.sha1, DEFAULT_ABBREV);
+                       find_unique_abbrev_r(hex, commit->object.oid.hash, DEFAULT_ABBREV);
                        switch (signature_check.result) {
                        case 'G':
                                break;
@@ -1353,7 +1353,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
        for (p = remoteheads; p; p = p->next) {
                struct commit *commit = p->item;
                strbuf_addf(&buf, "GITHEAD_%s",
-                           sha1_to_hex(commit->object.sha1));
+                           sha1_to_hex(commit->object.oid.hash));
                setenv(buf.buf, merge_remote_util(commit)->name, 1);
                strbuf_reset(&buf);
                if (fast_forward != FF_ONLY &&
@@ -1393,7 +1393,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                free(list);
        }
 
-       update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.sha1,
+       update_ref("updating ORIG_HEAD", "ORIG_HEAD", head_commit->object.oid.hash,
                   NULL, 0, UPDATE_REFS_DIE_ON_ERR);
 
        if (remoteheads && !common)
@@ -1409,16 +1409,16 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                goto done;
        } else if (fast_forward != FF_NO && !remoteheads->next &&
                        !common->next &&
-                       !hashcmp(common->item->object.sha1, head_commit->object.sha1)) {
+                       !hashcmp(common->item->object.oid.hash, head_commit->object.oid.hash)) {
                /* Again the most common case of merging one remote. */
                struct strbuf msg = STRBUF_INIT;
                struct commit *commit;
 
                if (verbosity >= 0) {
                        char from[GIT_SHA1_HEXSZ + 1], to[GIT_SHA1_HEXSZ + 1];
-                       find_unique_abbrev_r(from, head_commit->object.sha1,
+                       find_unique_abbrev_r(from, head_commit->object.oid.hash,
                                              DEFAULT_ABBREV);
-                       find_unique_abbrev_r(to, remoteheads->item->object.sha1,
+                       find_unique_abbrev_r(to, remoteheads->item->object.oid.hash,
                                              DEFAULT_ABBREV);
                        printf(_("Updating %s..%s\n"), from, to);
                }
@@ -1432,14 +1432,14 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                        goto done;
                }
 
-               if (checkout_fast_forward(head_commit->object.sha1,
-                                         commit->object.sha1,
+               if (checkout_fast_forward(head_commit->object.oid.hash,
+                                         commit->object.oid.hash,
                                          overwrite_ignore)) {
                        ret = 1;
                        goto done;
                }
 
-               finish(head_commit, remoteheads, commit->object.sha1, msg.buf);
+               finish(head_commit, remoteheads, commit->object.oid.hash, msg.buf);
                drop_save();
                goto done;
        } else if (!remoteheads->next && common->next)
@@ -1458,9 +1458,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.sha1,
-                                              head_commit->object.sha1,
-                                              remoteheads->item->object.sha1)) {
+                       if (!read_tree_trivial(common->item->object.oid.hash,
+                                              head_commit->object.oid.hash,
+                                              remoteheads->item->object.oid.hash)) {
                                ret = merge_trivial(head_commit, remoteheads);
                                goto done;
                        }
@@ -1483,8 +1483,8 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                         * HEAD^^" would be missed.
                         */
                        common_one = get_merge_bases(head_commit, j->item);
-                       if (hashcmp(common_one->item->object.sha1,
-                               j->item->object.sha1)) {
+                       if (hashcmp(common_one->item->object.oid.hash,
+                               j->item->object.oid.hash)) {
                                up_to_date = 0;
                                break;
                        }
@@ -1520,7 +1520,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
                int ret;
                if (i) {
                        printf(_("Rewinding the tree to pristine...\n"));
-                       restore_state(head_commit->object.sha1, stash);
+                       restore_state(head_commit->object.oid.hash, stash);
                }
                if (use_strategies_nr != 1)
                        printf(_("Trying merge strategy %s...\n"),
@@ -1586,7 +1586,7 @@ int cmd_merge(int argc, const char **argv, const char *prefix)
         * it up.
         */
        if (!best_strategy) {
-               restore_state(head_commit->object.sha1, stash);
+               restore_state(head_commit->object.oid.hash, stash);
                if (use_strategies_nr > 1)
                        fprintf(stderr,
                                _("No merge strategy handled the merge.\n"));
@@ -1599,7 +1599,7 @@ 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.sha1, stash);
+               restore_state(head_commit->object.oid.hash, stash);
                printf(_("Using the %s to prepare resolving by hand.\n"),
                        best_strategy);
                try_merge_strategy(best_strategy, common, remoteheads,
index 0377fc11428b8148b508658547987ea8954723e6..092e03c3cc9b24445ee5ed92cee5b202e04c9ec7 100644 (file)
@@ -162,7 +162,7 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
                struct tag *t = (struct tag *) o;
                if (!t->tagged)
                        break; /* broken repository */
-               o = parse_object(t->tagged->sha1);
+               o = parse_object(t->tagged->oid.hash);
                deref = 1;
        }
        if (o && o->type == OBJ_COMMIT) {
@@ -193,7 +193,7 @@ static const char *get_exact_ref_match(const struct object *o)
                tip_table.sorted = 1;
        }
 
-       found = sha1_pos(o->sha1, tip_table.table, tip_table.nr,
+       found = sha1_pos(o->oid.hash, tip_table.table, tip_table.nr,
                         nth_tip_table_ent);
        if (0 <= found)
                return tip_table.table[found].refname;
@@ -232,19 +232,19 @@ static void show_name(const struct object *obj,
                      int always, int allow_undefined, int name_only)
 {
        const char *name;
-       const unsigned char *sha1 = obj->sha1;
+       const struct object_id *oid = &obj->oid;
 
        if (!name_only)
-               printf("%s ", caller_name ? caller_name : sha1_to_hex(sha1));
+               printf("%s ", caller_name ? caller_name : oid_to_hex(oid));
        name = get_rev_name(obj);
        if (name)
                printf("%s\n", name);
        else if (allow_undefined)
                printf("undefined\n");
        else if (always)
-               printf("%s\n", find_unique_abbrev(sha1, DEFAULT_ABBREV));
+               printf("%s\n", find_unique_abbrev(oid->hash, DEFAULT_ABBREV));
        else
-               die("cannot describe '%s'", sha1_to_hex(sha1));
+               die("cannot describe '%s'", oid_to_hex(oid));
 }
 
 static char const * const name_rev_usage[] = {
index 515cebbeb8a33eb982a1ddccdc9d69e09beefb1d..52aa9af74be8d47780d4762185c5cbe09624b382 100644 (file)
@@ -707,7 +707,7 @@ static int merge_commit(struct notes_merge_options *o)
                die("Could not parse commit from NOTES_MERGE_PARTIAL.");
 
        if (partial->parents)
-               hashcpy(parent_sha1, partial->parents->item->object.sha1);
+               hashcpy(parent_sha1, partial->parents->item->object.oid.hash);
        else
                hashclr(parent_sha1);
 
index 1c63f8f28c7d925ee5647f190d236e64fc6e05a2..4dae5b11c28deb3c296cbd89753cafe3ca7a1e90 100644 (file)
@@ -2277,7 +2277,7 @@ static void read_object_list_from_stdin(void)
 
 static void show_commit(struct commit *commit, void *data)
 {
-       add_object_entry(commit->object.sha1, OBJ_COMMIT, NULL, 0);
+       add_object_entry(commit->object.oid.hash, OBJ_COMMIT, NULL, 0);
        commit->object.flags |= OBJECT_ADDED;
 
        if (write_bitmap_index)
@@ -2291,7 +2291,7 @@ static void show_object(struct object *obj,
        char *name = path_name(path, last);
 
        add_preferred_base_object(name);
-       add_object_entry(obj->sha1, obj->type, name, 0);
+       add_object_entry(obj->oid.hash, obj->type, name, 0);
        obj->flags |= OBJECT_ADDED;
 
        /*
@@ -2303,7 +2303,7 @@ static void show_object(struct object *obj,
 
 static void show_edge(struct commit *commit)
 {
-       add_preferred_base(commit->object.sha1);
+       add_preferred_base(commit->object.oid.hash);
 }
 
 struct in_pack_object {
@@ -2319,7 +2319,7 @@ struct in_pack {
 
 static void mark_in_pack_object(struct object *object, struct packed_git *p, struct in_pack *in_pack)
 {
-       in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->sha1, p);
+       in_pack->array[in_pack->nr].offset = find_pack_entry_one(object->oid.hash, p);
        in_pack->array[in_pack->nr].object = object;
        in_pack->nr++;
 }
@@ -2338,7 +2338,7 @@ static int ofscmp(const void *a_, const void *b_)
        else if (a->offset > b->offset)
                return 1;
        else
-               return hashcmp(a->object->sha1, b->object->sha1);
+               return oidcmp(&a->object->oid, &b->object->oid);
 }
 
 static void add_objects_in_unpacked_packs(struct rev_info *revs)
@@ -2376,7 +2376,7 @@ static void add_objects_in_unpacked_packs(struct rev_info *revs)
                      ofscmp);
                for (i = 0; i < in_pack.nr; i++) {
                        struct object *o = in_pack.array[i].object;
-                       add_object_entry(o->sha1, o->type, "", 0);
+                       add_object_entry(o->oid.hash, o->type, "", 0);
                }
        }
        free(in_pack.array);
@@ -2484,12 +2484,12 @@ static void record_recent_object(struct object *obj,
                                 const char *last,
                                 void *data)
 {
-       sha1_array_append(&recent_objects, obj->sha1);
+       sha1_array_append(&recent_objects, obj->oid.hash);
 }
 
 static void record_recent_commit(struct commit *commit, void *data)
 {
-       sha1_array_append(&recent_objects, commit->object.sha1);
+       sha1_array_append(&recent_objects, commit->object.oid.hash);
 }
 
 static void get_object_list(int ac, const char **av)
index bf3fd3f9c8d80ff84ace4150214118355585e917..5145fc60a0377a7fb799555892909f979aca9076 100644 (file)
@@ -743,7 +743,7 @@ static int get_octopus_merge_base(unsigned char *merge_base,
        if (!result)
                return 1;
 
-       hashcpy(merge_base, result->item->object.sha1);
+       hashcpy(merge_base, result->item->object.oid.hash);
        return 0;
 }
 
index f06f70a75f4926d7573e3b1d8f06dbe385e10fdc..ca38131873aad1c5dd5b8001aceb39ed906b3c2d 100644 (file)
@@ -246,7 +246,7 @@ static void show_one_alternate_sha1(const unsigned char sha1[20], void *unused)
 static void collect_one_alternate_ref(const struct ref *ref, void *data)
 {
        struct sha1_array *sa = data;
-       sha1_array_append(sa, ref->old_sha1);
+       sha1_array_append(sa, ref->old_oid.hash);
 }
 
 static void write_head_info(void)
index cf1145e635c559206a6b0d61e41321fa914e5e42..f39960e5e480abcd08898b2f04aa444c1d7bec17 100644 (file)
@@ -126,7 +126,7 @@ static int commit_is_complete(struct commit *commit)
                struct commit_list *parent;
 
                c = (struct commit *)study.objects[--study.nr].item;
-               if (!c->object.parsed && !parse_object(c->object.sha1))
+               if (!c->object.parsed && !parse_object(c->object.oid.hash))
                        c->object.flags |= INCOMPLETE;
 
                if (c->object.flags & INCOMPLETE) {
@@ -152,7 +152,7 @@ static int commit_is_complete(struct commit *commit)
                for (i = 0; i < found.nr; i++) {
                        struct commit *c =
                                (struct commit *)found.objects[i].item;
-                       if (!tree_is_complete(c->tree->object.sha1)) {
+                       if (!tree_is_complete(c->tree->object.oid.hash)) {
                                is_incomplete = 1;
                                c->object.flags |= INCOMPLETE;
                        }
index e4c3ea130c98b01f87bed617a6386b03158b42d5..6694cf20efdc01fb59d0b6afa41f7b1557c9b2d5 100644 (file)
@@ -401,7 +401,7 @@ static int get_push_ref_states(const struct ref *remote_refs,
 
                if (!ref->peer_ref)
                        continue;
-               hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
 
                item = string_list_append(&states->push,
                                          abbrev_branch(ref->peer_ref->name));
@@ -410,14 +410,14 @@ static int get_push_ref_states(const struct ref *remote_refs,
                info->forced = ref->force;
                info->dest = xstrdup(abbrev_branch(ref->name));
 
-               if (is_null_sha1(ref->new_sha1)) {
+               if (is_null_oid(&ref->new_oid)) {
                        info->status = PUSH_STATUS_DELETE;
-               } else if (!hashcmp(ref->old_sha1, ref->new_sha1))
+               } else if (!oidcmp(&ref->old_oid, &ref->new_oid))
                        info->status = PUSH_STATUS_UPTODATE;
-               else if (is_null_sha1(ref->old_sha1))
+               else if (is_null_oid(&ref->old_oid))
                        info->status = PUSH_STATUS_CREATE;
-               else if (has_sha1_file(ref->old_sha1) &&
-                        ref_newer(ref->new_sha1, ref->old_sha1))
+               else if (has_object_file(&ref->old_oid) &&
+                        ref_newer(&ref->new_oid, &ref->old_oid))
                        info->status = PUSH_STATUS_FASTFORWARD;
                else
                        info->status = PUSH_STATUS_OUTOFDATE;
index 6b3c469a331e62e13fb6f9626ee5a64f28747c06..748c6ca954679e3fc47c25991dd50e539eaa543d 100644 (file)
@@ -358,10 +358,10 @@ static void check_one_mergetag(struct commit *commit,
 
        /* iterate over new parents */
        for (i = 1; i < mergetag_data->argc; i++) {
-               unsigned char sha1[20];
-               if (get_sha1(mergetag_data->argv[i], sha1) < 0)
+               struct object_id oid;
+               if (get_sha1(mergetag_data->argv[i], oid.hash) < 0)
                        die(_("Not a valid object name: '%s'"), mergetag_data->argv[i]);
-               if (!hashcmp(tag->tagged->sha1, sha1))
+               if (!oidcmp(&tag->tagged->oid, &oid))
                        return; /* found */
        }
 
index c503e75a590df93bcc46e564583352dcffa60891..092c3a5399c731e83058233f8e3e459be493e140 100644 (file)
@@ -96,7 +96,7 @@ static void print_new_head_line(struct commit *commit)
        const char *hex, *body;
        const char *msg;
 
-       hex = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+       hex = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
        printf(_("HEAD is now at %s"), hex);
        msg = logmsg_reencode(commit, NULL, get_log_output_encoding());
        body = strstr(msg, "\n\n");
@@ -269,7 +269,7 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        int reset_type = NONE, update_ref_status = 0, quiet = 0;
        int patch_mode = 0, unborn;
        const char *rev;
-       unsigned char sha1[20];
+       struct object_id oid;
        struct pathspec pathspec;
        int intent_to_add = 0;
        const struct option options[] = {
@@ -295,26 +295,26 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                                                PARSE_OPT_KEEP_DASHDASH);
        parse_args(&pathspec, argv, prefix, patch_mode, &rev);
 
-       unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", sha1);
+       unborn = !strcmp(rev, "HEAD") && get_sha1("HEAD", oid.hash);
        if (unborn) {
                /* reset on unborn branch: treat as reset to empty tree */
-               hashcpy(sha1, EMPTY_TREE_SHA1_BIN);
+               hashcpy(oid.hash, EMPTY_TREE_SHA1_BIN);
        } else if (!pathspec.nr) {
                struct commit *commit;
-               if (get_sha1_committish(rev, sha1))
+               if (get_sha1_committish(rev, oid.hash))
                        die(_("Failed to resolve '%s' as a valid revision."), rev);
-               commit = lookup_commit_reference(sha1);
+               commit = lookup_commit_reference(oid.hash);
                if (!commit)
                        die(_("Could not parse object '%s'."), rev);
-               hashcpy(sha1, commit->object.sha1);
+               oidcpy(&oid, &commit->object.oid);
        } else {
                struct tree *tree;
-               if (get_sha1_treeish(rev, sha1))
+               if (get_sha1_treeish(rev, oid.hash))
                        die(_("Failed to resolve '%s' as a valid tree."), rev);
-               tree = parse_tree_indirect(sha1);
+               tree = parse_tree_indirect(oid.hash);
                if (!tree)
                        die(_("Could not parse object '%s'."), rev);
-               hashcpy(sha1, tree->object.sha1);
+               oidcpy(&oid, &tree->object.oid);
        }
 
        if (patch_mode) {
@@ -357,15 +357,15 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
                hold_locked_index(lock, 1);
                if (reset_type == MIXED) {
                        int flags = quiet ? REFRESH_QUIET : REFRESH_IN_PORCELAIN;
-                       if (read_from_tree(&pathspec, sha1, intent_to_add))
+                       if (read_from_tree(&pathspec, oid.hash, intent_to_add))
                                return 1;
                        if (get_git_work_tree())
                                refresh_index(&the_index, flags, NULL, NULL,
                                              _("Unstaged changes after reset:"));
                } else {
-                       int err = reset_index(sha1, reset_type, quiet);
+                       int err = reset_index(oid.hash, reset_type, quiet);
                        if (reset_type == KEEP && !err)
-                               err = reset_index(sha1, MIXED, quiet);
+                               err = reset_index(oid.hash, MIXED, quiet);
                        if (err)
                                die(_("Could not reset index file to revision '%s'."), rev);
                }
@@ -377,10 +377,10 @@ int cmd_reset(int argc, const char **argv, const char *prefix)
        if (!pathspec.nr && !unborn) {
                /* Any resets without paths update HEAD to the head being
                 * switched to, saving the previous head in ORIG_HEAD before. */
-               update_ref_status = reset_refs(rev, sha1);
+               update_ref_status = reset_refs(rev, oid.hash);
 
                if (reset_type == HARD && !update_ref_status && !quiet)
-                       print_new_head_line(lookup_commit_reference(sha1));
+                       print_new_head_line(lookup_commit_reference(oid.hash));
        }
        if (!pathspec.nr)
                remove_branch_state();
index 491d298fa25cebb1afd9b33cbbd099efe72629bc..3aa89a1a3cd951afdb97cf1fe371404fc96bca8d 100644 (file)
@@ -81,14 +81,14 @@ static void show_commit(struct commit *commit, void *data)
        if (!revs->graph)
                fputs(get_revision_mark(revs, commit), stdout);
        if (revs->abbrev_commit && revs->abbrev)
-               fputs(find_unique_abbrev(commit->object.sha1, revs->abbrev),
+               fputs(find_unique_abbrev(commit->object.oid.hash, revs->abbrev),
                      stdout);
        else
-               fputs(sha1_to_hex(commit->object.sha1), stdout);
+               fputs(oid_to_hex(&commit->object.oid), stdout);
        if (revs->print_parents) {
                struct commit_list *parents = commit->parents;
                while (parents) {
-                       printf(" %s", sha1_to_hex(parents->item->object.sha1));
+                       printf(" %s", oid_to_hex(&parents->item->object.oid));
                        parents = parents->next;
                }
        }
@@ -97,7 +97,7 @@ static void show_commit(struct commit *commit, void *data)
 
                children = lookup_decoration(&revs->children, &commit->object);
                while (children) {
-                       printf(" %s", sha1_to_hex(children->item->object.sha1));
+                       printf(" %s", oid_to_hex(&children->item->object.oid));
                        children = children->next;
                }
        }
@@ -182,10 +182,10 @@ static void finish_object(struct object *obj,
                          void *cb_data)
 {
        struct rev_list_info *info = cb_data;
-       if (obj->type == OBJ_BLOB && !has_sha1_file(obj->sha1))
-               die("missing blob object '%s'", sha1_to_hex(obj->sha1));
+       if (obj->type == OBJ_BLOB && !has_object_file(&obj->oid))
+               die("missing blob object '%s'", oid_to_hex(&obj->oid));
        if (info->revs->verify_objects && !obj->parsed && obj->type != OBJ_COMMIT)
-               parse_object(obj->sha1);
+               parse_object(obj->oid.hash);
 }
 
 static void show_object(struct object *obj,
@@ -201,7 +201,7 @@ static void show_object(struct object *obj,
 
 static void show_edge(struct commit *commit)
 {
-       printf("-%s\n", sha1_to_hex(commit->object.sha1));
+       printf("-%s\n", oid_to_hex(&commit->object.oid));
 }
 
 static void print_var_str(const char *var, const char *val)
@@ -242,7 +242,7 @@ static int show_bisect_vars(struct rev_list_info *info, int reaches, int all)
                cnt = reaches;
 
        if (revs->commits)
-               sha1_to_hex_r(hex, revs->commits->item->object.sha1);
+               sha1_to_hex_r(hex, revs->commits->item->object.oid.hash);
 
        if (flags & BISECT_SHOW_ALL) {
                traverse_commit_list(revs, show_commit, show_object, info);
index e92a782f7729f0e5522969238920be2b7e607366..7e074aad408ee7d63d86643878ff5c5bb311f732 100644 (file)
@@ -282,7 +282,7 @@ static int try_difference(const char *arg)
                        exclude = get_merge_bases(a, b);
                        while (exclude) {
                                struct commit *commit = pop_commit(&exclude);
-                               show_rev(REVERSED, commit->object.sha1, NULL);
+                               show_rev(REVERSED, commit->object.oid.hash, NULL);
                        }
                }
                *dotdot = '.';
@@ -319,7 +319,7 @@ static int try_parent_shorthands(const char *arg)
        commit = lookup_commit_reference(sha1);
        for (parents = commit->parents; parents; parents = parents->next)
                show_rev(parents_only ? NORMAL : REVERSED,
-                               parents->item->object.sha1, arg);
+                               parents->item->object.oid.hash, arg);
 
        *dotdot = '^';
        return 1;
index 007cc66a036429d602cf7afe19c163fcf6d06395..35ebd17f80d7784f17023a4f76ac71e1317a94d0 100644 (file)
@@ -129,7 +129,7 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        }
        if (!author) {
                warning(_("Missing author: %s"),
-                   sha1_to_hex(commit->object.sha1));
+                   oid_to_hex(&commit->object.oid));
                return;
        }
        if (log->user_format) {
index e17744bc0dad37b75865b5da8c8c3037bcc02167..25669357e97e892ffb050d879b74d13a91d4fafc 100644 (file)
@@ -291,7 +291,7 @@ static void show_one_commit(struct commit *commit, int no_name)
                }
                else
                        printf("[%s] ",
-                              find_unique_abbrev(commit->object.sha1,
+                              find_unique_abbrev(commit->object.oid.hash,
                                                  DEFAULT_ABBREV));
        }
        puts(pretty_str);
@@ -496,7 +496,7 @@ static int show_merge_base(struct commit_list *seen, int num_rev)
                int flags = commit->object.flags & all_mask;
                if (!(flags & UNINTERESTING) &&
                    ((flags & all_revs) == all_revs)) {
-                       puts(sha1_to_hex(commit->object.sha1));
+                       puts(oid_to_hex(&commit->object.oid));
                        exit_status = 0;
                        commit->object.flags |= UNINTERESTING;
                }
@@ -516,7 +516,7 @@ static int show_independent(struct commit **rev,
                unsigned int flag = rev_mask[i];
 
                if (commit->object.flags == flag)
-                       puts(sha1_to_hex(commit->object.sha1));
+                       puts(oid_to_hex(&commit->object.oid));
                commit->object.flags |= UNINTERESTING;
        }
        return 0;
@@ -867,7 +867,7 @@ int cmd_show_branch(int ac, const char **av, const char *prefix)
                                                  head_len,
                                                  ref_name[i],
                                                  head_oid.hash,
-                                                 rev[i]->object.sha1);
+                                                 rev[i]->object.oid.hash);
                        if (extra < 0)
                                printf("%c [%s] ",
                                       is_head ? '*' : ' ', ref_name[i]);
index 264c3920075952b179170b65960e47b3a5b06c61..6d4e669002b1e58ebf40af5543221d809ffd0427 100644 (file)
@@ -161,11 +161,6 @@ static int exclude_existing_callback(const struct option *opt, const char *arg,
        return 0;
 }
 
-static int help_callback(const struct option *opt, const char *arg, int unset)
-{
-       return -1;
-}
-
 static const struct option show_ref_options[] = {
        OPT_BOOL(0, "tags", &tags_only, N_("only show tags (can be combined with heads)")),
        OPT_BOOL(0, "heads", &heads_only, N_("only show heads (can be combined with tags)")),
@@ -186,18 +181,13 @@ static const struct option show_ref_options[] = {
        { OPTION_CALLBACK, 0, "exclude-existing", &exclude_existing_arg,
          N_("pattern"), N_("show refs from stdin that aren't in local repository"),
          PARSE_OPT_OPTARG | PARSE_OPT_NONEG, exclude_existing_callback },
-       { OPTION_CALLBACK, 0, "help-all", NULL, NULL, N_("show usage"),
-         PARSE_OPT_HIDDEN | PARSE_OPT_NOARG, help_callback },
        OPT_END()
 };
 
 int cmd_show_ref(int argc, const char **argv, const char *prefix)
 {
-       if (argc == 2 && !strcmp(argv[1], "-h"))
-               usage_with_options(show_ref_usage, show_ref_options);
-
        argc = parse_options(argc, argv, prefix, show_ref_options,
-                            show_ref_usage, PARSE_OPT_NO_INTERNAL_HELP);
+                            show_ref_usage, 0);
 
        if (exclude_arg)
                return exclude_existing(exclude_existing_arg);
index 7c3e79c48d68251f8af94d91ef51d22567c53c07..875e7ed99820998aa5d0e18f73b59763201cdb6c 100644 (file)
@@ -46,7 +46,7 @@ static void add_object_buffer(struct object *object, char *buffer, unsigned long
        obj->buffer = buffer;
        obj->size = size;
        if (add_decoration(&obj_decorate, object, obj))
-               die("object %s tried to add buffer twice!", sha1_to_hex(object->sha1));
+               die("object %s tried to add buffer twice!", oid_to_hex(&object->oid));
 }
 
 /*
@@ -170,7 +170,7 @@ static void write_cached_object(struct object *obj, struct obj_buffer *obj_buf)
        unsigned char sha1[20];
 
        if (write_sha1_file(obj_buf->buffer, obj_buf->size, typename(obj->type), sha1) < 0)
-               die("failed to write object %s", sha1_to_hex(obj->sha1));
+               die("failed to write object %s", oid_to_hex(&obj->oid));
        obj->flags |= FLAG_WRITTEN;
 }
 
@@ -194,7 +194,7 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op
 
        if (!(obj->flags & FLAG_OPEN)) {
                unsigned long size;
-               int type = sha1_object_info(obj->sha1, &size);
+               int type = sha1_object_info(obj->oid.hash, &size);
                if (type != obj->type || type <= 0)
                        die("object of unexpected type");
                obj->flags |= FLAG_WRITTEN;
@@ -203,12 +203,12 @@ static int check_object(struct object *obj, int type, void *data, struct fsck_op
 
        obj_buf = lookup_object_buffer(obj);
        if (!obj_buf)
-               die("Whoops! Cannot find object '%s'", sha1_to_hex(obj->sha1));
+               die("Whoops! Cannot find object '%s'", oid_to_hex(&obj->oid));
        if (fsck_object(obj, obj_buf->buffer, obj_buf->size, &fsck_options))
                die("Error in object");
        fsck_options.walk = check_object;
        if (fsck_walk(obj, NULL, &fsck_options))
-               die("Error on reachable objects of %s", sha1_to_hex(obj->sha1));
+               die("Error on reachable objects of %s", oid_to_hex(&obj->oid));
        write_cached_object(obj, obj_buf);
        return 0;
 }
index d281f6d887691fb43ebb052091f84e78aabe6d01..475b9581a5583166c43199f1662b510842c59c7d 100644 (file)
@@ -277,7 +277,7 @@ static int add_worktree(const char *path, const char *refname,
 
        if (commit)
                argv_array_pushl(&cp.args, "update-ref", "HEAD",
-                                sha1_to_hex(commit->object.sha1), NULL);
+                                oid_to_hex(&commit->object.oid), NULL);
        else
                argv_array_pushl(&cp.args, "symbolic-ref", "HEAD",
                                 symref.buf, NULL);
index b9dacc0241f0c58244b62ddff137c85291c37dd0..506ac49691bb7d53b07baf2a1d2581bf6f7ca82a 100644 (file)
--- a/bundle.c
+++ b/bundle.c
@@ -171,7 +171,7 @@ int verify_bundle(struct bundle_header *header, int verbose)
                if (!(refs.objects[i].item->flags & SHOWN)) {
                        if (++ret == 1)
                                error("%s", message);
-                       error("%s %s", sha1_to_hex(refs.objects[i].item->sha1),
+                       error("%s %s", oid_to_hex(&refs.objects[i].item->oid),
                                refs.objects[i].name);
                }
 
@@ -217,7 +217,7 @@ static int is_tag_in_date_range(struct object *tag, struct rev_info *revs)
        if (revs->max_age == -1 && revs->min_age == -1)
                goto out;
 
-       buf = read_sha1_file(tag->sha1, &type, &size);
+       buf = read_sha1_file(tag->oid.hash, &type, &size);
        if (!buf)
                goto out;
        line = memmem(buf, size, "\ntagger ", 8);
@@ -256,7 +256,7 @@ static int write_pack_data(int bundle_fd, struct rev_info *revs)
                struct object *object = revs->pending.objects[i].item;
                if (object->flags & UNINTERESTING)
                        write_or_die(pack_objects.in, "^", 1);
-               write_or_die(pack_objects.in, sha1_to_hex(object->sha1), 40);
+               write_or_die(pack_objects.in, oid_to_hex(&object->oid), GIT_SHA1_HEXSZ);
                write_or_die(pack_objects.in, "\n", 1);
        }
        close(pack_objects.in);
@@ -321,16 +321,16 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
 
        for (i = 0; i < revs->pending.nr; i++) {
                struct object_array_entry *e = revs->pending.objects + i;
-               unsigned char sha1[20];
+               struct object_id oid;
                char *ref;
                const char *display_ref;
                int flag;
 
                if (e->item->flags & UNINTERESTING)
                        continue;
-               if (dwim_ref(e->name, strlen(e->name), sha1, &ref) != 1)
+               if (dwim_ref(e->name, strlen(e->name), oid.hash, &ref) != 1)
                        goto skip_write_ref;
-               if (read_ref_full(e->name, RESOLVE_REF_READING, sha1, &flag))
+               if (read_ref_full(e->name, RESOLVE_REF_READING, oid.hash, &flag))
                        flag = 0;
                display_ref = (flag & REF_ISSYMREF) ? e->name : ref;
 
@@ -360,13 +360,13 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
                 * commit that is referenced by the tag, and not the tag
                 * itself.
                 */
-               if (hashcmp(sha1, e->item->sha1)) {
+               if (oidcmp(&oid, &e->item->oid)) {
                        /*
                         * Is this the positive end of a range expressed
                         * in terms of a tag (e.g. v2.0 from the range
                         * "v1.0..v2.0")?
                         */
-                       struct commit *one = lookup_commit_reference(sha1);
+                       struct commit *one = lookup_commit_reference(oid.hash);
                        struct object *obj;
 
                        if (e->item == &(one->object)) {
@@ -378,7 +378,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
                                 * end up triggering "empty bundle"
                                 * error.
                                 */
-                               obj = parse_object_or_die(sha1, e->name);
+                               obj = parse_object_or_die(oid.hash, e->name);
                                obj->flags |= SHOWN;
                                add_pending_object(revs, obj, e->name);
                        }
@@ -386,7 +386,7 @@ static int write_bundle_refs(int bundle_fd, struct rev_info *revs)
                }
 
                ref_count++;
-               write_or_die(bundle_fd, sha1_to_hex(e->item->sha1), 40);
+               write_or_die(bundle_fd, oid_to_hex(&e->item->oid), 40);
                write_or_die(bundle_fd, " ", 1);
                write_or_die(bundle_fd, display_ref, strlen(display_ref));
                write_or_die(bundle_fd, "\n", 1);
index feace8bd90913ff28fa7ec2d052b355ad23935ca..a59e6f1e1fcfb65bd2fe9cdba0db60d6d87e7d96 100644 (file)
@@ -657,7 +657,7 @@ static void prime_cache_tree_rec(struct cache_tree *it, struct tree *tree)
        struct name_entry entry;
        int cnt;
 
-       hashcpy(it->sha1, tree->object.sha1);
+       hashcpy(it->sha1, tree->object.oid.hash);
        init_tree_desc(&desc, tree->buffer, tree->size);
        cnt = 0;
        while (tree_entry(&desc, &entry)) {
diff --git a/cache.h b/cache.h
index 736abc03a4b2bde987077440453e253150a584f7..5ab6cb50a61e24dc418b351db8acc648b740c244 100644 (file)
--- a/cache.h
+++ b/cache.h
 #include "string-list.h"
 
 #include SHA1_HEADER
-#ifndef git_SHA_CTX
-#define git_SHA_CTX    SHA_CTX
-#define git_SHA1_Init  SHA1_Init
-#define git_SHA1_Update        SHA1_Update
-#define git_SHA1_Final SHA1_Final
+#ifndef platform_SHA_CTX
+/*
+ * platform's underlying implementation of SHA-1; could be OpenSSL,
+ * blk_SHA, Apple CommonCrypto, etc...  Note that including
+ * SHA1_HEADER may have already defined platform_SHA_CTX for our
+ * own implementations like block-sha1 and ppc-sha1, so we list
+ * the default for OpenSSL compatible SHA-1 implementations here.
+ */
+#define platform_SHA_CTX       SHA_CTX
+#define platform_SHA1_Init     SHA1_Init
+#define platform_SHA1_Update   SHA1_Update
+#define platform_SHA1_Final            SHA1_Final
+#endif
+
+#define git_SHA_CTX            platform_SHA_CTX
+#define git_SHA1_Init          platform_SHA1_Init
+#define git_SHA1_Update                platform_SHA1_Update
+#define git_SHA1_Final         platform_SHA1_Final
+
+#ifdef SHA1_MAX_BLOCK_SIZE
+#include "compat/sha1-chunked.h"
+#undef git_SHA1_Update
+#define git_SHA1_Update                git_SHA1_Update_Chunked
 #endif
 
 #include <zlib.h>
@@ -1030,6 +1048,9 @@ static inline int has_sha1_file(const unsigned char *sha1)
        return has_sha1_file_with_flags(sha1, 0);
 }
 
+/* Same as the above, except for struct object_id. */
+extern int has_object_file(const struct object_id *oid);
+
 /*
  * Return true iff an alternate object database has a loose object
  * with the specified name.  This function does not respect replace
@@ -1750,4 +1771,12 @@ void stat_validity_update(struct stat_validity *sv, int fd);
 int versioncmp(const char *s1, const char *s2);
 void sleep_millisec(int millisec);
 
+/*
+ * Create a directory and (if share is nonzero) adjust its permissions
+ * according to the shared_repository setting. Only use this for
+ * directories under $GIT_DIR.  Don't use it for working tree
+ * directories.
+ */
+void safe_create_dir(const char *dir, int share);
+
 #endif /* CACHE_H */
index 0f62f54b5ef4bb4ec81cff5aead2d6cd66da194e..55713049a4d6764722307e2a2c870996ff9b373b 100644 (file)
@@ -1540,9 +1540,9 @@ void diff_tree_combined_merge(const struct commit *commit, int dense,
        struct sha1_array parents = SHA1_ARRAY_INIT;
 
        while (parent) {
-               sha1_array_append(&parents, parent->item->object.sha1);
+               sha1_array_append(&parents, parent->item->object.oid.hash);
                parent = parent->next;
        }
-       diff_tree_combined(commit->object.sha1, &parents, dense, rev);
+       diff_tree_combined(commit->object.oid.hash, &parents, dense, rev);
        sha1_array_clear(&parents);
 }
index d1810c940b3cb354437e86eee38e2c2499375e80..40388d71e754bb35e3879a73f6e8a0ad8904ca41 100644 (file)
--- a/commit.c
+++ b/commit.c
@@ -38,7 +38,7 @@ struct commit *lookup_commit_or_die(const unsigned char *sha1, const char *ref_n
        struct commit *c = lookup_commit_reference(sha1);
        if (!c)
                die(_("could not parse %s"), ref_name);
-       if (hashcmp(sha1, c->object.sha1)) {
+       if (hashcmp(sha1, c->object.oid.hash)) {
                warning(_("%s %s is not a commit!"),
                        ref_name, sha1_to_hex(sha1));
        }
@@ -262,13 +262,13 @@ const void *get_commit_buffer(const struct commit *commit, unsigned long *sizep)
        if (!ret) {
                enum object_type type;
                unsigned long size;
-               ret = read_sha1_file(commit->object.sha1, &type, &size);
+               ret = read_sha1_file(commit->object.oid.hash, &type, &size);
                if (!ret)
                        die("cannot read commit object %s",
-                           sha1_to_hex(commit->object.sha1));
+                           oid_to_hex(&commit->object.oid));
                if (type != OBJ_COMMIT)
                        die("expected commit for %s, got %s",
-                           sha1_to_hex(commit->object.sha1), typename(type));
+                           oid_to_hex(&commit->object.oid), typename(type));
                if (sizep)
                        *sizep = size;
        }
@@ -327,22 +327,22 @@ int parse_commit_buffer(struct commit *item, const void *buffer, unsigned long s
        tail += size;
        if (tail <= bufptr + tree_entry_len + 1 || memcmp(bufptr, "tree ", 5) ||
                        bufptr[tree_entry_len] != '\n')
-               return error("bogus commit object %s", sha1_to_hex(item->object.sha1));
+               return error("bogus commit object %s", oid_to_hex(&item->object.oid));
        if (get_sha1_hex(bufptr + 5, parent.hash) < 0)
                return error("bad tree pointer in commit %s",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        item->tree = lookup_tree(parent.hash);
        bufptr += tree_entry_len + 1; /* "tree " + "hex sha1" + "\n" */
        pptr = &item->parents;
 
-       graft = lookup_commit_graft(item->object.sha1);
+       graft = lookup_commit_graft(item->object.oid.hash);
        while (bufptr + parent_entry_len < tail && !memcmp(bufptr, "parent ", 7)) {
                struct commit *new_parent;
 
                if (tail <= bufptr + parent_entry_len + 1 ||
                    get_sha1_hex(bufptr + 7, parent.hash) ||
                    bufptr[parent_entry_len] != '\n')
-                       return error("bad parents in commit %s", sha1_to_hex(item->object.sha1));
+                       return error("bad parents in commit %s", oid_to_hex(&item->object.oid));
                bufptr += parent_entry_len + 1;
                /*
                 * The clone is shallow if nr_parent < 0, and we must
@@ -380,15 +380,15 @@ int parse_commit_gently(struct commit *item, int quiet_on_missing)
                return -1;
        if (item->object.parsed)
                return 0;
-       buffer = read_sha1_file(item->object.sha1, &type, &size);
+       buffer = read_sha1_file(item->object.oid.hash, &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        if (type != OBJ_COMMIT) {
                free(buffer);
                return error("Object %s not a commit",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        }
        ret = parse_commit_buffer(item, buffer, size);
        if (save_commit_buffer && !ret) {
@@ -403,7 +403,7 @@ void parse_commit_or_die(struct commit *item)
 {
        if (parse_commit(item))
                die("unable to parse commit %s",
-                   item ? sha1_to_hex(item->object.sha1) : "(null)");
+                   item ? oid_to_hex(&item->object.oid) : "(null)");
 }
 
 int find_commit_subject(const char *commit_buffer, const char **subject)
@@ -563,7 +563,7 @@ void clear_commit_marks_for_object_array(struct object_array *a, unsigned mark)
 
        for (i = 0; i < a->nr; i++) {
                object = a->objects[i].item;
-               commit = lookup_commit_reference_gently(object->sha1, 1);
+               commit = lookup_commit_reference_gently(object->oid.hash, 1);
                if (commit)
                        clear_commit_marks(commit, mark);
        }
@@ -1206,7 +1206,7 @@ static void handle_signed_tag(struct commit *parent, struct commit_extra_header
        desc = merge_remote_util(parent);
        if (!desc || !desc->obj)
                return;
-       buf = read_sha1_file(desc->obj->sha1, &type, &size);
+       buf = read_sha1_file(desc->obj->oid.hash, &type, &size);
        if (!buf || type != OBJ_TAG)
                goto free_return;
        len = parse_signature(buf, size);
@@ -1539,7 +1539,7 @@ int commit_tree_extended(const char *msg, size_t msg_len,
        while (parents) {
                struct commit *parent = pop_commit(&parents);
                strbuf_addf(&buffer, "parent %s\n",
-                           sha1_to_hex(parent->object.sha1));
+                           oid_to_hex(&parent->object.oid));
        }
 
        /* Person/date information */
@@ -1623,7 +1623,7 @@ void print_commit_list(struct commit_list *list,
 {
        for ( ; list; list = list->next) {
                const char *format = list->next ? format_cur : format_last;
-               printf(format, sha1_to_hex(list->item->object.sha1));
+               printf(format, oid_to_hex(&list->item->object.oid));
        }
 }
 
index c8b9b0e1a6ce63d16aec142611d03fbe24784b43..d3fb2641813404a95709d2b219b9ab1fa1434296 100644 (file)
 #undef TYPE_BOOL
 #endif
 
+#ifndef SHA1_MAX_BLOCK_SIZE
+#error Using Apple Common Crypto library requires setting SHA1_MAX_BLOCK_SIZE
+#endif
+
 #ifdef APPLE_LION_OR_NEWER
 #define git_CC_error_check(pattern, err) \
        do { \
diff --git a/compat/sha1-chunked.c b/compat/sha1-chunked.c
new file mode 100644 (file)
index 0000000..6adfcfd
--- /dev/null
@@ -0,0 +1,19 @@
+#include "cache.h"
+
+int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len)
+{
+       size_t nr;
+       size_t total = 0;
+       const char *cdata = (const char*)data;
+
+       while (len) {
+               nr = len;
+               if (nr > SHA1_MAX_BLOCK_SIZE)
+                       nr = SHA1_MAX_BLOCK_SIZE;
+               platform_SHA1_Update(c, cdata, nr);
+               total += nr;
+               cdata += nr;
+               len -= nr;
+       }
+       return total;
+}
diff --git a/compat/sha1-chunked.h b/compat/sha1-chunked.h
new file mode 100644 (file)
index 0000000..7b2df28
--- /dev/null
@@ -0,0 +1,2 @@
+
+int git_SHA1_Update_Chunked(platform_SHA_CTX *c, const void *data, size_t len);
index 248a21ab94116fabba95e01a8571a458efa99f76..86a5eb2571fb282a1d7bad9e6d097b2374b550dc 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2144,7 +2144,8 @@ int git_config_set_multivar_in_file(const char *config_filename,
        }
 
        if (commit_lock_file(lock) < 0) {
-               error("could not commit config file %s", config_filename);
+               error("could not write config file %s: %s", config_filename,
+                     strerror(errno));
                ret = CONFIG_NO_WRITE;
                lock = NULL;
                goto out_free;
@@ -2330,7 +2331,8 @@ int git_config_rename_section_in_file(const char *config_filename,
        fclose(config_file);
 unlock_and_out:
        if (commit_lock_file(lock) < 0)
-               ret = error("could not commit config file %s", config_filename);
+               ret = error("could not write config file %s: %s",
+                           config_filename, strerror(errno));
 out:
        free(filename_buf);
        return ret;
index 76170ad06d1e8a591af209b91c622d3073c691dd..89e2590bd6926a52d8f57e0dfc164360809c90d2 100644 (file)
@@ -1142,7 +1142,12 @@ elif test -z "$PTHREAD_CFLAGS"; then
   # would then trigger compiler warnings on every single file we compile.
   for opt in "" -mt -pthread -lpthread; do
      old_CFLAGS="$CFLAGS"
-     CFLAGS="$opt $CFLAGS"
+     old_LIBS="$LIBS"
+     case "$opt" in
+        -l*)  LIBS="$opt $LIBS" ;;
+        *)    CFLAGS="$opt $CFLAGS" ;;
+     esac
+
      AC_MSG_CHECKING([for POSIX Threads with '$opt'])
      AC_LINK_IFELSE([PTHREADTEST_SRC],
        [AC_MSG_RESULT([yes])
@@ -1154,6 +1159,7 @@ elif test -z "$PTHREAD_CFLAGS"; then
        ],
        [AC_MSG_RESULT([no])])
       CFLAGS="$old_CFLAGS"
+      LIBS="$old_LIBS"
   done
   if test $threads_found != yes; then
     AC_CHECK_LIB([pthread], [pthread_create],
index 108f5ab60e0ab122f0a86612ba9e4875ded29f26..fd7ffe1840e64417cf94d208840cf97c91e962b7 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -120,7 +120,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
        *list = NULL;
        for (;;) {
                struct ref *ref;
-               unsigned char old_sha1[20];
+               struct object_id old_oid;
                char *name;
                int len, name_len;
                char *buffer = packet_buffer;
@@ -139,34 +139,36 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                if (len > 4 && skip_prefix(buffer, "ERR ", &arg))
                        die("remote error: %s", arg);
 
-               if (len == 48 && skip_prefix(buffer, "shallow ", &arg)) {
-                       if (get_sha1_hex(arg, old_sha1))
+               if (len == GIT_SHA1_HEXSZ + strlen("shallow ") &&
+                       skip_prefix(buffer, "shallow ", &arg)) {
+                       if (get_oid_hex(arg, &old_oid))
                                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_sha1);
+                       sha1_array_append(shallow_points, old_oid.hash);
                        continue;
                }
 
-               if (len < 42 || get_sha1_hex(buffer, old_sha1) || buffer[40] != ' ')
+               if (len < GIT_SHA1_HEXSZ + 2 || get_oid_hex(buffer, &old_oid) ||
+                       buffer[GIT_SHA1_HEXSZ] != ' ')
                        die("protocol error: expected sha/ref, got '%s'", buffer);
-               name = buffer + 41;
+               name = buffer + GIT_SHA1_HEXSZ + 1;
 
                name_len = strlen(name);
-               if (len != name_len + 41) {
+               if (len != name_len + GIT_SHA1_HEXSZ + 1) {
                        free(server_capabilities);
                        server_capabilities = xstrdup(name + name_len + 1);
                }
 
                if (extra_have && !strcmp(name, ".have")) {
-                       sha1_array_append(extra_have, old_sha1);
+                       sha1_array_append(extra_have, old_oid.hash);
                        continue;
                }
 
                if (!check_ref(name, flags))
                        continue;
-               ref = alloc_ref(buffer + 41);
-               hashcpy(ref->old_sha1, old_sha1);
+               ref = alloc_ref(buffer + GIT_SHA1_HEXSZ + 1);
+               oidcpy(&ref->old_oid, &old_oid);
                *list = ref;
                list = &ref->next;
                got_at_least_one_head = 1;
index 07b52bedf183231b5708caf1ce255a115c9a980c..64219e631ab9babdc9eb78137478bde2c2cabec8 100644 (file)
@@ -476,10 +476,9 @@ __git_ps1 ()
                if [ -n "${GIT_PS1_SHOWDIRTYSTATE-}" ] &&
                   [ "$(git config --bool bash.showDirtyState)" != "false" ]
                then
-                       git diff --no-ext-diff --quiet --exit-code || w="*"
-                       if [ -n "$short_sha" ]; then
-                               git diff-index --cached --quiet HEAD -- || i="+"
-                       else
+                       git diff --no-ext-diff --quiet || w="*"
+                       git diff --no-ext-diff --cached --quiet || i="+"
+                       if [ -z "$short_sha" ] && [ -z "$i" ]; then
                                i="#"
                        fi
                fi
index 36b6feebe00060e4d522c506f12d2848673dbee6..52ad9e41fb7aba37abf71dad2bf5a0015b01dca7 100755 (executable)
@@ -7,7 +7,7 @@ USAGE="$me rev-list-args"
 
 SUBDIRECTORY_OK=Yes
 OPTIONS_SPEC=
-. $(git --exec-path)/git-sh-setup
+. "$(git --exec-path)/git-sh-setup"
 require_work_tree
 cd_to_toplevel
 
index 308b777b0aa43a7466fb3402cc67c1f023f57a7a..edf36f8c36091551d0a3e6cacb6c91446f0147eb 100755 (executable)
@@ -90,7 +90,7 @@ while [ $# -gt 0 ]; do
                --annotate) annotate="$1"; shift ;;
                --no-annotate) annotate= ;;
                -b) branch="$1"; shift ;;
-               -P) prefix="$1"; shift ;;
+               -P) prefix="${1%/}"; shift ;;
                -m) message="$1"; shift ;;
                --no-prefix) prefix= ;;
                --onto) onto="$1"; shift ;;
index c86481038937060d10b765ecd9eb03ae2bf32c33..276898eb6bd720ac63a87a0f3d7a2bebb542cb37 100644 (file)
@@ -13,11 +13,23 @@ TAR ?= $(TAR)
 RM ?= rm -f
 PROVE ?= prove
 DEFAULT_TEST_TARGET ?= test
+TEST_LINT ?= test-lint
+
+ifdef TEST_OUTPUT_DIRECTORY
+TEST_RESULTS_DIRECTORY = $(TEST_OUTPUT_DIRECTORY)/test-results
+else
+TEST_RESULTS_DIRECTORY = ../../../t/test-results
+endif
 
 # Shell quote;
 SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+PERL_PATH_SQ = $(subst ','\'',$(PERL_PATH))
+TEST_RESULTS_DIRECTORY_SQ = $(subst ','\'',$(TEST_RESULTS_DIRECTORY))
 
-T = $(wildcard t[0-9][0-9][0-9][0-9]-*.sh)
+T = $(sort $(wildcard t[0-9][0-9][0-9][0-9]-*.sh))
+TSVN = $(sort $(wildcard t91[0-9][0-9]-*.sh))
+TGITWEB = $(sort $(wildcard t95[0-9][0-9]-*.sh))
+THELPERS = $(sort $(filter-out $(T),$(wildcard *.sh)))
 
 all: $(DEFAULT_TEST_TARGET)
 
@@ -26,20 +38,22 @@ test: pre-clean $(TEST_LINT)
 
 prove: pre-clean $(TEST_LINT)
        @echo "*** prove ***"; GIT_CONFIG=.git/config $(PROVE) --exec '$(SHELL_PATH_SQ)' $(GIT_PROVE_OPTS) $(T) :: $(GIT_TEST_OPTS)
-       $(MAKE) clean
+       $(MAKE) clean-except-prove-cache
 
 $(T):
        @echo "*** $@ ***"; GIT_CONFIG=.git/config '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
 
 pre-clean:
-       $(RM) -r test-results
+       $(RM) -r '$(TEST_RESULTS_DIRECTORY_SQ)'
 
-clean:
-       $(RM) -r 'trash directory'.* test-results
+clean-except-prove-cache:
+       $(RM) -r 'trash directory'.* '$(TEST_RESULTS_DIRECTORY_SQ)'
        $(RM) -r valgrind/bin
+
+clean: clean-except-prove-cache
        $(RM) .prove
 
-test-lint: test-lint-duplicates test-lint-executable
+test-lint: test-lint-duplicates test-lint-executable test-lint-shell-syntax
 
 test-lint-duplicates:
        @dups=`echo $(T) | tr ' ' '\n' | sed 's/-.*//' | sort | uniq -d` && \
@@ -51,12 +65,15 @@ test-lint-executable:
                test -z "$$bad" || { \
                echo >&2 "non-executable tests:" $$bad; exit 1; }
 
+test-lint-shell-syntax:
+       @'$(PERL_PATH_SQ)' ../../../t/check-non-portable-shell.pl $(T) $(THELPERS)
+
 aggregate-results-and-cleanup: $(T)
        $(MAKE) aggregate-results
        $(MAKE) clean
 
 aggregate-results:
-       for f in ../../../t/test-results/t*-*.counts; do \
+       for f in '$(TEST_RESULTS_DIRECTORY_SQ)'/t*-*.counts; do \
                echo "$$f"; \
        done | '$(SHELL_PATH_SQ)' ../../../t/aggregate-results.sh
 
index dfbe443deaf1739e47d3f0f791331a3ee6773174..751aee3a0cd782c9eb1f98cb78f582011fb0b181 100755 (executable)
@@ -5,7 +5,7 @@
 #
 test_description='Basic porcelain support for subtrees
 
-This test verifies the basic operation of the merge, pull, add
+This test verifies the basic operation of the add, pull, merge
 and split subcommands of git subtree.
 '
 
@@ -14,13 +14,21 @@ export TEST_DIRECTORY
 
 . ../../../t/test-lib.sh
 
+subtree_test_create_repo()
+{
+       test_create_repo "$1"
+       (
+               cd $1
+               git config log.date relative
+       )
+}
+
 create()
 {
        echo "$1" >"$1"
        git add "$1"
 }
 
-
 check_equal()
 {
        test_debug 'echo'
@@ -38,484 +46,972 @@ undo()
        git reset --hard HEAD~
 }
 
-last_commit_message()
+# Make sure no patch changes more than one file.
+# The original set of commits changed only one file each.
+# A multi-file change would imply that we pruned commits
+# too aggressively.
+join_commits()
 {
-       git log --pretty=format:%s -1
+       commit=
+       all=
+       while read x y; do
+               if [ -z "$x" ]; then
+                       continue
+               elif [ "$x" = "commit:" ]; then
+                       if [ -n "$commit" ]; then
+                               echo "$commit $all"
+                               all=
+                       fi
+                       commit="$y"
+               else
+                       all="$all $y"
+               fi
+       done
+       echo "$commit $all"
 }
 
-test_expect_success 'init subproj' '
-       test_create_repo "sub proj"
-'
-
-# To the subproject!
-cd ./"sub proj"
-
-test_expect_success 'add sub1' '
-       create sub1 &&
-       git commit -m "sub1" &&
-       git branch sub1 &&
-       git branch -m master subproj
-'
-
-# Save this hash for testing later.
-
-subdir_hash=$(git rev-parse HEAD)
-
-test_expect_success 'add sub2' '
-       create sub2 &&
-       git commit -m "sub2" &&
-       git branch sub2
-'
-
-test_expect_success 'add sub3' '
-       create sub3 &&
-       git commit -m "sub3" &&
-       git branch sub3
-'
-
-# Back to mainline
-cd ..
-
-test_expect_success 'enable log.date=relative to catch errors' '
-       git config log.date relative
-'
-
-test_expect_success 'add main4' '
-       create main4 &&
-       git commit -m "main4" &&
-       git branch -m master mainline &&
-       git branch subdir
-'
-
-test_expect_success 'fetch subproj history' '
-       git fetch ./"sub proj" sub1 &&
-       git branch sub1 FETCH_HEAD
-'
-
-test_expect_success 'no subtree exists in main tree' '
-       test_must_fail git subtree merge --prefix="sub dir" sub1
-'
-
-test_expect_success 'no pull from non-existant subtree' '
-       test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" sub1
-'
-
-test_expect_success 'check if --message works for add' '
-       git subtree add --prefix="sub dir" --message="Added subproject" sub1 &&
-       check_equal ''"$(last_commit_message)"'' "Added subproject" &&
-       undo
-'
-
-test_expect_success 'check if --message works as -m and --prefix as -P' '
-       git subtree add -P "sub dir" -m "Added subproject using git subtree" sub1 &&
-       check_equal ''"$(last_commit_message)"'' "Added subproject using git subtree" &&
-       undo
-'
-
-test_expect_success 'check if --message works with squash too' '
-       git subtree add -P "sub dir" -m "Added subproject with squash" --squash sub1 &&
-       check_equal ''"$(last_commit_message)"'' "Added subproject with squash" &&
-       undo
-'
-
-test_expect_success 'add subproj to mainline' '
-       git subtree add --prefix="sub dir"/ FETCH_HEAD &&
-       check_equal ''"$(last_commit_message)"'' "Add '"'sub dir/'"' from commit '"'"'''"$(git rev-parse sub1)"'''"'"'"
-'
-
-# this shouldn't actually do anything, since FETCH_HEAD is already a parent
-test_expect_success 'merge fetched subproj' '
-       git merge -m "merge -s -ours" -s ours FETCH_HEAD
-'
-
-test_expect_success 'add main-sub5' '
-       create "sub dir/main-sub5" &&
-       git commit -m "main-sub5"
-'
-
-test_expect_success 'add main6' '
-       create main6 &&
-       git commit -m "main6 boring"
-'
-
-test_expect_success 'add main-sub7' '
-       create "sub dir/main-sub7" &&
-       git commit -m "main-sub7"
-'
-
-test_expect_success 'fetch new subproj history' '
-       git fetch ./"sub proj" sub2 &&
-       git branch sub2 FETCH_HEAD
-'
+test_create_commit() (
+       repo=$1
+       commit=$2
+       cd "$repo"
+       mkdir -p $(dirname "$commit") \
+       || error "Could not create directory for commit"
+       echo "$commit" >"$commit"
+       git add "$commit" || error "Could not add commit"
+       git commit -m "$commit" || error "Could not commit"
+)
 
-test_expect_success 'check if --message works for merge' '
-       git subtree merge --prefix="sub dir" -m "Merged changes from subproject" sub2 &&
-       check_equal ''"$(last_commit_message)"'' "Merged changes from subproject" &&
-       undo
-'
+last_commit_message()
+{
+       git log --pretty=format:%s -1
+}
 
-test_expect_success 'check if --message for merge works with squash too' '
-       git subtree merge --prefix "sub dir" -m "Merged changes from subproject using squash" --squash sub2 &&
-       check_equal ''"$(last_commit_message)"'' "Merged changes from subproject using squash" &&
-       undo
-'
+subtree_test_count=0
+next_test() {
+       subtree_test_count=$(($subtree_test_count+1))
+}
 
-test_expect_success 'merge new subproj history into subdir' '
-       git subtree merge --prefix="sub dir" FETCH_HEAD &&
-       git branch pre-split &&
-       check_equal ''"$(last_commit_message)"'' "Merge commit '"'"'"$(git rev-parse sub2)"'"'"' into mainline" &&
-       undo
-'
+#
+# Tests for 'git subtree add'
+#
 
-test_expect_success 'Check that prefix argument is required for split' '
-       echo "You must provide the --prefix option." > expected &&
-       test_must_fail git subtree split > actual 2>&1 &&
-       test_debug "printf '"'"'expected: '"'"'" &&
-       test_debug "cat expected" &&
-       test_debug "printf '"'"'actual: '"'"'" &&
-       test_debug "cat actual" &&
-       test_cmp expected actual &&
-       rm -f expected actual
+next_test
+test_expect_success 'no merge from non-existent subtree' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               test_must_fail git subtree merge --prefix="sub dir" FETCH_HEAD
+       )
+'
+
+next_test
+test_expect_success 'no pull from non-existent subtree' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               test_must_fail git subtree pull --prefix="sub dir" ./"sub proj" master
+       )'
+
+next_test
+test_expect_success 'add subproj as subtree into sub dir/ with --prefix' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Add '\''sub dir/'\'' from commit '\''$(git rev-parse FETCH_HEAD)'\''"
+       )
+'
+
+next_test
+test_expect_success 'add subproj as subtree into sub dir/ with --prefix and --message' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" --message="Added subproject" FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Added subproject"
+       )
+'
+
+next_test
+test_expect_success 'add subproj as subtree into sub dir/ with --prefix as -P and --message as -m' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add -P "sub dir" -m "Added subproject" FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Added subproject"
+       )
+'
+
+next_test
+test_expect_success 'add subproj as subtree into sub dir/ with --squash and --prefix and --message' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" --message="Added subproject with squash" --squash FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Added subproject with squash"
+       )
 '
 
-test_expect_success 'Check that the <prefix> exists for a split' '
-       echo "'"'"'non-existent-directory'"'"'" does not exist\; use "'"'"'git subtree add'"'"'" > expected &&
-       test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
-       test_debug "printf '"'"'expected: '"'"'" &&
-       test_debug "cat expected" &&
-       test_debug "printf '"'"'actual: '"'"'" &&
-       test_debug "cat actual" &&
-       test_cmp expected actual
-#      rm -f expected actual
-'
+#
+# Tests for 'git subtree merge'
+#
 
-test_expect_success 'check if --message works for split+rejoin' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-       git branch spl1 "$spl1" &&
-       check_equal ''"$(last_commit_message)"'' "Split & rejoin" &&
-       undo
+next_test
+test_expect_success 'merge new subproj history into sub dir/ with --prefix' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+       )
+'
+
+next_test
+test_expect_success 'merge new subproj history into sub dir/ with --prefix and --message' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" --message="Merged changes from subproject" FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Merged changes from subproject"
+       )
+'
+
+next_test
+test_expect_success 'merge new subproj history into sub dir/ with --squash and --prefix and --message' '
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       subtree_test_create_repo "$subtree_test_count" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" --message="Merged changes from subproject using squash" --squash FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Merged changes from subproject using squash"
+       )
+'
+
+next_test
+test_expect_success 'merge the added subproj again, should do nothing' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD &&
+               # this shouldn not actually do anything, since FETCH_HEAD
+               # is already a parent
+               result=$(git merge -s ours -m "merge -s -ours" FETCH_HEAD) &&
+               check_equal "${result}" "Already up-to-date."
+       )
+'
+
+next_test
+test_expect_success 'merge new subproj history into subdir/ with a slash appended to the argument of --prefix' '
+       test_create_repo "$test_count" &&
+       test_create_repo "$test_count/subproj" &&
+       test_create_commit "$test_count" main1 &&
+       test_create_commit "$test_count/subproj" sub1 &&
+       (
+               cd "$test_count" &&
+               git fetch ./subproj master &&
+               git subtree add --prefix=subdir/ FETCH_HEAD
+       ) &&
+       test_create_commit "$test_count/subproj" sub2 &&
+       (
+               cd "$test_count" &&
+               git fetch ./subproj master &&
+               git subtree merge --prefix=subdir/ FETCH_HEAD &&
+               check_equal "$(last_commit_message)" "Merge commit '\''$(git rev-parse FETCH_HEAD)'\''"
+       )
 '
 
-test_expect_success 'check split with --branch' '
-       spl1=$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin) &&
-       undo &&
-       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr1 &&
-       check_equal ''"$(git rev-parse splitbr1)"'' "$spl1"
-'
+#
+# Tests for 'git subtree split'
+#
 
+next_test
+test_expect_success 'split requires option --prefix' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD &&
+               echo "You must provide the --prefix option." > expected &&
+               test_must_fail git subtree split > actual 2>&1 &&
+               test_debug "printf '"expected: "'" &&
+               test_debug "cat expected" &&
+               test_debug "printf '"actual: "'" &&
+               test_debug "cat actual" &&
+               test_cmp expected actual
+       )
+'
+
+next_test
+test_expect_success 'split requires path given by option --prefix must exist' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD &&
+               echo "'\''non-existent-directory'\'' does not exist; use '\''git subtree add'\''" > expected &&
+               test_must_fail git subtree split --prefix=non-existent-directory > actual 2>&1 &&
+               test_debug "printf '"expected: "'" &&
+               test_debug "cat expected" &&
+               test_debug "printf '"actual: "'" &&
+               test_debug "cat actual" &&
+               test_cmp expected actual
+       )
+'
+
+next_test
+test_expect_success 'split sub dir/ with --rejoin' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
+               git subtree split --prefix="sub dir" --annotate="*" --rejoin &&
+               check_equal "$(last_commit_message)" "Split '\''sub dir/'\'' into commit '\''$split_hash'\''"
+       )
+ '
+
+next_test
+test_expect_success 'split sub dir/ with --rejoin and --message' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --message="Split & rejoin" --annotate="*" --rejoin &&
+               check_equal "$(last_commit_message)" "Split & rejoin"
+       )
+'
+
+next_test
+test_expect_success 'split "sub dir"/ with --branch' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
+               check_equal "$(git rev-parse subproj-br)" "$split_hash"
+       )
+'
+
+next_test
 test_expect_success 'check hash of split' '
-       spl1=$(git subtree split --prefix "sub dir") &&
-       git subtree split --prefix "sub dir" --branch splitbr1test &&
-       check_equal ''"$(git rev-parse splitbr1test)"'' "$spl1" &&
-       new_hash=$(git rev-parse splitbr1test~2) &&
-       check_equal ''"$new_hash"'' "$subdir_hash"
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
+               check_equal "$(git rev-parse subproj-br)" "$split_hash" &&
+               # Check hash of split
+               new_hash=$(git rev-parse subproj-br^2) &&
+               (
+                       cd ./"sub proj" &&
+                       subdir_hash=$(git rev-parse HEAD) &&
+                       check_equal ''"$new_hash"'' "$subdir_hash"
+               )
+       )
+'
+
+next_test
+test_expect_success 'split "sub dir"/ with --branch for an existing branch' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git branch subproj-br FETCH_HEAD &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               split_hash=$(git subtree split --prefix="sub dir" --annotate="*") &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br &&
+               check_equal "$(git rev-parse subproj-br)" "$split_hash"
+       )
+'
+
+next_test
+test_expect_success 'split "sub dir"/ with --branch for an incompatible branch' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git branch init HEAD &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               test_must_fail git subtree split --prefix="sub dir" --branch init
+       )
 '
 
-test_expect_success 'check split with --branch for an existing branch' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-       undo &&
-       git branch splitbr2 sub1 &&
-       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --branch splitbr2 &&
-       check_equal ''"$(git rev-parse splitbr2)"'' "$spl1"
-'
-
-test_expect_success 'check split with --branch for an incompatible branch' '
-       test_must_fail git subtree split --prefix "sub dir" --onto FETCH_HEAD --branch subdir
-'
-
-test_expect_success 'check split+rejoin' '
-       spl1=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --message "Split & rejoin" --rejoin)"'' &&
-       undo &&
-       git subtree split --annotate='"'*'"' --prefix "sub dir" --onto FETCH_HEAD --rejoin &&
-       check_equal ''"$(last_commit_message)"'' "Split '"'"'sub dir/'"'"' into commit '"'"'"$spl1"'"'"'"
-'
-
-test_expect_success 'add main-sub8' '
-       create "sub dir/main-sub8" &&
-       git commit -m "main-sub8"
-'
-
-# To the subproject!
-cd ./"sub proj"
-
-test_expect_success 'merge split into subproj' '
-       git fetch .. spl1 &&
-       git branch spl1 FETCH_HEAD &&
-       git merge FETCH_HEAD
-'
-
-test_expect_success 'add sub9' '
-       create sub9 &&
-       git commit -m "sub9"
-'
-
-# Back to mainline
-cd ..
-
-test_expect_success 'split for sub8' '
-       split2=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir/" --rejoin)"'' &&
-       git branch split2 "$split2"
-'
-
-test_expect_success 'add main-sub10' '
-       create "sub dir/main-sub10" &&
-       git commit -m "main-sub10"
-'
-
-test_expect_success 'split for sub10' '
-       spl3=''"$(git subtree split --annotate='"'*'"' --prefix "sub dir" --rejoin)"'' &&
-       git branch spl3 "$spl3"
-'
-
-# To the subproject!
-cd ./"sub proj"
-
-test_expect_success 'merge split into subproj' '
-       git fetch .. spl3 &&
-       git branch spl3 FETCH_HEAD &&
-       git merge FETCH_HEAD &&
-       git branch subproj-merge-spl3
-'
+#
+# Validity checking
+#
 
-chkm="main4
-main6"
-chkms="main-sub10
-main-sub5
-main-sub7
-main-sub8"
-chkms_sub=$(cat <<TXT | sed 's,^,sub dir/,'
-$chkms
-TXT
-)
-chks="sub1
+next_test
+test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD &&
+
+               chks="sub1
 sub2
 sub3
-sub9"
-chks_sub=$(cat <<TXT | sed 's,^,sub dir/,'
+sub4" &&
+               chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
 $chks
 TXT
-)
+) &&
+               chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+               chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chkms
+TXT
+) &&
 
-test_expect_success 'make sure exactly the right set of files ends up in the subproj' '
-       subfiles="$(git ls-files)" &&
-       check_equal "$subfiles" "$chkms
+               subfiles=$(git ls-files) &&
+               check_equal "$subfiles" "$chkms
 $chks"
-'
-test_expect_success 'make sure the subproj history *only* contains commits that affect the subdir' '
-       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
-       check_equal "$allchanges" "$chkms
+       )
+'
+
+next_test
+test_expect_success 'make sure the subproj *only* contains commits that affect the "sub dir"' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD &&
+
+               chks="sub1
+sub2
+sub3
+sub4" &&
+               chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chks
+TXT
+) &&
+               chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+               chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chkms
+TXT
+) &&
+               allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") &&
+               check_equal "$allchanges" "$chkms
 $chks"
+       )
 '
 
-# Back to mainline
-cd ..
-
-test_expect_success 'pull from subproj' '
-       git fetch ./"sub proj" subproj-merge-spl3 &&
-       git branch subproj-merge-spl3 FETCH_HEAD &&
-       git subtree pull --prefix="sub dir" ./"sub proj" subproj-merge-spl3
-'
-
+next_test
 test_expect_success 'make sure exactly the right set of files ends up in the mainline' '
-       mainfiles=$(git ls-files) &&
-       check_equal "$mainfiles" "$chkm
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree pull --prefix="sub dir" ./"sub proj" master &&
+
+               chkm="main1
+main2" &&
+               chks="sub1
+sub2
+sub3
+sub4" &&
+               chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chks
+TXT
+) &&
+               chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+               chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chkms
+TXT
+) &&
+               mainfiles=$(git ls-files) &&
+               check_equal "$mainfiles" "$chkm
 $chkms_sub
 $chks_sub"
+)
 '
 
+next_test
 test_expect_success 'make sure each filename changed exactly once in the entire history' '
-       # main-sub?? and /subdir/main-sub?? both change, because those are the
-       # changes that were split into their own history.  And subdir/sub?? never
-       # change, since they were *only* changed in the subtree branch.
-       allchanges=''"$(git log --name-only --pretty=format:'"''"' | sort | sed "/^$/d")"'' &&
-       check_equal "$allchanges" ''"$(cat <<TXT | sort
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git config log.date relative
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree pull --prefix="sub dir" ./"sub proj" master &&
+
+               chkm="main1
+main2" &&
+               chks="sub1
+sub2
+sub3
+sub4" &&
+               chks_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chks
+TXT
+) &&
+               chkms="main-sub1
+main-sub2
+main-sub3
+main-sub4" &&
+               chkms_sub=$(cat <<TXT | sed '\''s,^,sub dir/,'\''
+$chkms
+TXT
+) &&
+
+               # main-sub?? and /"sub dir"/main-sub?? both change, because those are the
+               # changes that were split into their own history.  And "sub dir"/sub?? never
+               # change, since they were *only* changed in the subtree branch.
+               allchanges=$(git log --name-only --pretty=format:"" | sort | sed "/^$/d") &&
+               expected=''"$(cat <<TXT | sort
 $chkms
 $chkm
 $chks
 $chkms_sub
 TXT
-)"''
+)"'' &&
+               check_equal "$allchanges" "$expected"
+       )
 '
 
+next_test
 test_expect_success 'make sure the --rejoin commits never make it into subproj' '
-       check_equal ''"$(git log --pretty=format:'"'%s'"' HEAD^2 | grep -i split)"'' ""
-'
-
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree pull --prefix="sub dir" ./"sub proj" master &&
+               check_equal "$(git log --pretty=format:"%s" HEAD^2 | grep -i split)" ""
+       )
+'
+
+next_test
 test_expect_success 'make sure no "git subtree" tagged commits make it into subproj' '
-       # They are meaningless to subproj since one side of the merge refers to the mainline
-       check_equal ''"$(git log --pretty=format:'"'%s%n%b'"' HEAD^2 | grep "git-subtree.*:")"'' ""
-'
-
-# prepare second pair of repositories
-mkdir test2
-cd test2
-
-test_expect_success 'init main' '
-       test_create_repo main
-'
-
-cd main
-
-test_expect_success 'add main1' '
-       create main1 &&
-       git commit -m "main1"
-'
-
-cd ..
-
-test_expect_success 'init sub' '
-       test_create_repo sub
-'
-
-cd sub
-
-test_expect_success 'add sub2' '
-       create sub2 &&
-       git commit -m "sub2"
-'
-
-cd ../main
-
-# check if split can find proper base without --onto
-
-test_expect_success 'add sub as subdir in main' '
-       git fetch ../sub master &&
-       git branch sub2 FETCH_HEAD &&
-       git subtree add --prefix "sub dir" sub2
-'
-
-cd ../sub
-
-test_expect_success 'add sub3' '
-       create sub3 &&
-       git commit -m "sub3"
-'
-
-cd ../main
-
-test_expect_success 'merge from sub' '
-       git fetch ../sub master &&
-       git branch sub3 FETCH_HEAD &&
-       git subtree merge --prefix "sub dir" sub3
-'
-
-test_expect_success 'add main-sub4' '
-       create "sub dir/main-sub4" &&
-       git commit -m "main-sub4"
-'
-
-test_expect_success 'split for main-sub4 without --onto' '
-       git subtree split --prefix "sub dir" --branch mainsub4
-'
-
-# at this point, the new commit parent should be sub3 if it is not,
-# something went wrong (the "newparent" of "master~" commit should
-# have been sub3, but it was not, because its cache was not set to
-# itself)
-
-test_expect_success 'check that the commit parent is sub3' '
-       check_equal ''"$(git log --pretty=format:%P -1 mainsub4)"'' ''"$(git rev-parse sub3)"''
-'
-
-test_expect_success 'add main-sub5' '
-       mkdir subdir2 &&
-       create subdir2/main-sub5 &&
-       git commit -m "main-sub5"
-'
-
-test_expect_success 'split for main-sub5 without --onto' '
-       # also test that we still can split out an entirely new subtree
-       # if the parent of the first commit in the tree is not empty,
-       # then the new subtree has accidentally been attached to something
-       git subtree split --prefix subdir2 --branch mainsub5 &&
-       check_equal ''"$(git log --pretty=format:%P -1 mainsub5)"'' ""
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub3 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+                git merge FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub4 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --annotate="*" --branch subproj-br --rejoin
+       ) &&
+       (
+               cd "$subtree_test_count/sub proj" &&
+               git fetch .. subproj-br &&
+               git merge FETCH_HEAD
+       ) &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree pull --prefix="sub dir" ./"sub proj" master &&
+
+               # They are meaningless to subproj since one side of the merge refers to the mainline
+               check_equal "$(git log --pretty=format:"%s%n%b" HEAD^2 | grep "git-subtree.*:")" ""
+       )
 '
 
-# make sure no patch changes more than one file.  The original set of commits
-# changed only one file each.  A multi-file change would imply that we pruned
-# commits too aggressively.
-joincommits()
-{
-       commit=
-       all=
-       while read x y; do
-               #echo "{$x}" >&2
-               if [ -z "$x" ]; then
-                       continue
-               elif [ "$x" = "commit:" ]; then
-                       if [ -n "$commit" ]; then
-                               echo "$commit $all"
-                               all=
-                       fi
-                       commit="$y"
-               else
-                       all="$all $y"
-               fi
-       done
-       echo "$commit $all"
-}
+#
+# A new set of tests
+#
 
+next_test
+test_expect_success 'make sure "git subtree split" find the correct parent' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git branch subproj-ref FETCH_HEAD &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --branch subproj-br &&
+
+               # at this point, the new commit parent should be subproj-ref, if it is
+               # not, something went wrong (the "newparent" of "master~" commit should
+               # have been sub2, but it was not, because its cache was not set to
+               # itself)
+               check_equal "$(git log --pretty=format:%P -1 subproj-br)" "$(git rev-parse subproj-ref)"
+       )
+'
+
+next_test
+test_expect_success 'split a new subtree without --onto option' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --branch subproj-br
+       ) &&
+       mkdir "$subtree_test_count"/"sub dir2" &&
+       test_create_commit "$subtree_test_count" "sub dir2"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+
+               # also test that we still can split out an entirely new subtree
+               # if the parent of the first commit in the tree is not empty,
+               # then the new subtree has accidently been attached to something
+               git subtree split --prefix="sub dir2" --branch subproj2-br &&
+               check_equal "$(git log --pretty=format:%P -1 subproj2-br)" ""
+       )
+'
+
+next_test
 test_expect_success 'verify one file change per commit' '
-       x= &&
-       list=''"$(git log --pretty=format:'"'commit: %H'"' | joincommits)"'' &&
-#      test_debug "echo HERE" &&
-#      test_debug "echo ''"$list"''" &&
-       (git log --pretty=format:'"'commit: %H'"' | joincommits |
-       (       while read commit a b; do
-                       test_debug "echo Verifying commit "''"$commit"''
-                       test_debug "echo a: "''"$a"''
-                       test_debug "echo b: "''"$b"''
-                       check_equal "$b" ""
-                       x=1
-               done
-               check_equal "$x" 1
-       ))
-'
-
-# test push
-
-cd ../..
-
-mkdir test-push
-
-cd test-push
-
-test_expect_success 'init main' '
-       test_create_repo main
-'
-
-test_expect_success 'init sub' '
-       test_create_repo "sub project"
-'
-
-cd ./"sub project"
-
-test_expect_success 'add subproject' '
-       create "sub project" &&
-       git commit -m "Sub project: 1" &&
-       git branch sub-branch-1
-'
-
-cd ../main
-
-test_expect_success 'make first commit and add subproject' '
-       create "main-1" &&
-       git commit -m "main: 1" &&
-       git subtree add "../sub project" --prefix "sub dir" --message "Added subproject" sub-branch-1 &&
-       check_equal "$(last_commit_message)" "Added subproject"
-'
-
-test_expect_success 'make second commit to a subproject file and push it into a sub project' '
-       create "sub dir/sub1" &&
-       git commit -m "Sub project: 2" &&
-       git subtree push "../sub project" --prefix "sub dir" sub-branch-1
-'
-
-cd ../"sub project"
-
-test_expect_success 'Test second commit is pushed' '
-       git checkout sub-branch-1 &&
-       check_equal "$(last_commit_message)" "Sub project: 2"
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git branch sub1 FETCH_HEAD &&
+               git subtree add --prefix="sub dir" sub1
+       ) &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir" --branch subproj-br
+       ) &&
+       mkdir "$subtree_test_count"/"sub dir2" &&
+       test_create_commit "$subtree_test_count" "sub dir2"/main-sub2 &&
+       (
+               cd "$subtree_test_count" &&
+               git subtree split --prefix="sub dir2" --branch subproj2-br &&
+
+               x= &&
+               git log --pretty=format:"commit: %H" | join_commits |
+               (
+                       while read commit a b; do
+                               test_debug "echo Verifying commit $commit"
+                               test_debug "echo a: $a"
+                               test_debug "echo b: $b"
+                               check_equal "$b" ""
+                               x=1
+                       done
+                       check_equal "$x" 1
+               )
+       )
+'
+
+next_test
+test_expect_success 'push split to subproj' '
+       subtree_test_create_repo "$subtree_test_count" &&
+       subtree_test_create_repo "$subtree_test_count/sub proj" &&
+       test_create_commit "$subtree_test_count" main1 &&
+       test_create_commit "$subtree_test_count/sub proj" sub1 &&
+       (
+               cd "$subtree_test_count" &&
+               git fetch ./"sub proj" master &&
+               git subtree add --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub1 &&
+       test_create_commit "$subtree_test_count" main2 &&
+       test_create_commit "$subtree_test_count/sub proj" sub2 &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub2 &&
+       (
+               cd $subtree_test_count/"sub proj" &&
+                git branch sub-branch-1 &&
+                cd .. &&
+               git fetch ./"sub proj" master &&
+               git subtree merge --prefix="sub dir" FETCH_HEAD
+       ) &&
+       test_create_commit "$subtree_test_count" "sub dir"/main-sub3 &&
+        (
+               cd "$subtree_test_count" &&
+               git subtree push ./"sub proj" --prefix "sub dir" sub-branch-1 &&
+                cd ./"sub proj" &&
+                git checkout sub-branch-1 &&
+               check_equal "$(last_commit_message)" "sub dir/main-sub3"
+       )
 '
 
 test_done
index 7e44b0024fa35ae76f62875704fcd24e3f690c7d..0d0e77765175489a92719bb7f2f9f3c5aa1db8c0 100644 (file)
@@ -12,8 +12,6 @@
                exactly the right subtree structure, rather than using
                subtree merge...)
 
-       add a 'push' subcommand to parallel 'pull'
-       
        add a 'log' subcommand to see what's new in a subtree?
 
        add to-submodule and from-submodule commands
index 82715aa8b8c050591559a1c4f9f92b2e99602212..9365f2ce5c188d86b1b60029e4549e1bd844214f 100644 (file)
@@ -244,6 +244,7 @@ static void check_socket_directory(const char *path)
 int main(int argc, const char **argv)
 {
        const char *socket_path;
+       int ignore_sighup = 0;
        static const char *usage[] = {
                "git-credential-cache--daemon [opts] <socket_path>",
                NULL
@@ -255,6 +256,8 @@ int main(int argc, const char **argv)
                OPT_END()
        };
 
+       git_config_get_bool("credentialcache.ignoresighup", &ignore_sighup);
+
        argc = parse_options(argc, argv, NULL, options, usage, 0);
        socket_path = argv[0];
 
@@ -263,6 +266,10 @@ int main(int argc, const char **argv)
 
        check_socket_directory(socket_path);
        register_tempfile(&socket_file, socket_path);
+
+       if (ignore_sighup)
+               signal(SIGHUP, SIG_IGN);
+
        serve_cache(socket_path, debug);
        delete_tempfile(&socket_file);
 
index 00aea3aa304f2731da070c447f694591b3021bc9..fc67d16c10882bf543bd521d2f6dfee8d3c4d213 100644 (file)
@@ -64,7 +64,8 @@ static void rewrite_credential_file(const char *fn, struct credential *c,
                print_line(extra);
        parse_credential_file(fn, c, NULL, print_line);
        if (commit_lock_file(&credential_lock) < 0)
-               die_errno("unable to commit credential store");
+               die_errno("unable to write credential store: %s",
+                         strerror(errno));
 }
 
 static void store_credential_file(const char *fn, struct credential *c)
index b2aac90c26296eacdd6c2cdb68a15f3744c595e9..270eb2519764f1378fe53352021cd7afb3ea306a 100644 (file)
@@ -8,7 +8,7 @@
 
 static unsigned int hash_obj(const struct object *obj, unsigned int n)
 {
-       return sha1hash(obj->sha1) % n;
+       return sha1hash(obj->oid.hash) % n;
 }
 
 static void *insert_decoration(struct decoration *n, const struct object *base, void *decoration)
index 241a8435eb1f7ddb88664343a5d9c42a1951886f..bc49c708c1854a0f67c25e4fd2b77a9ad50ff511 100644 (file)
@@ -493,7 +493,7 @@ int run_diff_index(struct rev_info *revs, int cached)
        struct object_array_entry *ent;
 
        ent = revs->pending.objects;
-       if (diff_cache(revs, ent->item->sha1, ent->name, cached))
+       if (diff_cache(revs, ent->item->oid.hash, ent->name, cached))
                exit(128);
 
        diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/");
index e3b421d5149bc3a2ad58ca8df98fdf2c924ed7c0..3c65edb5c44e1fa57beef851d56a9674a5be1b72 100644 (file)
@@ -1824,7 +1824,7 @@ static void dump_marks(void)
 
        dump_marks_helper(f, 0, marks);
        if (commit_lock_file(&mark_lock)) {
-               failure |= error("Unable to commit marks file %s: %s",
+               failure |= error("Unable to write file %s: %s",
                        export_marks_file, strerror(errno));
                return;
        }
index 2dabee97b28915ccf5c06b58dd0193bf03a01db8..01e34b689b975799610eabc15fc9138990706ff8 100644 (file)
@@ -169,7 +169,7 @@ static const unsigned char *get_rev(void)
                }
        }
 
-       return commit->object.sha1;
+       return commit->object.oid.hash;
 }
 
 enum ack_type {
@@ -238,7 +238,7 @@ static void send_request(struct fetch_pack_args *args,
 
 static void insert_one_alternate_ref(const struct ref *ref, void *unused)
 {
-       rev_list_insert_ref(NULL, ref->old_sha1);
+       rev_list_insert_ref(NULL, ref->old_oid.hash);
 }
 
 #define INITIAL_FLUSH 16
@@ -280,7 +280,7 @@ static int find_common(struct fetch_pack_args *args,
 
        fetching = 0;
        for ( ; refs ; refs = refs->next) {
-               unsigned char *remote = refs->old_sha1;
+               unsigned char *remote = refs->old_oid.hash;
                const char *remote_hex;
                struct object *o;
 
@@ -487,7 +487,7 @@ static int mark_complete(const unsigned char *sha1)
                if (!t->tagged)
                        break; /* broken repository */
                o->flags |= COMPLETE;
-               o = parse_object(t->tagged->sha1);
+               o = parse_object(t->tagged->oid.hash);
        }
        if (o && o->type == OBJ_COMMIT) {
                struct commit *commit = (struct commit *)o;
@@ -511,7 +511,7 @@ static void mark_recent_complete_commits(struct fetch_pack_args *args,
        while (complete && cutoff <= complete->item->date) {
                if (args->verbose)
                        fprintf(stderr, "Marking %s as complete\n",
-                               sha1_to_hex(complete->item->object.sha1));
+                               oid_to_hex(&complete->item->object.oid));
                pop_most_recent_commit(&complete, COMPLETE);
        }
 }
@@ -570,7 +570,7 @@ static void filter_refs(struct fetch_pack_args *args,
                                continue;
                        if (get_sha1_hex(ref->name, sha1) ||
                            ref->name[40] != '\0' ||
-                           hashcmp(sha1, ref->old_sha1))
+                           hashcmp(sha1, ref->old_oid.hash))
                                continue;
 
                        ref->matched = 1;
@@ -583,7 +583,7 @@ static void filter_refs(struct fetch_pack_args *args,
 
 static void mark_alternate_complete(const struct ref *ref, void *unused)
 {
-       mark_complete(ref->old_sha1);
+       mark_complete(ref->old_oid.hash);
 }
 
 static int everything_local(struct fetch_pack_args *args,
@@ -599,10 +599,10 @@ static int everything_local(struct fetch_pack_args *args,
        for (ref = *refs; ref; ref = ref->next) {
                struct object *o;
 
-               if (!has_sha1_file(ref->old_sha1))
+               if (!has_object_file(&ref->old_oid))
                        continue;
 
-               o = parse_object(ref->old_sha1);
+               o = parse_object(ref->old_oid.hash);
                if (!o)
                        continue;
 
@@ -630,7 +630,7 @@ static int everything_local(struct fetch_pack_args *args,
         * Don't mark them common yet; the server has to be told so first.
         */
        for (ref = *refs; ref; ref = ref->next) {
-               struct object *o = deref_tag(lookup_object(ref->old_sha1),
+               struct object *o = deref_tag(lookup_object(ref->old_oid.hash),
                                             NULL, 0);
 
                if (!o || o->type != OBJ_COMMIT || !(o->flags & COMPLETE))
@@ -646,7 +646,7 @@ static int everything_local(struct fetch_pack_args *args,
        filter_refs(args, refs, sought, nr_sought);
 
        for (retval = 1, ref = *refs; ref ; ref = ref->next) {
-               const unsigned char *remote = ref->old_sha1;
+               const unsigned char *remote = ref->old_oid.hash;
                struct object *o;
 
                o = lookup_object(remote);
@@ -985,7 +985,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_sha1);
+               sha1_array_append(&ref, sought[i]->old_oid.hash);
        si->ref = &ref;
 
        if (args->update_shallow) {
diff --git a/fsck.c b/fsck.c
index e41e753d6dcbb577148260a1392210d50fb0b325..c637f6676b639beb90240e4667ed07ceb83cdbf8 100644 (file)
--- a/fsck.c
+++ b/fsck.c
@@ -276,7 +276,7 @@ static int report(struct fsck_options *options, struct object *object,
                return 0;
 
        if (options->skiplist && object &&
-                       sha1_array_lookup(options->skiplist, object->sha1) >= 0)
+                       sha1_array_lookup(options->skiplist, object->oid.hash) >= 0)
                return 0;
 
        if (msg_type == FSCK_FATAL)
@@ -316,7 +316,7 @@ static int fsck_walk_tree(struct tree *tree, void *data, struct fsck_options *op
                        result = options->walk(&lookup_blob(entry.sha1)->object, OBJ_BLOB, data, options);
                else {
                        result = error("in tree %s: entry %s has bad mode %.6o",
-                                       sha1_to_hex(tree->object.sha1), entry.path, entry.mode);
+                                       oid_to_hex(&tree->object.oid), entry.path, entry.mode);
                }
                if (result < 0)
                        return result;
@@ -373,7 +373,7 @@ int fsck_walk(struct object *obj, void *data, struct fsck_options *options)
        case OBJ_TAG:
                return fsck_walk_tag((struct tag *)obj, data, options);
        default:
-               error("Unknown object type for %s", sha1_to_hex(obj->sha1));
+               error("Unknown object type for %s", oid_to_hex(&obj->oid));
                return -1;
        }
 }
@@ -630,7 +630,7 @@ static int fsck_commit_buffer(struct commit *commit, const char *buffer,
                buffer += 41;
                parent_line_count++;
        }
-       graft = lookup_commit_graft(commit->object.sha1);
+       graft = lookup_commit_graft(commit->object.oid.hash);
        parent_count = commit_list_count(commit->parents);
        if (graft) {
                if (graft->nr_parent == -1 && !parent_count)
@@ -696,7 +696,7 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
                enum object_type type;
 
                buffer = to_free =
-                       read_sha1_file(tag->object.sha1, &type, &size);
+                       read_sha1_file(tag->object.oid.hash, &type, &size);
                if (!buffer)
                        return report(options, &tag->object,
                                FSCK_MSG_MISSING_TAG_OBJECT,
@@ -711,7 +711,8 @@ static int fsck_tag_buffer(struct tag *tag, const char *data,
                }
        }
 
-       if (verify_headers(buffer, size, &tag->object, options))
+       ret = verify_headers(buffer, size, &tag->object, options);
+       if (ret)
                goto done;
 
        if (!skip_prefix(buffer, "object ", &buffer)) {
@@ -809,9 +810,9 @@ int fsck_object(struct object *obj, void *data, unsigned long size,
 int fsck_error_function(struct object *obj, int msg_type, const char *message)
 {
        if (msg_type == FSCK_WARN) {
-               warning("object %s: %s", sha1_to_hex(obj->sha1), message);
+               warning("object %s: %s", oid_to_hex(&obj->oid), message);
                return 0;
        }
-       error("object %s: %s", sha1_to_hex(obj->sha1), message);
+       error("object %s: %s", oid_to_hex(&obj->oid), message);
        return 1;
 }
index 27c9c54fbd24ef3a2995fd90b04911e5a1d8aed9..98f1779cf3241fa12300fbf3dc0c4eff8ab10b61 100755 (executable)
@@ -306,6 +306,15 @@ then
        start_timestamp=$(date '+%s')
 fi
 
+if test -n "$filter_index" ||
+   test -n "$filter_tree" ||
+   test -n "$filter_subdir"
+then
+       need_index=t
+else
+       need_index=
+fi
+
 while read commit parents; do
        git_filter_branch__commit_count=$(($git_filter_branch__commit_count+1))
 
@@ -313,7 +322,10 @@ while read commit parents; do
 
        case "$filter_subdir" in
        "")
-               GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
+               if test -n "$need_index"
+               then
+                       GIT_ALLOW_NULL_SHA1=1 git read-tree -i -m $commit
+               fi
                ;;
        *)
                # The commit may not have the subdirectory at all
@@ -349,7 +361,7 @@ while read commit parents; do
                        die "tree filter failed: $filter_tree"
 
                (
-                       git diff-index -r --name-only --ignore-submodules $commit &&
+                       git diff-index -r --name-only --ignore-submodules $commit -- &&
                        git ls-files --others
                ) > "$tempdir"/tree-state || exit
                git update-index --add --replace --remove --stdin \
@@ -387,8 +399,15 @@ while read commit parents; do
        } <../commit |
                eval "$filter_msg" > ../message ||
                        die "msg filter failed: $filter_msg"
+
+       if test -n "$need_index"
+       then
+               tree=$(git write-tree)
+       else
+               tree="$commit^{tree}"
+       fi
        workdir=$workdir @SHELL_PATH@ -c "$filter_commit" "git commit-tree" \
-               $(git write-tree) $parentstr < ../message > ../map/$commit ||
+               "$tree" $parentstr < ../message > ../map/$commit ||
                        die "could not write rewritten commit"
 done <../revs
 
index 212ef2be9670bc2fe5573eee856471113eb96889..7a9dd6ad74580f648813a97c5012e6ef4f837896 100755 (executable)
--- a/git-p4.py
+++ b/git-p4.py
@@ -203,14 +203,16 @@ def p4_has_move_command():
     # assume it failed because @... was invalid changelist
     return True
 
-def system(cmd):
+def system(cmd, ignore_error=False):
     expand = isinstance(cmd,basestring)
     if verbose:
         sys.stderr.write("executing %s\n" % str(cmd))
     retcode = subprocess.call(cmd, shell=expand)
-    if retcode:
+    if retcode and not ignore_error:
         raise CalledProcessError(retcode, cmd)
 
+    return retcode
+
 def p4_system(cmd):
     """Specifically invoke p4 as the system command. """
     real_cmd = p4_build_cmd(cmd)
@@ -553,7 +555,12 @@ def p4Where(depotPath):
     return clientPath
 
 def currentGitBranch():
-    return read_pipe("git name-rev HEAD").split(" ")[1].strip()
+    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()
 
 def isValidGitDir(path):
     if (os.path.exists(path + "/HEAD")
@@ -1741,44 +1748,47 @@ def applyCommit(self, id):
         #
         # Let the user edit the change description, then submit it.
         #
-        if self.edit_template(fileName):
-            # read the edited message and submit
-            ret = True
-            tmpFile = open(fileName, "rb")
-            message = tmpFile.read()
-            tmpFile.close()
-            if self.isWindows:
-                message = message.replace("\r\n", "\n")
-            submitTemplate = message[:message.index(separatorLine)]
-            p4_write_pipe(['submit', '-i'], submitTemplate)
-
-            if self.preserveUser:
-                if p4User:
-                    # Get last changelist number. Cannot easily get it from
-                    # the submit command output as the output is
-                    # unmarshalled.
-                    changelist = self.lastP4Changelist()
-                    self.modifyChangelistUser(changelist, p4User)
-
-            # The rename/copy happened by applying a patch that created a
-            # new file.  This leaves it writable, which confuses p4.
-            for f in pureRenameCopy:
-                p4_sync(f, "-f")
+        submitted = False
 
-        else:
+        try:
+            if self.edit_template(fileName):
+                # read the edited message and submit
+                tmpFile = open(fileName, "rb")
+                message = tmpFile.read()
+                tmpFile.close()
+                if self.isWindows:
+                    message = message.replace("\r\n", "\n")
+                submitTemplate = message[:message.index(separatorLine)]
+                p4_write_pipe(['submit', '-i'], submitTemplate)
+
+                if self.preserveUser:
+                    if p4User:
+                        # Get last changelist number. Cannot easily get it from
+                        # the submit command output as the output is
+                        # unmarshalled.
+                        changelist = self.lastP4Changelist()
+                        self.modifyChangelistUser(changelist, p4User)
+
+                # The rename/copy happened by applying a patch that created a
+                # new file.  This leaves it writable, which confuses p4.
+                for f in pureRenameCopy:
+                    p4_sync(f, "-f")
+                submitted = True
+
+        finally:
             # skip this patch
-            ret = False
-            print "Submission cancelled, undoing p4 changes."
-            for f in editedFiles:
-                p4_revert(f)
-            for f in filesToAdd:
-                p4_revert(f)
-                os.remove(f)
-            for f in filesToDelete:
-                p4_revert(f)
+            if not submitted:
+                print "Submission cancelled, undoing p4 changes."
+                for f in editedFiles:
+                    p4_revert(f)
+                for f in filesToAdd:
+                    p4_revert(f)
+                    os.remove(f)
+                for f in filesToDelete:
+                    p4_revert(f)
 
         os.remove(fileName)
-        return ret
+        return submitted
 
     # Export git tags as p4 labels. Create a p4 label and then tag
     # with that.
@@ -1854,8 +1864,6 @@ def exportGitTags(self, gitTags):
     def run(self, args):
         if len(args) == 0:
             self.master = currentGitBranch()
-            if len(self.master) == 0 or not gitBranchExists("refs/heads/%s" % self.master):
-                die("Detecting current git branch failed!")
         elif len(args) == 1:
             self.master = args[0]
             if not branchExists(self.master):
@@ -1863,9 +1871,10 @@ def run(self, args):
         else:
             return False
 
-        allowSubmit = gitConfig("git-p4.allowSubmit")
-        if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
-            die("%s is not in git-p4.allowSubmit" % self.master)
+        if self.master:
+            allowSubmit = gitConfig("git-p4.allowSubmit")
+            if len(allowSubmit) > 0 and not self.master in allowSubmit.split(","):
+                die("%s is not in git-p4.allowSubmit" % self.master)
 
         [upstream, settings] = findUpstreamBranchPoint()
         self.depotPath = settings['depot-paths'][0]
@@ -1933,7 +1942,12 @@ def run(self, args):
         self.check()
 
         commits = []
-        for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, self.master)]):
+        if self.master:
+            commitish = self.master
+        else:
+            commitish = 'HEAD'
+
+        for line in read_pipe_lines(["git", "rev-list", "--no-merges", "%s..%s" % (self.origin, commitish)]):
             commits.append(line.strip())
         commits.reverse()
 
@@ -2542,12 +2556,6 @@ def streamP4Files(self, files):
         filesToDelete = []
 
         for f in files:
-            # if using a client spec, only add the files that have
-            # a path in the client
-            if self.clientSpecDirs:
-                if self.clientSpecDirs.map_in_client(f['path']) == "":
-                    continue
-
             filesForCommit.append(f)
             if f['action'] in self.delete_actions:
                 filesToDelete.append(f)
@@ -2618,25 +2626,41 @@ def streamTag(self, gitStream, labelName, labelDetails, commit, epoch):
         gitStream.write(description)
         gitStream.write("\n")
 
+    def inClientSpec(self, path):
+        if not self.clientSpecDirs:
+            return True
+        inClientSpec = self.clientSpecDirs.map_in_client(path)
+        if not inClientSpec and self.verbose:
+            print('Ignoring file outside of client spec: {0}'.format(path))
+        return inClientSpec
+
+    def hasBranchPrefix(self, path):
+        if not self.branchPrefixes:
+            return True
+        hasPrefix = [p for p in self.branchPrefixes
+                        if p4PathStartsWith(path, p)]
+        if hasPrefix and self.verbose:
+            print('Ignoring file outside of prefix: {0}'.format(path))
+        return hasPrefix
+
     def commit(self, details, files, branch, parent = ""):
         epoch = details["time"]
         author = details["user"]
 
         if self.verbose:
-            print "commit into %s" % branch
-
-        # start with reading files; if that fails, we should not
-        # create a commit.
-        new_files = []
-        for f in files:
-            if [p for p in self.branchPrefixes if p4PathStartsWith(f['path'], p)]:
-                new_files.append (f)
-            else:
-                sys.stderr.write("Ignoring file outside of prefix: %s\n" % f['path'])
+            print('commit into {0}'.format(branch))
 
         if self.clientSpecDirs:
             self.clientSpecDirs.update_client_spec_path_cache(files)
 
+        files = [f for f in files
+            if self.inClientSpec(f['path']) and self.hasBranchPrefix(f['path'])]
+
+        if not files and not gitConfigBool('git-p4.keepEmptyCommits'):
+            print('Ignoring revision {0} as it would produce an empty commit.'
+                .format(details['change']))
+            return
+
         self.gitStream.write("commit %s\n" % branch)
         self.gitStream.write("mark :%s\n" % details["change"])
         self.committedChanges.add(int(details["change"]))
@@ -2660,7 +2684,7 @@ def commit(self, details, files, branch, parent = ""):
                 print "parent %s" % parent
             self.gitStream.write("from %s\n" % parent)
 
-        self.streamP4Files(new_files)
+        self.streamP4Files(files)
         self.gitStream.write("\n")
 
         change = int(details["change"])
index 30edb179259d634f20649fe7f74df3f0c58f10ec..b938a6d4aa86b5f1e75312188c71cc4b0cdaab23 100644 (file)
@@ -610,7 +610,7 @@ do_next () {
                read -r command rest < "$todo"
                mark_action_done
                printf 'Executing: %s\n' "$rest"
-               ${SHELL:-@SHELL_PATH@} -c "$rest" # Actual execution
+               "${SHELL:-@SHELL_PATH@}" -c "$rest" # Actual execution
                status=$?
                # Run in subshell because require_clean_work_tree can die.
                dirty=f
index a475b0d7537006df2d541764c7580062518088a4..2a3873b744b313c8871f339911ed7fba4fb34d99 100755 (executable)
@@ -244,7 +244,6 @@ sub do_edit {
     "smtpserveroption" => \@smtp_server_options,
     "smtpuser" => \$smtp_authuser,
     "smtppass" => \$smtp_authpass,
-    "smtpsslcertpath" => \$smtp_ssl_cert_path,
     "smtpdomain" => \$smtp_domain,
     "smtpauth" => \$smtp_auth,
     "to" => \@initial_to,
@@ -264,6 +263,7 @@ sub do_edit {
 
 my %config_path_settings = (
     "aliasesfile" => \@alias_files,
+    "smtpsslcertpath" => \$smtp_ssl_cert_path,
 );
 
 # Handle Uncouth Termination
@@ -1211,8 +1211,7 @@ sub ssl_verify_params {
                return (SSL_verify_mode => SSL_VERIFY_PEER(),
                        SSL_ca_file => $smtp_ssl_cert_path);
        } else {
-               print STDERR "Not using SSL_VERIFY_PEER because the CA path does not exist.\n";
-               return (SSL_verify_mode => SSL_VERIFY_NONE());
+               die "CA path \"$smtp_ssl_cert_path\" does not exist";
        }
 }
 
index bac40ef6e6e3566a0ceda81303a6ebb769ac520d..8870a2681eb375505e4273b03a0d3e034e59a48e 100644 (file)
@@ -436,7 +436,7 @@ static int show_text_ref(const char *name, const struct object_id *oid,
                o = deref_tag(o, name, 0);
                if (!o)
                        return 0;
-               strbuf_addf(buf, "%s\t%s^{}\n", sha1_to_hex(o->sha1),
+               strbuf_addf(buf, "%s\t%s^{}\n", oid_to_hex(&o->oid),
                            name_nons);
        }
        return 0;
index 48f39b7f71c632aae6a42d65be929a9e44874d84..d857b131a8f7b02e3120900c5a4e525d9f8de591 100644 (file)
@@ -251,7 +251,7 @@ static void start_fetch_loose(struct transfer_request *request)
        struct active_request_slot *slot;
        struct http_object_request *obj_req;
 
-       obj_req = new_http_object_request(repo->url, request->obj->sha1);
+       obj_req = new_http_object_request(repo->url, request->obj->oid.hash);
        if (obj_req == NULL) {
                request->state = ABORTED;
                return;
@@ -275,7 +275,7 @@ static void start_fetch_loose(struct transfer_request *request)
 
 static void start_mkcol(struct transfer_request *request)
 {
-       char *hex = sha1_to_hex(request->obj->sha1);
+       char *hex = oid_to_hex(&request->obj->oid);
        struct active_request_slot *slot;
 
        request->url = get_remote_object_url(repo->url, hex, 1);
@@ -304,16 +304,16 @@ static void start_fetch_packed(struct transfer_request *request)
        struct transfer_request *check_request = request_queue_head;
        struct http_pack_request *preq;
 
-       target = find_sha1_pack(request->obj->sha1, repo->packs);
+       target = find_sha1_pack(request->obj->oid.hash, repo->packs);
        if (!target) {
-               fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", sha1_to_hex(request->obj->sha1));
+               fprintf(stderr, "Unable to fetch %s, will not be able to update server info refs\n", oid_to_hex(&request->obj->oid));
                repo->can_update_info_refs = 0;
                release_request(request);
                return;
        }
 
        fprintf(stderr, "Fetching pack %s\n", sha1_to_hex(target->sha1));
-       fprintf(stderr, " which contains %s\n", sha1_to_hex(request->obj->sha1));
+       fprintf(stderr, " which contains %s\n", oid_to_hex(&request->obj->oid));
 
        preq = new_http_pack_request(target, repo->url);
        if (preq == NULL) {
@@ -350,7 +350,7 @@ static void start_fetch_packed(struct transfer_request *request)
 
 static void start_put(struct transfer_request *request)
 {
-       char *hex = sha1_to_hex(request->obj->sha1);
+       char *hex = oid_to_hex(&request->obj->oid);
        struct active_request_slot *slot;
        struct strbuf buf = STRBUF_INIT;
        enum object_type type;
@@ -361,7 +361,7 @@ static void start_put(struct transfer_request *request)
        ssize_t size;
        git_zstream stream;
 
-       unpacked = read_sha1_file(request->obj->sha1, &type, &len);
+       unpacked = read_sha1_file(request->obj->oid.hash, &type, &len);
        hdrlen = xsnprintf(hdr, sizeof(hdr), "%s %lu", typename(type), len) + 1;
 
        /* Set it up */
@@ -533,11 +533,11 @@ static void finish_request(struct transfer_request *request)
        if (request->state == RUN_MKCOL) {
                if (request->curl_result == CURLE_OK ||
                    request->http_code == 405) {
-                       remote_dir_exists[request->obj->sha1[0]] = 1;
+                       remote_dir_exists[request->obj->oid.hash[0]] = 1;
                        start_put(request);
                } else {
                        fprintf(stderr, "MKCOL %s failed, aborting (%d/%ld)\n",
-                               sha1_to_hex(request->obj->sha1),
+                               oid_to_hex(&request->obj->oid),
                                request->curl_result, request->http_code);
                        request->state = ABORTED;
                        aborted = 1;
@@ -547,7 +547,7 @@ static void finish_request(struct transfer_request *request)
                        start_move(request);
                } else {
                        fprintf(stderr, "PUT %s failed, aborting (%d/%ld)\n",
-                               sha1_to_hex(request->obj->sha1),
+                               oid_to_hex(&request->obj->oid),
                                request->curl_result, request->http_code);
                        request->state = ABORTED;
                        aborted = 1;
@@ -556,12 +556,12 @@ static void finish_request(struct transfer_request *request)
                if (request->curl_result == CURLE_OK) {
                        if (push_verbosely)
                                fprintf(stderr, "    sent %s\n",
-                                       sha1_to_hex(request->obj->sha1));
+                                       oid_to_hex(&request->obj->oid));
                        request->obj->flags |= REMOTE;
                        release_request(request);
                } else {
                        fprintf(stderr, "MOVE %s failed, aborting (%d/%ld)\n",
-                               sha1_to_hex(request->obj->sha1),
+                               oid_to_hex(&request->obj->oid),
                                request->curl_result, request->http_code);
                        request->state = ABORTED;
                        aborted = 1;
@@ -614,7 +614,7 @@ static int fill_active_slot(void *unused)
                        start_fetch_loose(request);
                        return 1;
                } else if (pushing && request->state == NEED_PUSH) {
-                       if (remote_dir_exists[request->obj->sha1[0]] == 1) {
+                       if (remote_dir_exists[request->obj->oid.hash[0]] == 1) {
                                start_put(request);
                        } else {
                                start_mkcol(request);
@@ -638,8 +638,8 @@ static void add_fetch_request(struct object *obj)
         * Don't fetch the object if it's known to exist locally
         * or is already in the request queue
         */
-       if (remote_dir_exists[obj->sha1[0]] == -1)
-               get_remote_object_list(obj->sha1[0]);
+       if (remote_dir_exists[obj->oid.hash[0]] == -1)
+               get_remote_object_list(obj->oid.hash[0]);
        if (obj->flags & (LOCAL | FETCHING))
                return;
 
@@ -671,11 +671,11 @@ static int add_send_request(struct object *obj, struct remote_lock *lock)
         * Don't push the object if it's known to exist on the remote
         * or is already in the request queue
         */
-       if (remote_dir_exists[obj->sha1[0]] == -1)
-               get_remote_object_list(obj->sha1[0]);
+       if (remote_dir_exists[obj->oid.hash[0]] == -1)
+               get_remote_object_list(obj->oid.hash[0]);
        if (obj->flags & (REMOTE | PUSHING))
                return 0;
-       target = find_sha1_pack(obj->sha1, repo->packs);
+       target = find_sha1_pack(obj->oid.hash, repo->packs);
        if (target) {
                obj->flags |= REMOTE;
                return 0;
@@ -1307,7 +1307,7 @@ static struct object_list **process_tree(struct tree *tree,
        if (obj->flags & (UNINTERESTING | SEEN))
                return p;
        if (parse_tree(tree) < 0)
-               die("bad tree object %s", sha1_to_hex(obj->sha1));
+               die("bad tree object %s", oid_to_hex(&obj->oid));
 
        obj->flags |= SEEN;
        name = xstrdup(name);
@@ -1369,7 +1369,7 @@ static int get_delta(struct rev_info *revs, struct remote_lock *lock)
                        p = process_blob((struct blob *)obj, p, NULL, name);
                        continue;
                }
-               die("unknown pending object %s (%s)", sha1_to_hex(obj->sha1), name);
+               die("unknown pending object %s (%s)", oid_to_hex(&obj->oid), name);
        }
 
        while (objects) {
@@ -1438,11 +1438,11 @@ static void one_remote_ref(const char *refname)
         * Fetch a copy of the object if it doesn't exist locally - it
         * may be required for updating server info later.
         */
-       if (repo->can_update_info_refs && !has_sha1_file(ref->old_sha1)) {
-               obj = lookup_unknown_object(ref->old_sha1);
+       if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
+               obj = lookup_unknown_object(ref->old_oid.hash);
                if (obj) {
                        fprintf(stderr, "  fetch %s for %s\n",
-                               sha1_to_hex(ref->old_sha1), refname);
+                               oid_to_hex(&ref->old_oid), refname);
                        add_fetch_request(obj);
                }
        }
@@ -1473,24 +1473,24 @@ static void add_remote_info_ref(struct remote_ls_ctx *ls)
                return;
        }
 
-       o = parse_object(ref->old_sha1);
+       o = parse_object(ref->old_oid.hash);
        if (!o) {
                fprintf(stderr,
                        "Unable to parse object %s for remote ref %s\n",
-                       sha1_to_hex(ref->old_sha1), ls->dentry_name);
+                       oid_to_hex(&ref->old_oid), ls->dentry_name);
                aborted = 1;
                free(ref);
                return;
        }
 
        strbuf_addf(buf, "%s\t%s\n",
-                   sha1_to_hex(ref->old_sha1), ls->dentry_name);
+                   oid_to_hex(&ref->old_oid), ls->dentry_name);
 
        if (o->type == OBJ_TAG) {
                o = deref_tag(o, ls->dentry_name, 0);
                if (o)
                        strbuf_addf(buf, "%s\t%s^{}\n",
-                                   sha1_to_hex(o->sha1), ls->dentry_name);
+                                   oid_to_hex(&o->oid), ls->dentry_name);
        }
        free(ref);
 }
@@ -1581,7 +1581,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
 static int verify_merge_base(unsigned char *head_sha1, struct ref *remote)
 {
        struct commit *head = lookup_commit_or_die(head_sha1, "HEAD");
-       struct commit *branch = lookup_commit_or_die(remote->old_sha1, remote->name);
+       struct commit *branch = lookup_commit_or_die(remote->old_oid.hash, remote->name);
 
        return in_merge_bases(branch, head);
 }
@@ -1644,11 +1644,11 @@ static int delete_remote_branch(const char *pattern, int force)
                        return error("Remote HEAD resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", sha1_to_hex(head_sha1));
 
                /* Remote branch must resolve to a known object */
-               if (is_null_sha1(remote_ref->old_sha1))
+               if (is_null_oid(&remote_ref->old_oid))
                        return error("Unable to resolve remote branch %s",
                                     remote_ref->name);
-               if (!has_sha1_file(remote_ref->old_sha1))
-                       return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, sha1_to_hex(remote_ref->old_sha1));
+               if (!has_object_file(&remote_ref->old_oid))
+                       return error("Remote branch %s resolves to object %s\nwhich does not exist locally, perhaps you need to fetch?", remote_ref->name, oid_to_hex(&remote_ref->old_oid));
 
                /* Remote branch must be an ancestor of remote HEAD */
                if (!verify_merge_base(head_sha1, remote_ref)) {
@@ -1861,7 +1861,7 @@ int main(int argc, char **argv)
                if (!ref->peer_ref)
                        continue;
 
-               if (is_null_sha1(ref->peer_ref->new_sha1)) {
+               if (is_null_oid(&ref->peer_ref->new_oid)) {
                        if (delete_remote_branch(ref->name, 1) == -1) {
                                error("Could not remove %s", ref->name);
                                if (helper_status)
@@ -1874,7 +1874,7 @@ int main(int argc, char **argv)
                        continue;
                }
 
-               if (!hashcmp(ref->old_sha1, ref->peer_ref->new_sha1)) {
+               if (!oidcmp(&ref->old_oid, &ref->peer_ref->new_oid)) {
                        if (push_verbosely)
                                fprintf(stderr, "'%s': up-to-date\n", ref->name);
                        if (helper_status)
@@ -1883,11 +1883,11 @@ int main(int argc, char **argv)
                }
 
                if (!force_all &&
-                   !is_null_sha1(ref->old_sha1) &&
+                   !is_null_oid(&ref->old_oid) &&
                    !ref->force) {
-                       if (!has_sha1_file(ref->old_sha1) ||
-                           !ref_newer(ref->peer_ref->new_sha1,
-                                      ref->old_sha1)) {
+                       if (!has_object_file(&ref->old_oid) ||
+                           !ref_newer(&ref->peer_ref->new_oid,
+                                      &ref->old_oid)) {
                                /*
                                 * We do not have the remote ref, or
                                 * we know that the remote ref is not
@@ -1908,14 +1908,14 @@ int main(int argc, char **argv)
                                continue;
                        }
                }
-               hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+               oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
                new_refs++;
 
                fprintf(stderr, "updating '%s'", ref->name);
                if (strcmp(ref->name, ref->peer_ref->name))
                        fprintf(stderr, " using '%s'", ref->peer_ref->name);
                fprintf(stderr, "\n  from %s\n  to   %s\n",
-                       sha1_to_hex(ref->old_sha1), sha1_to_hex(ref->new_sha1));
+                       oid_to_hex(&ref->old_oid), oid_to_hex(&ref->new_oid));
                if (dry_run) {
                        if (helper_status)
                                printf("ok %s\n", ref->name);
@@ -1936,10 +1936,10 @@ int main(int argc, char **argv)
                /* Set up revision info for this refspec */
                argv_array_push(&commit_argv, ""); /* ignored */
                argv_array_push(&commit_argv, "--objects");
-               argv_array_push(&commit_argv, sha1_to_hex(ref->new_sha1));
-               if (!push_all && !is_null_sha1(ref->old_sha1))
+               argv_array_push(&commit_argv, oid_to_hex(&ref->new_oid));
+               if (!push_all && !is_null_oid(&ref->old_oid))
                        argv_array_pushf(&commit_argv, "^%s",
-                                        sha1_to_hex(ref->old_sha1));
+                                        oid_to_hex(&ref->old_oid));
                init_revisions(&revs, setup_git_directory());
                setup_revisions(commit_argv.argc, commit_argv.argv, &revs, NULL);
                revs.edge_hint = 0; /* just in case */
@@ -1962,7 +1962,7 @@ int main(int argc, char **argv)
                run_request_queue();
 
                /* Update the remote branch if all went well */
-               if (aborted || !update_remote(ref->new_sha1, ref_lock))
+               if (aborted || !update_remote(ref->new_oid.hash, ref_lock))
                        rc = 1;
 
                if (!rc)
diff --git a/http.c b/http.c
index 42f29ce0aaf7aac1e81849cf733e8ea92738f5b0..0da9e6639832a493f932e3ba332683d3d4ae73ae 100644 (file)
--- a/http.c
+++ b/http.c
@@ -214,10 +214,10 @@ static int http_options(const char *var, const char *value, void *cb)
 #endif
 #if LIBCURL_VERSION_NUM >= 0x070908
        if (!strcmp("http.sslcapath", var))
-               return git_config_string(&ssl_capath, var, value);
+               return git_config_pathname(&ssl_capath, var, value);
 #endif
        if (!strcmp("http.sslcainfo", var))
-               return git_config_string(&ssl_cainfo, var, value);
+               return git_config_pathname(&ssl_cainfo, var, value);
        if (!strcmp("http.sslcertpasswordprotected", var)) {
                ssl_cert_password_required = git_config_bool(var, value);
                return 0;
@@ -464,6 +464,17 @@ static CURL *get_curl_handle(void)
 
        if (curl_http_proxy) {
                curl_easy_setopt(result, CURLOPT_PROXY, curl_http_proxy);
+#if LIBCURL_VERSION_NUM >= 0x071800
+               if (starts_with(curl_http_proxy, "socks5"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
+               else if (starts_with(curl_http_proxy, "socks4a"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4A);
+               else if (starts_with(curl_http_proxy, "socks"))
+                       curl_easy_setopt(result,
+                               CURLOPT_PROXYTYPE, CURLPROXY_SOCKS4);
+#endif
        }
 #if LIBCURL_VERSION_NUM >= 0x070a07
        curl_easy_setopt(result, CURLOPT_PROXYAUTH, CURLAUTH_ANY);
@@ -1391,7 +1402,7 @@ int http_fetch_ref(const char *base, struct ref *ref)
        if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
                strbuf_rtrim(&buffer);
                if (buffer.len == 40)
-                       ret = get_sha1_hex(buffer.buf, ref->old_sha1);
+                       ret = get_oid_hex(buffer.buf, &ref->old_oid);
                else if (starts_with(buffer.buf, "ref: ")) {
                        ref->symref = xstrdup(buffer.buf + 5);
                        ret = 0;
@@ -1617,8 +1628,8 @@ struct http_pack_request *new_http_pack_request(
        if (prev_posn>0) {
                if (http_is_verbose)
                        fprintf(stderr,
-                               "Resuming fetch of pack %s at byte %ld\n",
-                               sha1_to_hex(target->sha1), prev_posn);
+                               "Resuming fetch of pack %s at byte %"PRIuMAX"\n",
+                               sha1_to_hex(target->sha1), (uintmax_t)prev_posn);
                http_opt_request_remainder(preq->slot->curl, prev_posn);
        }
 
@@ -1772,8 +1783,8 @@ struct http_object_request *new_http_object_request(const char *base_url,
        if (prev_posn>0) {
                if (http_is_verbose)
                        fprintf(stderr,
-                               "Resuming fetch of object %s at byte %ld\n",
-                               hex, prev_posn);
+                               "Resuming fetch of object %s at byte %"PRIuMAX"\n",
+                               hex, (uintmax_t)prev_posn);
                http_opt_request_remainder(freq->slot->curl, prev_posn);
        }
 
diff --git a/ident.c b/ident.c
index 5ff1aadaaaa999df3bfecb07f84f259469b3a54d..00a62e0c4276fb8889a0b158c9e71a22e3be092c 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -70,10 +70,35 @@ static int add_mailname_host(struct strbuf *buf)
        return 0;
 }
 
+static int canonical_name(const char *host, struct strbuf *out)
+{
+       int status = -1;
+
+#ifndef NO_IPV6
+       struct addrinfo hints, *ai;
+       memset (&hints, '\0', sizeof (hints));
+       hints.ai_flags = AI_CANONNAME;
+       if (!getaddrinfo(host, NULL, &hints, &ai)) {
+               if (ai && strchr(ai->ai_canonname, '.')) {
+                       strbuf_addstr(out, ai->ai_canonname);
+                       status = 0;
+               }
+               freeaddrinfo(ai);
+       }
+#else
+       struct hostent *he = gethostbyname(host);
+       if (he && strchr(he->h_name, '.')) {
+               strbuf_addstr(out, he->h_name);
+               status = 0;
+       }
+#endif /* NO_IPV6 */
+
+       return status;
+}
+
 static void add_domainname(struct strbuf *out)
 {
        char buf[1024];
-       struct hostent *he;
 
        if (gethostname(buf, sizeof(buf))) {
                warning("cannot get host name: %s", strerror(errno));
@@ -82,9 +107,7 @@ static void add_domainname(struct strbuf *out)
        }
        if (strchr(buf, '.'))
                strbuf_addstr(out, buf);
-       else if ((he = gethostbyname(buf)) && strchr(he->h_name, '.'))
-               strbuf_addstr(out, he->h_name);
-       else
+       else if (canonical_name(buf, out) < 0)
                strbuf_addf(out, "%s.(none)", buf);
 }
 
index 626b22cc31726c146347f44d73daa80b776ab74f..af6e2f799ec7e2447ee888854442a7cbfe7644e4 100644 (file)
@@ -502,7 +502,7 @@ static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec)
        unsigned mode;
        unsigned char sha1[20];
 
-       if (get_tree_entry(commit->object.sha1, spec->path,
+       if (get_tree_entry(commit->object.oid.hash, spec->path,
                           sha1, &mode))
                die("There is no path %s in the commit", spec->path);
        fill_filespec(spec, sha1, 1, mode);
@@ -824,8 +824,8 @@ static void queue_diffs(struct line_log_data *range,
        assert(commit);
 
        DIFF_QUEUE_CLEAR(&diff_queued_diff);
-       diff_tree_sha1(parent ? parent->tree->object.sha1 : NULL,
-                       commit->tree->object.sha1, "", opt);
+       diff_tree_sha1(parent ? parent->tree->object.oid.hash : NULL,
+                       commit->tree->object.oid.hash, "", opt);
        if (opt->detect_rename) {
                filter_diffs_for_paths(range, 1);
                if (diff_might_be_rename())
index 41736d23727e2ff246d7000ee74a47ce80ea189d..11732d93883ee6af8990ad311d07f28028af9876 100644 (file)
@@ -84,7 +84,7 @@ static void process_tree(struct rev_info *revs,
        if (parse_tree_gently(tree, revs->ignore_missing_links) < 0) {
                if (revs->ignore_missing_links)
                        return;
-               die("bad tree object %s", sha1_to_hex(obj->sha1));
+               die("bad tree object %s", oid_to_hex(&obj->oid));
        }
        obj->flags |= SEEN;
        show(obj, path, name, cb_data);
@@ -229,7 +229,7 @@ void traverse_commit_list(struct rev_info *revs,
                        continue;
                }
                die("unknown pending object %s (%s)",
-                   sha1_to_hex(obj->sha1), name);
+                   oid_to_hex(&obj->oid), name);
        }
        object_array_clear(&revs->pending);
        strbuf_release(&base);
index 7b1b57aaf6e48a430f664fa8deafb75a0cfcc576..35e780170a2529cf528f4edfcd6bac9dcab5591f 100644 (file)
@@ -133,7 +133,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
                if (!obj)
                        break;
                if (!obj->parsed)
-                       parse_object(obj->sha1);
+                       parse_object(obj->oid.hash);
                add_name_decoration(DECORATION_REF_TAG, refname, obj);
        }
        return 0;
@@ -165,7 +165,7 @@ static void show_parents(struct commit *commit, int abbrev)
        struct commit_list *p;
        for (p = commit->parents; p ; p = p->next) {
                struct commit *parent = p->item;
-               printf(" %s", find_unique_abbrev(parent->object.sha1, abbrev));
+               printf(" %s", find_unique_abbrev(parent->object.oid.hash, abbrev));
        }
 }
 
@@ -173,7 +173,7 @@ static void show_children(struct rev_info *opt, struct commit *commit, int abbre
 {
        struct commit_list *p = lookup_decoration(&opt->children, &commit->object);
        for ( ; p; p = p->next) {
-               printf(" %s", find_unique_abbrev(p->item->object.sha1, abbrev));
+               printf(" %s", find_unique_abbrev(p->item->object.oid.hash, abbrev));
        }
 }
 
@@ -342,7 +342,7 @@ void log_write_email_headers(struct rev_info *opt, struct commit *commit,
 {
        const char *subject = NULL;
        const char *extra_headers = opt->extra_headers;
-       const char *name = sha1_to_hex(commit->object.sha1);
+       const char *name = oid_to_hex(&commit->object.oid);
 
        *need_8bit_cte_p = 0; /* unknown */
        if (opt->total > 0) {
@@ -469,7 +469,7 @@ static int which_parent(const unsigned char *sha1, const struct commit *commit)
        const struct commit_list *parent;
 
        for (nth = 0, parent = commit->parents; parent; parent = parent->next) {
-               if (!hashcmp(parent->item->object.sha1, sha1))
+               if (!hashcmp(parent->item->object.oid.hash, sha1))
                        return nth;
                nth++;
        }
@@ -503,13 +503,13 @@ static void show_one_mergetag(struct commit *commit,
        if (parse_tag_buffer(tag, extra->value, extra->len))
                strbuf_addstr(&verify_message, "malformed mergetag\n");
        else if (is_common_merge(commit) &&
-                !hashcmp(tag->tagged->sha1,
-                         commit->parents->next->item->object.sha1))
+                !oidcmp(&tag->tagged->oid,
+                         &commit->parents->next->item->object.oid))
                strbuf_addf(&verify_message,
                            "merged tag '%s'\n", tag->tag);
-       else if ((nth = which_parent(tag->tagged->sha1, commit)) < 0)
+       else if ((nth = which_parent(tag->tagged->oid.hash, commit)) < 0)
                strbuf_addf(&verify_message, "tag %s names a non-parent %s\n",
-                                   tag->tag, tag->tagged->sha1);
+                                   tag->tag, tag->tagged->oid.hash);
        else
                strbuf_addf(&verify_message,
                            "parent #%d, tagged '%s'\n", nth + 1, tag->tag);
@@ -553,7 +553,7 @@ void show_log(struct rev_info *opt)
 
                if (!opt->graph)
                        put_revision_mark(opt, commit);
-               fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit), stdout);
+               fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit), stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
                if (opt->children.name)
@@ -613,7 +613,7 @@ void show_log(struct rev_info *opt)
 
                if (!opt->graph)
                        put_revision_mark(opt, commit);
-               fputs(find_unique_abbrev(commit->object.sha1, abbrev_commit),
+               fputs(find_unique_abbrev(commit->object.oid.hash, abbrev_commit),
                      stdout);
                if (opt->print_parents)
                        show_parents(commit, abbrev_commit);
@@ -621,7 +621,7 @@ void show_log(struct rev_info *opt)
                        show_children(opt, commit, abbrev_commit);
                if (parent)
                        printf(" (from %s)",
-                              find_unique_abbrev(parent->object.sha1,
+                              find_unique_abbrev(parent->object.oid.hash,
                                                  abbrev_commit));
                fputs(diff_get_color_opt(&opt->diffopt, DIFF_RESET), stdout);
                show_decorations(opt, commit);
@@ -660,7 +660,7 @@ void show_log(struct rev_info *opt)
                struct strbuf notebuf = STRBUF_INIT;
 
                raw = (opt->commit_format == CMIT_FMT_USERFORMAT);
-               format_display_notes(commit->object.sha1, &notebuf,
+               format_display_notes(commit->object.oid.hash, &notebuf,
                                     get_log_output_encoding(), raw);
                ctx.notes_message = notebuf.len
                        ? strbuf_detach(&notebuf, NULL)
@@ -796,19 +796,19 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
 {
        int showed_log;
        struct commit_list *parents;
-       unsigned const char *sha1;
+       struct object_id *oid;
 
        if (!opt->diff && !DIFF_OPT_TST(&opt->diffopt, EXIT_WITH_STATUS))
                return 0;
 
        parse_commit_or_die(commit);
-       sha1 = commit->tree->object.sha1;
+       oid = &commit->tree->object.oid;
 
        /* Root commit? */
        parents = get_saved_parents(opt, commit);
        if (!parents) {
                if (opt->show_root_diff) {
-                       diff_root_tree_sha1(sha1, "", &opt->diffopt);
+                       diff_root_tree_sha1(oid->hash, "", &opt->diffopt);
                        log_tree_diff_flush(opt);
                }
                return !opt->loginfo;
@@ -827,8 +827,8 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                         * we merged _in_.
                         */
                        parse_commit_or_die(parents->item);
-                       diff_tree_sha1(parents->item->tree->object.sha1,
-                                      sha1, "", &opt->diffopt);
+                       diff_tree_sha1(parents->item->tree->object.oid.hash,
+                                      oid->hash, "", &opt->diffopt);
                        log_tree_diff_flush(opt);
                        return !opt->loginfo;
                }
@@ -842,8 +842,8 @@ static int log_tree_diff(struct rev_info *opt, struct commit *commit, struct log
                struct commit *parent = parents->item;
 
                parse_commit_or_die(parent);
-               diff_tree_sha1(parent->tree->object.sha1,
-                              sha1, "", &opt->diffopt);
+               diff_tree_sha1(parent->tree->object.oid.hash,
+                              oid->hash, "", &opt->diffopt);
                log_tree_diff_flush(opt);
 
                showed_log |= !opt->loginfo;
index 7abb894c684d1ff7bc26b30ffa41597451750f20..ddca601c77fcab7d9d68362ce5c4f252ab7ccf51 100644 (file)
@@ -11,7 +11,7 @@ static int fill_mmfile_blob(mmfile_t *f, struct blob *obj)
        unsigned long size;
        enum object_type type;
 
-       buf = read_sha1_file(obj->object.sha1, &type, &size);
+       buf = read_sha1_file(obj->object.oid.hash, &type, &size);
        if (!buf)
                return -1;
        if (type != OBJ_BLOB) {
@@ -100,7 +100,7 @@ void *merge_blobs(const char *path, struct blob *base, struct blob *our, struct
                        return NULL;
                if (!our)
                        our = their;
-               return read_sha1_file(our->object.sha1, &type, size);
+               return read_sha1_file(our->object.oid.hash, &type, size);
        }
 
        if (fill_mmfile_blob(&f1, our) < 0)
index 21e680a78e758eeaf03cf03a8b485095d96bcf5d..8eabde20fbe030a1242cf934d6e2e4cdb6ac14e7 100644 (file)
 static struct tree *shift_tree_object(struct tree *one, struct tree *two,
                                      const char *subtree_shift)
 {
-       unsigned char shifted[20];
+       struct object_id shifted;
 
        if (!*subtree_shift) {
-               shift_tree(one->object.sha1, two->object.sha1, shifted, 0);
+               shift_tree(one->object.oid.hash, two->object.oid.hash, shifted.hash, 0);
        } else {
-               shift_tree_by(one->object.sha1, two->object.sha1, shifted,
+               shift_tree_by(one->object.oid.hash, two->object.oid.hash, shifted.hash,
                              subtree_shift);
        }
-       if (!hashcmp(two->object.sha1, shifted))
+       if (!oidcmp(&two->object.oid, &shifted))
                return two;
-       return lookup_tree(shifted);
+       return lookup_tree(shifted.hash);
 }
 
 static struct commit *make_virtual_commit(struct tree *tree, const char *comment)
@@ -184,7 +184,7 @@ static void output_commit_title(struct merge_options *o, struct commit *commit)
        if (commit->util)
                printf("virtual %s\n", merge_remote_util(commit)->name);
        else {
-               printf("%s ", find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV));
+               printf("%s ", find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV));
                if (parse_commit(commit) != 0)
                        printf(_("(bad commit)\n"));
                else {
@@ -313,11 +313,11 @@ static struct stage_data *insert_stage_data(const char *path,
 {
        struct string_list_item *item;
        struct stage_data *e = xcalloc(1, sizeof(struct stage_data));
-       get_tree_entry(o->object.sha1, path,
+       get_tree_entry(o->object.oid.hash, path,
                        e->stages[1].sha, &e->stages[1].mode);
-       get_tree_entry(a->object.sha1, path,
+       get_tree_entry(a->object.oid.hash, path,
                        e->stages[2].sha, &e->stages[2].mode);
-       get_tree_entry(b->object.sha1, path,
+       get_tree_entry(b->object.oid.hash, path,
                        e->stages[3].sha, &e->stages[3].mode);
        item = string_list_insert(entries, path);
        item->util = e;
@@ -493,7 +493,7 @@ static struct string_list *get_renames(struct merge_options *o,
        opts.show_rename_progress = o->show_rename_progress;
        opts.output_format = DIFF_FORMAT_NO_OUTPUT;
        diff_setup_done(&opts);
-       diff_tree_sha1(o_tree->object.sha1, tree->object.sha1, "", &opts);
+       diff_tree_sha1(o_tree->object.oid.hash, tree->object.oid.hash, "", &opts);
        diffcore_std(&opts);
        if (opts.needed_rename_limit > o->needed_rename_limit)
                o->needed_rename_limit = opts.needed_rename_limit;
@@ -1812,7 +1812,7 @@ int merge_trees(struct merge_options *o,
                common = shift_tree_object(head, common, o->subtree_shift);
        }
 
-       if (sha_eq(common->object.sha1, merge->object.sha1)) {
+       if (sha_eq(common->object.oid.hash, merge->object.oid.hash)) {
                output(o, 0, _("Already up-to-date!"));
                *result = head;
                return 1;
@@ -1823,8 +1823,8 @@ int merge_trees(struct merge_options *o,
        if (code != 0) {
                if (show(o, 4) || o->call_depth)
                        die(_("merging of trees %s and %s failed"),
-                           sha1_to_hex(head->object.sha1),
-                           sha1_to_hex(merge->object.sha1));
+                           oid_to_hex(&head->object.oid),
+                           oid_to_hex(&merge->object.oid));
                else
                        exit(128);
        }
diff --git a/merge.c b/merge.c
index fcff632bd63a113ca78b0bd86da1e6af8bc6f7dc..5db7d56b90c3413bbd831f90de73d3306e10ce2f 100644 (file)
--- a/merge.c
+++ b/merge.c
@@ -10,7 +10,7 @@
 static const char *merge_argument(struct commit *commit)
 {
        if (commit)
-               return sha1_to_hex(commit->object.sha1);
+               return oid_to_hex(&commit->object.oid);
        else
                return EMPTY_TREE_SHA1_HEX;
 }
index b3d1dab51fb9e7aea4af05211326a1a91b1279d7..34bfac0c685f18c635b30dbf19d7b25250faa1c6 100644 (file)
@@ -602,15 +602,15 @@ int notes_merge(struct notes_merge_options *o,
                if (o->verbosity >= 4)
                        printf("No merge base found; doing history-less merge\n");
        } else if (!bases->next) {
-               base_sha1 = bases->item->object.sha1;
-               base_tree_sha1 = bases->item->tree->object.sha1;
+               base_sha1 = bases->item->object.oid.hash;
+               base_tree_sha1 = bases->item->tree->object.oid.hash;
                if (o->verbosity >= 4)
                        printf("One merge base found (%.7s)\n",
                                sha1_to_hex(base_sha1));
        } else {
                /* TODO: How to handle multiple merge-bases? */
-               base_sha1 = bases->item->object.sha1;
-               base_tree_sha1 = bases->item->tree->object.sha1;
+               base_sha1 = bases->item->object.oid.hash;
+               base_tree_sha1 = bases->item->tree->object.oid.hash;
                if (o->verbosity >= 3)
                        printf("Multiple merge bases found. Using the first "
                                "(%.7s)\n", sha1_to_hex(base_sha1));
@@ -618,27 +618,27 @@ int notes_merge(struct notes_merge_options *o,
 
        if (o->verbosity >= 4)
                printf("Merging remote commit %.7s into local commit %.7s with "
-                       "merge-base %.7s\n", sha1_to_hex(remote->object.sha1),
-                       sha1_to_hex(local->object.sha1),
+                       "merge-base %.7s\n", oid_to_hex(&remote->object.oid),
+                       oid_to_hex(&local->object.oid),
                        sha1_to_hex(base_sha1));
 
-       if (!hashcmp(remote->object.sha1, base_sha1)) {
+       if (!hashcmp(remote->object.oid.hash, base_sha1)) {
                /* Already merged; result == local commit */
                if (o->verbosity >= 2)
                        printf("Already up-to-date!\n");
-               hashcpy(result_sha1, local->object.sha1);
+               hashcpy(result_sha1, local->object.oid.hash);
                goto found_result;
        }
-       if (!hashcmp(local->object.sha1, base_sha1)) {
+       if (!hashcmp(local->object.oid.hash, base_sha1)) {
                /* Fast-forward; result == remote commit */
                if (o->verbosity >= 2)
                        printf("Fast-forward\n");
-               hashcpy(result_sha1, remote->object.sha1);
+               hashcpy(result_sha1, remote->object.oid.hash);
                goto found_result;
        }
 
-       result = merge_from_diffs(o, base_tree_sha1, local->tree->object.sha1,
-                                 remote->tree->object.sha1, local_tree);
+       result = merge_from_diffs(o, base_tree_sha1, local->tree->object.oid.hash,
+                                 remote->tree->object.oid.hash, local_tree);
 
        if (result != 0) { /* non-trivial merge (with or without conflicts) */
                /* Commit (partial) result */
index 980ac5fcdfa7654667c9902aa56407f1ce7053ba..67d9a9e2211fc2f6ab5772bfe58e42a2a95e469d 100644 (file)
--- a/object.c
+++ b/object.c
@@ -68,7 +68,7 @@ static unsigned int hash_obj(const unsigned char *sha1, unsigned int n)
  */
 static void insert_obj_hash(struct object *obj, struct object **hash, unsigned int size)
 {
-       unsigned int j = hash_obj(obj->sha1, size);
+       unsigned int j = hash_obj(obj->oid.hash, size);
 
        while (hash[j]) {
                j++;
@@ -92,7 +92,7 @@ struct object *lookup_object(const unsigned char *sha1)
 
        first = i = hash_obj(sha1, obj_hash_size);
        while ((obj = obj_hash[i]) != NULL) {
-               if (!hashcmp(sha1, obj->sha1))
+               if (!hashcmp(sha1, obj->oid.hash))
                        break;
                i++;
                if (i == obj_hash_size)
@@ -145,7 +145,7 @@ void *create_object(const unsigned char *sha1, void *o)
        obj->parsed = 0;
        obj->used = 0;
        obj->flags = 0;
-       hashcpy(obj->sha1, sha1);
+       hashcpy(obj->oid.hash, sha1);
 
        if (obj_hash_size - 1 <= nr_objs * 2)
                grow_object_hash();
@@ -168,7 +168,7 @@ void *object_as_type(struct object *obj, enum object_type type, int quiet)
        else {
                if (!quiet)
                        error("object %s is a %s, not a %s",
-                             sha1_to_hex(obj->sha1),
+                             oid_to_hex(&obj->oid),
                              typename(obj->type), typename(type));
                return NULL;
        }
index 6416247defa133c5c7ca43c69dfe09ae00ac03f7..f8b644263ff7e3b244bc1804b8527f87f7dbcf84 100644 (file)
--- a/object.h
+++ b/object.h
@@ -49,7 +49,7 @@ struct object {
        unsigned used : 1;
        unsigned type : TYPE_BITS;
        unsigned flags : FLAG_BITS;
-       unsigned char sha1[20];
+       struct object_id oid;
 };
 
 extern const char *typename(unsigned int type);
index c05d1386af7210e75a1012f3772745315ecc3592..6bff970c905b6a9c8a04773d64afb164dd124cbe 100644 (file)
@@ -152,7 +152,7 @@ static void show_object(struct object *object, const struct name_path *path,
                        const char *last, void *data)
 {
        struct bitmap *base = data;
-       bitmap_set(base, find_object_pos(object->sha1));
+       bitmap_set(base, find_object_pos(object->oid.hash));
        mark_as_seen(object);
 }
 
@@ -165,12 +165,12 @@ static int
 add_to_include_set(struct bitmap *base, struct commit *commit)
 {
        khiter_t hash_pos;
-       uint32_t bitmap_pos = find_object_pos(commit->object.sha1);
+       uint32_t bitmap_pos = find_object_pos(commit->object.oid.hash);
 
        if (bitmap_get(base, bitmap_pos))
                return 0;
 
-       hash_pos = kh_get_sha1(writer.bitmaps, commit->object.sha1);
+       hash_pos = kh_get_sha1(writer.bitmaps, commit->object.oid.hash);
        if (hash_pos < kh_end(writer.bitmaps)) {
                struct bitmapped_commit *bc = kh_value(writer.bitmaps, hash_pos);
                bitmap_or_ewah(base, bc->bitmap);
@@ -308,10 +308,10 @@ void bitmap_writer_build(struct packing_data *to_pack)
                if (i >= reuse_after)
                        stored->flags |= BITMAP_FLAG_REUSE;
 
-               hash_pos = kh_put_sha1(writer.bitmaps, object->sha1, &hash_ret);
+               hash_pos = kh_put_sha1(writer.bitmaps, object->oid.hash, &hash_ret);
                if (hash_ret == 0)
                        die("Duplicate entry when writing index: %s",
-                           sha1_to_hex(object->sha1));
+                           oid_to_hex(&object->oid));
 
                kh_value(writer.bitmaps, hash_pos) = stored;
                display_progress(writer.progress, writer.selected_nr - i);
@@ -414,14 +414,14 @@ void bitmap_writer_select_commits(struct commit **indexed_commits,
 
                if (next == 0) {
                        chosen = indexed_commits[i];
-                       reused_bitmap = find_reused_bitmap(chosen->object.sha1);
+                       reused_bitmap = find_reused_bitmap(chosen->object.oid.hash);
                } else {
                        chosen = indexed_commits[i + next];
 
                        for (j = 0; j <= next; ++j) {
                                struct commit *cm = indexed_commits[i + j];
 
-                               reused_bitmap = find_reused_bitmap(cm->object.sha1);
+                               reused_bitmap = find_reused_bitmap(cm->object.oid.hash);
                                if (reused_bitmap || (cm->object.flags & NEEDS_BITMAP) != 0) {
                                        chosen = cm;
                                        break;
@@ -474,7 +474,7 @@ static void write_selected_commits_v1(struct sha1file *f,
                struct bitmapped_commit *stored = &writer.selected[i];
 
                int commit_pos =
-                       sha1_pos(stored->commit->object.sha1, index, index_nr, sha1_access);
+                       sha1_pos(stored->commit->object.oid.hash, index, index_nr, sha1_access);
 
                if (commit_pos < 0)
                        die("BUG: trying to write commit not in index");
index 7dfcb341d61aa93e2c655963571ba1809fc93d40..cb9c62280389cfdb3bcaf483ac4edf2f0a00969f 100644 (file)
@@ -397,7 +397,7 @@ static int ext_index_add_object(struct object *object, const char *name)
        int hash_ret;
        int bitmap_pos;
 
-       hash_pos = kh_put_sha1_pos(eindex->positions, object->sha1, &hash_ret);
+       hash_pos = kh_put_sha1_pos(eindex->positions, object->oid.hash, &hash_ret);
        if (hash_ret > 0) {
                if (eindex->count >= eindex->alloc) {
                        eindex->alloc = (eindex->alloc + 16) * 3 / 2;
@@ -423,7 +423,7 @@ static void show_object(struct object *object, const struct name_path *path,
        struct bitmap *base = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->sha1);
+       bitmap_pos = bitmap_position(object->oid.hash);
 
        if (bitmap_pos < 0) {
                char *name = path_name(path, last);
@@ -466,11 +466,11 @@ static int should_include(struct commit *commit, void *_data)
        struct include_data *data = _data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.sha1);
+       bitmap_pos = bitmap_position(commit->object.oid.hash);
        if (bitmap_pos < 0)
                bitmap_pos = ext_index_add_object((struct object *)commit, NULL);
 
-       if (!add_to_include_set(data, commit->object.sha1, bitmap_pos)) {
+       if (!add_to_include_set(data, commit->object.oid.hash, bitmap_pos)) {
                struct commit_list *parent = commit->parents;
 
                while (parent) {
@@ -506,7 +506,7 @@ static struct bitmap *find_objects(struct rev_info *revs,
                roots = roots->next;
 
                if (object->type == OBJ_COMMIT) {
-                       khiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->sha1);
+                       khiter_t pos = kh_get_sha1(bitmap_git.bitmaps, object->oid.hash);
 
                        if (pos < kh_end(bitmap_git.bitmaps)) {
                                struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
@@ -548,7 +548,7 @@ static struct bitmap *find_objects(struct rev_info *revs,
                int pos;
 
                roots = roots->next;
-               pos = bitmap_position(object->sha1);
+               pos = bitmap_position(object->oid.hash);
 
                if (pos < 0 || base == NULL || !bitmap_get(base, pos)) {
                        object->flags &= ~UNINTERESTING;
@@ -593,7 +593,7 @@ static void show_extended_objects(struct bitmap *objects,
                        continue;
 
                obj = eindex->objects[i];
-               show_reach(obj->sha1, obj->type, 0, eindex->hashes[i], NULL, 0);
+               show_reach(obj->oid.hash, obj->type, 0, eindex->hashes[i], NULL, 0);
        }
 }
 
@@ -650,7 +650,7 @@ static int in_bitmapped_pack(struct object_list *roots)
                struct object *object = roots->item;
                roots = roots->next;
 
-               if (find_pack_entry_one(object->sha1, bitmap_git.pack) > 0)
+               if (find_pack_entry_one(object->oid.hash, bitmap_git.pack) > 0)
                        return 1;
        }
 
@@ -680,7 +680,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
                struct object *object = pending_e[i].item;
 
                if (object->type == OBJ_NONE)
-                       parse_object_or_die(object->sha1, NULL);
+                       parse_object_or_die(object->oid.hash, NULL);
 
                while (object->type == OBJ_TAG) {
                        struct tag *tag = (struct tag *) object;
@@ -692,7 +692,7 @@ int prepare_bitmap_walk(struct rev_info *revs)
 
                        if (!tag->tagged)
                                die("bad tag");
-                       object = parse_object_or_die(tag->tagged->sha1, NULL);
+                       object = parse_object_or_die(tag->tagged->oid.hash, NULL);
                }
 
                if (object->flags & UNINTERESTING)
@@ -904,9 +904,9 @@ static void test_show_object(struct object *object,
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(object->sha1);
+       bitmap_pos = bitmap_position(object->oid.hash);
        if (bitmap_pos < 0)
-               die("Object not in bitmap: %s\n", sha1_to_hex(object->sha1));
+               die("Object not in bitmap: %s\n", oid_to_hex(&object->oid));
 
        bitmap_set(tdata->base, bitmap_pos);
        display_progress(tdata->prg, ++tdata->seen);
@@ -917,9 +917,9 @@ static void test_show_commit(struct commit *commit, void *data)
        struct bitmap_test_data *tdata = data;
        int bitmap_pos;
 
-       bitmap_pos = bitmap_position(commit->object.sha1);
+       bitmap_pos = bitmap_position(commit->object.oid.hash);
        if (bitmap_pos < 0)
-               die("Object not in bitmap: %s\n", sha1_to_hex(commit->object.sha1));
+               die("Object not in bitmap: %s\n", oid_to_hex(&commit->object.oid));
 
        bitmap_set(tdata->base, bitmap_pos);
        display_progress(tdata->prg, ++tdata->seen);
@@ -943,20 +943,20 @@ void test_bitmap_walk(struct rev_info *revs)
                bitmap_git.version, bitmap_git.entry_count);
 
        root = revs->pending.objects[0].item;
-       pos = kh_get_sha1(bitmap_git.bitmaps, root->sha1);
+       pos = kh_get_sha1(bitmap_git.bitmaps, root->oid.hash);
 
        if (pos < kh_end(bitmap_git.bitmaps)) {
                struct stored_bitmap *st = kh_value(bitmap_git.bitmaps, pos);
                struct ewah_bitmap *bm = lookup_stored_bitmap(st);
 
                fprintf(stderr, "Found bitmap for %s. %d bits / %08x checksum\n",
-                       sha1_to_hex(root->sha1), (int)bm->bit_size, ewah_checksum(bm));
+                       oid_to_hex(&root->oid), (int)bm->bit_size, ewah_checksum(bm));
 
                result = ewah_to_bitmap(bm);
        }
 
        if (result == NULL)
-               die("Commit %s doesn't have an indexed bitmap", sha1_to_hex(root->sha1));
+               die("Commit %s doesn't have an indexed bitmap", oid_to_hex(&root->oid));
 
        revs->tag_objects = 1;
        revs->tree_objects = 1;
index 63a595c45c961fce54cca95ab6e09e3336f0bb8e..433bd86ccd5c38ff5a4993be4e1463946aead59d 100644 (file)
@@ -126,7 +126,7 @@ static int verify_packfile(struct packed_git *p,
                                    sha1_to_hex(entries[i].sha1), p->pack_name);
                else if (fn) {
                        int eaten = 0;
-                       fn(entries[i].sha1, type, size, data, &eaten);
+                       err |= fn(entries[i].sha1, type, size, data, &eaten);
                        if (eaten)
                                data = NULL;
                }
index 3eceba4463b7e837cc12ee810811c8c5b8b253e7..47a91920601d74842a0947ce1431c00b3d723972 100644 (file)
@@ -5,10 +5,6 @@
 #include "color.h"
 #include "utf8.h"
 
-static int parse_options_usage(struct parse_opt_ctx_t *ctx,
-                              const char * const *usagestr,
-                              const struct option *opts, int err);
-
 #define OPT_SHORT 1
 #define OPT_UNSET 2
 
@@ -414,7 +410,7 @@ void parse_options_start(struct parse_opt_ctx_t *ctx,
                         const struct option *options, int flags)
 {
        memset(ctx, 0, sizeof(*ctx));
-       ctx->argc = argc - 1;
+       ctx->argc = ctx->total = argc - 1;
        ctx->argv = argv + 1;
        ctx->out  = argv;
        ctx->prefix = prefix;
@@ -435,6 +431,7 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                       const char * const usagestr[])
 {
        int internal_help = !(ctx->flags & PARSE_OPT_NO_INTERNAL_HELP);
+       int err = 0;
 
        /* we must reset ->opt, unknown short option leave it dangling */
        ctx->opt = NULL;
@@ -451,27 +448,32 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                        continue;
                }
 
+               /* lone -h asks for help */
+               if (internal_help && ctx->total == 1 && !strcmp(arg + 1, "h"))
+                       goto show_usage;
+
                if (arg[1] != '-') {
                        ctx->opt = arg + 1;
-                       if (internal_help && *ctx->opt == 'h')
-                               return parse_options_usage(ctx, usagestr, options, 0);
                        switch (parse_short_opt(ctx, options)) {
                        case -1:
-                               return parse_options_usage(ctx, usagestr, options, 1);
+                               goto show_usage_error;
                        case -2:
                                if (ctx->opt)
                                        check_typos(arg + 1, options);
+                               if (internal_help && *ctx->opt == 'h')
+                                       goto show_usage;
                                goto unknown;
                        }
                        if (ctx->opt)
                                check_typos(arg + 1, options);
                        while (ctx->opt) {
-                               if (internal_help && *ctx->opt == 'h')
-                                       return parse_options_usage(ctx, usagestr, options, 0);
                                switch (parse_short_opt(ctx, options)) {
                                case -1:
-                                       return parse_options_usage(ctx, usagestr, options, 1);
+                                       goto show_usage_error;
                                case -2:
+                                       if (internal_help && *ctx->opt == 'h')
+                                               goto show_usage;
+
                                        /* fake a short option thing to hide the fact that we may have
                                         * started to parse aggregated stuff
                                         *
@@ -496,10 +498,10 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                if (internal_help && !strcmp(arg + 2, "help-all"))
                        return usage_with_options_internal(ctx, usagestr, options, 1, 0);
                if (internal_help && !strcmp(arg + 2, "help"))
-                       return parse_options_usage(ctx, usagestr, options, 0);
+                       goto show_usage;
                switch (parse_long_opt(ctx, arg + 2, options)) {
                case -1:
-                       return parse_options_usage(ctx, usagestr, options, 1);
+                       goto show_usage_error;
                case -2:
                        goto unknown;
                }
@@ -511,6 +513,11 @@ int parse_options_step(struct parse_opt_ctx_t *ctx,
                ctx->opt = NULL;
        }
        return PARSE_OPT_DONE;
+
+ show_usage_error:
+       err = 1;
+ show_usage:
+       return usage_with_options_internal(ctx, usagestr, options, 0, err);
 }
 
 int parse_options_end(struct parse_opt_ctx_t *ctx)
@@ -656,13 +663,6 @@ void NORETURN usage_msg_opt(const char *msg,
        usage_with_options(usagestr, options);
 }
 
-static int parse_options_usage(struct parse_opt_ctx_t *ctx,
-                              const char * const *usagestr,
-                              const struct option *opts, int err)
-{
-       return usage_with_options_internal(ctx, usagestr, opts, 0, err);
-}
-
 #undef opterror
 int opterror(const struct option *opt, const char *reason, int flags)
 {
index e8b55ea87aaea6a0e16239fd9b2025c4927d2145..ea4af92a5110554c450665e08fce3a97310e5640 100644 (file)
@@ -199,7 +199,7 @@ enum {
 struct parse_opt_ctx_t {
        const char **argv;
        const char **out;
-       int argc, cpidx;
+       int argc, cpidx, total;
        const char *opt;
        int flags;
        const char *prefix;
index bf81b923610fe2473655560cd111adb3061673d9..b7b3e5a1a700d4d2e1656c605697c2cf6345b8a6 100644 (file)
@@ -8,10 +8,10 @@ static int commit_patch_id(struct commit *commit, struct diff_options *options,
                    unsigned char *sha1)
 {
        if (commit->parents)
-               diff_tree_sha1(commit->parents->item->object.sha1,
-                              commit->object.sha1, "", options);
+               diff_tree_sha1(commit->parents->item->object.oid.hash,
+                              commit->object.oid.hash, "", options);
        else
-               diff_root_tree_sha1(commit->object.sha1, "", options);
+               diff_root_tree_sha1(commit->object.oid.hash, "", options);
        diffcore_std(options);
        return diff_flush_patch_id(options, sha1);
 }
diff --git a/path.c b/path.c
index f28ace2963bb3f0f76af0e363add22ed73a5a2c9..3cd155e27dddee6774d48522945c0615f6207d8c 100644 (file)
--- a/path.c
+++ b/path.c
@@ -740,6 +740,18 @@ int adjust_shared_perm(const char *path)
        return 0;
 }
 
+void safe_create_dir(const char *dir, int share)
+{
+       if (mkdir(dir, 0777) < 0) {
+               if (errno != EEXIST) {
+                       perror(dir);
+                       exit(1);
+               }
+       }
+       else if (share && adjust_shared_perm(dir))
+               die(_("Could not make %s writable by group"), dir);
+}
+
 static int have_same_root(const char *path1, const char *path2)
 {
        int is_abs1, is_abs2;
index c405f734c2050e2e4ad3469d087368a29a98d27e..9b24b3261592d856b9f4a5eeebdcd589e645c020 100644 (file)
@@ -19,7 +19,7 @@ int ppc_SHA1_Init(ppc_SHA_CTX *c);
 int ppc_SHA1_Update(ppc_SHA_CTX *c, const void *p, unsigned long n);
 int ppc_SHA1_Final(unsigned char *hash, ppc_SHA_CTX *c);
 
-#define git_SHA_CTX    ppc_SHA_CTX
-#define git_SHA1_Init  ppc_SHA1_Init
-#define git_SHA1_Update        ppc_SHA1_Update
-#define git_SHA1_Final ppc_SHA1_Final
+#define platform_SHA_CTX       ppc_SHA_CTX
+#define platform_SHA1_Init     ppc_SHA1_Init
+#define platform_SHA1_Update   ppc_SHA1_Update
+#define platform_SHA1_Final    ppc_SHA1_Final
index 151c2ae3128bb7c1a0ae13166539cb79e6edaeb8..92b2870a7eabb6a99ffbbcfc222d2ff1c1186fa0 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -543,9 +543,9 @@ static void add_merge_info(const struct pretty_print_context *pp,
                struct commit *p = parent->item;
                const char *hex = NULL;
                if (pp->abbrev)
-                       hex = find_unique_abbrev(p->object.sha1, pp->abbrev);
+                       hex = find_unique_abbrev(p->object.oid.hash, pp->abbrev);
                if (!hex)
-                       hex = sha1_to_hex(p->object.sha1);
+                       hex = oid_to_hex(&p->object.oid);
                parent = parent->next;
 
                strbuf_addf(sb, " %s", hex);
@@ -1119,12 +1119,12 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 
        /* these depend on the commit */
        if (!commit->object.parsed)
-               parse_object(commit->object.sha1);
+               parse_object(commit->object.oid.hash);
 
        switch (placeholder[0]) {
        case 'H':               /* commit hash */
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_COMMIT));
-               strbuf_addstr(sb, sha1_to_hex(commit->object.sha1));
+               strbuf_addstr(sb, oid_to_hex(&commit->object.oid));
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                return 1;
        case 'h':               /* abbreviated commit hash */
@@ -1133,18 +1133,18 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                        return 1;
                }
-               strbuf_addstr(sb, find_unique_abbrev(commit->object.sha1,
+               strbuf_addstr(sb, find_unique_abbrev(commit->object.oid.hash,
                                                     c->pretty_ctx->abbrev));
                strbuf_addstr(sb, diff_get_color(c->auto_color, DIFF_RESET));
                c->abbrev_commit_hash.len = sb->len - c->abbrev_commit_hash.off;
                return 1;
        case 'T':               /* tree hash */
-               strbuf_addstr(sb, sha1_to_hex(commit->tree->object.sha1));
+               strbuf_addstr(sb, oid_to_hex(&commit->tree->object.oid));
                return 1;
        case 't':               /* abbreviated tree hash */
                if (add_again(sb, &c->abbrev_tree_hash))
                        return 1;
-               strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.sha1,
+               strbuf_addstr(sb, find_unique_abbrev(commit->tree->object.oid.hash,
                                                     c->pretty_ctx->abbrev));
                c->abbrev_tree_hash.len = sb->len - c->abbrev_tree_hash.off;
                return 1;
@@ -1152,7 +1152,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                for (p = commit->parents; p; p = p->next) {
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
-                       strbuf_addstr(sb, sha1_to_hex(p->item->object.sha1));
+                       strbuf_addstr(sb, oid_to_hex(&p->item->object.oid));
                }
                return 1;
        case 'p':               /* abbreviated parent hashes */
@@ -1162,7 +1162,7 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
                        if (p != commit->parents)
                                strbuf_addch(sb, ' ');
                        strbuf_addstr(sb, find_unique_abbrev(
-                                       p->item->object.sha1,
+                                       p->item->object.oid.hash,
                                        c->pretty_ctx->abbrev));
                }
                c->abbrev_parent_hashes.len = sb->len -
index e205dd2f689ee5aeafe6ea66343fd01b85d2e354..7bef7f8dac644b49a2cb13d8d5154fd563d6e8a0 100644 (file)
@@ -372,7 +372,7 @@ static void grab_common_values(struct atom_value *val, int deref, struct object
                        v->s = xstrfmt("%lu", sz);
                }
                else if (deref)
-                       grab_objectname(name, obj->sha1, v);
+                       grab_objectname(name, obj->oid.hash, v);
        }
 }
 
@@ -394,7 +394,7 @@ static void grab_tag_values(struct atom_value *val, int deref, struct object *ob
                else if (!strcmp(name, "type") && tag->tagged)
                        v->s = typename(tag->tagged->type);
                else if (!strcmp(name, "object") && tag->tagged)
-                       v->s = xstrdup(sha1_to_hex(tag->tagged->sha1));
+                       v->s = xstrdup(oid_to_hex(&tag->tagged->oid));
        }
 }
 
@@ -412,7 +412,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                if (deref)
                        name++;
                if (!strcmp(name, "tree")) {
-                       v->s = xstrdup(sha1_to_hex(commit->tree->object.sha1));
+                       v->s = xstrdup(oid_to_hex(&commit->tree->object.oid));
                }
                else if (!strcmp(name, "numparent")) {
                        v->ul = commit_list_count(commit->parents);
@@ -425,7 +425,7 @@ static void grab_commit_values(struct atom_value *val, int deref, struct object
                                struct commit *parent = parents->item;
                                if (parents != commit->parents)
                                        strbuf_addch(&s, ' ');
-                               strbuf_addstr(&s, sha1_to_hex(parent->object.sha1));
+                               strbuf_addstr(&s, oid_to_hex(&parent->object.oid));
                        }
                        v->s = strbuf_detach(&s, NULL);
                }
@@ -992,7 +992,7 @@ static void populate_value(struct ref_array_item *ref)
         * If it is a tag object, see if we use a value that derefs
         * the object, and if we do grab the object it refers to.
         */
-       tagged = ((struct tag *)obj)->tagged->sha1;
+       tagged = ((struct tag *)obj)->tagged->oid.hash;
 
        /*
         * NEEDSWORK: This derefs tag only once, which
@@ -1049,7 +1049,7 @@ struct contains_stack {
 static int in_commit_list(const struct commit_list *want, struct commit *c)
 {
        for (; want; want = want->next)
-               if (!hashcmp(want->item->object.sha1, c->object.sha1))
+               if (!oidcmp(&want->item->object.oid, &c->object.oid))
                        return 1;
        return 0;
 }
@@ -1218,7 +1218,7 @@ static const unsigned char *match_points_at(struct sha1_array *points_at,
        if (!obj)
                die(_("malformed object at '%s'"), refname);
        if (obj->type == OBJ_TAG)
-               tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
+               tagged_sha1 = ((struct tag *)obj)->tagged->oid.hash;
        if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
                return tagged_sha1;
        return NULL;
diff --git a/refs.c b/refs.c
index bab92d773d1e06a29805564e48b96d500a6f9b9b..e2d34b253e4c9c4efcb30e88d11ccd3ce9e14deb 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -1,17 +1,13 @@
+/*
+ * The backend-independent part of the reference module.
+ */
+
 #include "cache.h"
 #include "lockfile.h"
 #include "refs.h"
+#include "refs/refs-internal.h"
 #include "object.h"
 #include "tag.h"
-#include "dir.h"
-#include "string-list.h"
-
-struct ref_lock {
-       char *ref_name;
-       char *orig_ref_name;
-       struct lock_file *lk;
-       struct object_id old_oid;
-};
 
 /*
  * How to handle various characters in refnames:
@@ -34,41 +30,6 @@ static unsigned char refname_disposition[256] = {
        0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 4, 4
 };
 
-/*
- * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
- * refs (i.e., because the reference is about to be deleted anyway).
- */
-#define REF_DELETING   0x02
-
-/*
- * Used as a flag in ref_update::flags when a loose ref is being
- * pruned.
- */
-#define REF_ISPRUNING  0x04
-
-/*
- * Used as a flag in ref_update::flags when the reference should be
- * updated to new_sha1.
- */
-#define REF_HAVE_NEW   0x08
-
-/*
- * Used as a flag in ref_update::flags when old_sha1 should be
- * checked.
- */
-#define REF_HAVE_OLD   0x10
-
-/*
- * Used as a flag in ref_update::flags when the lockfile needs to be
- * committed.
- */
-#define REF_NEEDS_COMMIT 0x20
-
-/*
- * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
- * value to ref_update::flags
- */
-
 /*
  * Try to read one refname component from the front of refname.
  * Return the length of the component found, or -1 if the component is
@@ -157,199 +118,7 @@ int check_refname_format(const char *refname, int flags)
        return 0;
 }
 
-struct ref_entry;
-
-/*
- * Information used (along with the information in ref_entry) to
- * describe a single cached reference.  This data structure only
- * occurs embedded in a union in struct ref_entry, and only when
- * (ref_entry->flag & REF_DIR) is zero.
- */
-struct ref_value {
-       /*
-        * The name of the object to which this reference resolves
-        * (which may be a tag object).  If REF_ISBROKEN, this is
-        * null.  If REF_ISSYMREF, then this is the name of the object
-        * referred to by the last reference in the symlink chain.
-        */
-       struct object_id oid;
-
-       /*
-        * If REF_KNOWS_PEELED, then this field holds the peeled value
-        * of this reference, or null if the reference is known not to
-        * be peelable.  See the documentation for peel_ref() for an
-        * exact definition of "peelable".
-        */
-       struct object_id peeled;
-};
-
-struct ref_cache;
-
-/*
- * Information used (along with the information in ref_entry) to
- * describe a level in the hierarchy of references.  This data
- * structure only occurs embedded in a union in struct ref_entry, and
- * only when (ref_entry.flag & REF_DIR) is set.  In that case,
- * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
- * in the directory have already been read:
- *
- *     (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
- *         or packed references, already read.
- *
- *     (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
- *         references that hasn't been read yet (nor has any of its
- *         subdirectories).
- *
- * Entries within a directory are stored within a growable array of
- * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <
- * sorted are sorted by their component name in strcmp() order and the
- * remaining entries are unsorted.
- *
- * Loose references are read lazily, one directory at a time.  When a
- * directory of loose references is read, then all of the references
- * in that directory are stored, and REF_INCOMPLETE stubs are created
- * for any subdirectories, but the subdirectories themselves are not
- * read.  The reading is triggered by get_ref_dir().
- */
-struct ref_dir {
-       int nr, alloc;
-
-       /*
-        * Entries with index 0 <= i < sorted are sorted by name.  New
-        * entries are appended to the list unsorted, and are sorted
-        * only when required; thus we avoid the need to sort the list
-        * after the addition of every reference.
-        */
-       int sorted;
-
-       /* A pointer to the ref_cache that contains this ref_dir. */
-       struct ref_cache *ref_cache;
-
-       struct ref_entry **entries;
-};
-
-/*
- * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
- * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
- * public values; see refs.h.
- */
-
-/*
- * The field ref_entry->u.value.peeled of this value entry contains
- * the correct peeled value for the reference, which might be
- * null_sha1 if the reference is not a tag or if it is broken.
- */
-#define REF_KNOWS_PEELED 0x10
-
-/* ref_entry represents a directory of references */
-#define REF_DIR 0x20
-
-/*
- * Entry has not yet been read from disk (used only for REF_DIR
- * entries representing loose references)
- */
-#define REF_INCOMPLETE 0x40
-
-/*
- * A ref_entry represents either a reference or a "subdirectory" of
- * references.
- *
- * Each directory in the reference namespace is represented by a
- * ref_entry with (flags & REF_DIR) set and containing a subdir member
- * that holds the entries in that directory that have been read so
- * far.  If (flags & REF_INCOMPLETE) is set, then the directory and
- * its subdirectories haven't been read yet.  REF_INCOMPLETE is only
- * used for loose reference directories.
- *
- * References are represented by a ref_entry with (flags & REF_DIR)
- * unset and a value member that describes the reference's value.  The
- * flag member is at the ref_entry level, but it is also needed to
- * interpret the contents of the value field (in other words, a
- * ref_value object is not very much use without the enclosing
- * ref_entry).
- *
- * Reference names cannot end with slash and directories' names are
- * always stored with a trailing slash (except for the top-level
- * directory, which is always denoted by "").  This has two nice
- * consequences: (1) when the entries in each subdir are sorted
- * lexicographically by name (as they usually are), the references in
- * a whole tree can be generated in lexicographic order by traversing
- * the tree in left-to-right, depth-first order; (2) the names of
- * references and subdirectories cannot conflict, and therefore the
- * presence of an empty subdirectory does not block the creation of a
- * similarly-named reference.  (The fact that reference names with the
- * same leading components can conflict *with each other* is a
- * separate issue that is regulated by verify_refname_available().)
- *
- * Please note that the name field contains the fully-qualified
- * reference (or subdirectory) name.  Space could be saved by only
- * storing the relative names.  But that would require the full names
- * to be generated on the fly when iterating in do_for_each_ref(), and
- * would break callback functions, who have always been able to assume
- * that the name strings that they are passed will not be freed during
- * the iteration.
- */
-struct ref_entry {
-       unsigned char flag; /* ISSYMREF? ISPACKED? */
-       union {
-               struct ref_value value; /* if not (flags&REF_DIR) */
-               struct ref_dir subdir; /* if (flags&REF_DIR) */
-       } u;
-       /*
-        * The full name of the reference (e.g., "refs/heads/master")
-        * or the full name of the directory with a trailing slash
-        * (e.g., "refs/heads/"):
-        */
-       char name[FLEX_ARRAY];
-};
-
-static void read_loose_refs(const char *dirname, struct ref_dir *dir);
-static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
-static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
-                                         const char *dirname, size_t len,
-                                         int incomplete);
-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
-
-static struct ref_dir *get_ref_dir(struct ref_entry *entry)
-{
-       struct ref_dir *dir;
-       assert(entry->flag & REF_DIR);
-       dir = &entry->u.subdir;
-       if (entry->flag & REF_INCOMPLETE) {
-               read_loose_refs(entry->name, dir);
-
-               /*
-                * Manually add refs/bisect, which, being
-                * per-worktree, might not appear in the directory
-                * listing for refs/ in the main repo.
-                */
-               if (!strcmp(entry->name, "refs/")) {
-                       int pos = search_ref_dir(dir, "refs/bisect/", 12);
-                       if (pos < 0) {
-                               struct ref_entry *child_entry;
-                               child_entry = create_dir_entry(dir->ref_cache,
-                                                              "refs/bisect/",
-                                                              12, 1);
-                               add_entry_to_dir(dir, child_entry);
-                               read_loose_refs("refs/bisect",
-                                               &child_entry->u.subdir);
-                       }
-               }
-               entry->flag &= ~REF_INCOMPLETE;
-       }
-       return dir;
-}
-
-/*
- * Check if a refname is safe.
- * For refs that start with "refs/" we consider it safe as long they do
- * not try to resolve to outside of refs/.
- *
- * For all other refs we only consider them safe iff they only contain
- * upper case characters and '_' (like "HEAD" AND "MERGE_HEAD", and not like
- * "config").
- */
-static int refname_is_safe(const char *refname)
+int refname_is_safe(const char *refname)
 {
        if (starts_with(refname, "refs/")) {
                char *buf;
@@ -373,1842 +142,165 @@ static int refname_is_safe(const char *refname)
        return 1;
 }
 
-static struct ref_entry *create_ref_entry(const char *refname,
-                                         const unsigned char *sha1, int flag,
-                                         int check_name)
+char *resolve_refdup(const char *refname, int resolve_flags,
+                    unsigned char *sha1, int *flags)
 {
-       int len;
-       struct ref_entry *ref;
-
-       if (check_name &&
-           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
-               die("Reference has invalid format: '%s'", refname);
-       len = strlen(refname) + 1;
-       ref = xmalloc(sizeof(struct ref_entry) + len);
-       hashcpy(ref->u.value.oid.hash, sha1);
-       oidclr(&ref->u.value.peeled);
-       memcpy(ref->name, refname, len);
-       ref->flag = flag;
-       return ref;
+       return xstrdup_or_null(resolve_ref_unsafe(refname, resolve_flags,
+                                                 sha1, flags));
 }
 
-static void clear_ref_dir(struct ref_dir *dir);
+/* The argument to filter_refs */
+struct ref_filter {
+       const char *pattern;
+       each_ref_fn *fn;
+       void *cb_data;
+};
 
-static void free_ref_entry(struct ref_entry *entry)
+int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
 {
-       if (entry->flag & REF_DIR) {
-               /*
-                * Do not use get_ref_dir() here, as that might
-                * trigger the reading of loose refs.
-                */
-               clear_ref_dir(&entry->u.subdir);
-       }
-       free(entry);
+       if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
+               return 0;
+       return -1;
 }
 
-/*
- * Add a ref_entry to the end of dir (unsorted).  Entry is always
- * stored directly in dir; no recursion into subdirectories is
- * done.
- */
-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
+int read_ref(const char *refname, unsigned char *sha1)
 {
-       ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
-       dir->entries[dir->nr++] = entry;
-       /* optimize for the case that entries are added in order */
-       if (dir->nr == 1 ||
-           (dir->nr == dir->sorted + 1 &&
-            strcmp(dir->entries[dir->nr - 2]->name,
-                   dir->entries[dir->nr - 1]->name) < 0))
-               dir->sorted = dir->nr;
+       return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
 }
 
-/*
- * Clear and free all entries in dir, recursively.
- */
-static void clear_ref_dir(struct ref_dir *dir)
+int ref_exists(const char *refname)
 {
-       int i;
-       for (i = 0; i < dir->nr; i++)
-               free_ref_entry(dir->entries[i]);
-       free(dir->entries);
-       dir->sorted = dir->nr = dir->alloc = 0;
-       dir->entries = NULL;
+       unsigned char sha1[20];
+       return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
 }
 
-/*
- * Create a struct ref_entry object for the specified dirname.
- * dirname is the name of the directory with a trailing slash (e.g.,
- * "refs/heads/") or "" for the top-level directory.
- */
-static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
-                                         const char *dirname, size_t len,
-                                         int incomplete)
+static int filter_refs(const char *refname, const struct object_id *oid,
+                          int flags, void *data)
 {
-       struct ref_entry *direntry;
-       direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
-       memcpy(direntry->name, dirname, len);
-       direntry->name[len] = '\0';
-       direntry->u.subdir.ref_cache = ref_cache;
-       direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
-       return direntry;
+       struct ref_filter *filter = (struct ref_filter *)data;
+
+       if (wildmatch(filter->pattern, refname, 0, NULL))
+               return 0;
+       return filter->fn(refname, oid, flags, filter->cb_data);
 }
 
-static int ref_entry_cmp(const void *a, const void *b)
+enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
 {
-       struct ref_entry *one = *(struct ref_entry **)a;
-       struct ref_entry *two = *(struct ref_entry **)b;
-       return strcmp(one->name, two->name);
-}
+       struct object *o = lookup_unknown_object(name);
 
-static void sort_ref_dir(struct ref_dir *dir);
+       if (o->type == OBJ_NONE) {
+               int type = sha1_object_info(name, NULL);
+               if (type < 0 || !object_as_type(o, type, 0))
+                       return PEEL_INVALID;
+       }
 
-struct string_slice {
-       size_t len;
-       const char *str;
-};
+       if (o->type != OBJ_TAG)
+               return PEEL_NON_TAG;
 
-static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
-{
-       const struct string_slice *key = key_;
-       const struct ref_entry *ent = *(const struct ref_entry * const *)ent_;
-       int cmp = strncmp(key->str, ent->name, key->len);
-       if (cmp)
-               return cmp;
-       return '\0' - (unsigned char)ent->name[key->len];
+       o = deref_tag_noverify(o);
+       if (!o)
+               return PEEL_INVALID;
+
+       hashcpy(sha1, o->oid.hash);
+       return PEEL_PEELED;
 }
 
-/*
- * Return the index of the entry with the given refname from the
- * ref_dir (non-recursively), sorting dir if necessary.  Return -1 if
- * no such entry is found.  dir must already be complete.
- */
-static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
-{
-       struct ref_entry **r;
-       struct string_slice key;
+struct warn_if_dangling_data {
+       FILE *fp;
+       const char *refname;
+       const struct string_list *refnames;
+       const char *msg_fmt;
+};
 
-       if (refname == NULL || !dir->nr)
-               return -1;
+static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
+                                  int flags, void *cb_data)
+{
+       struct warn_if_dangling_data *d = cb_data;
+       const char *resolves_to;
+       struct object_id junk;
 
-       sort_ref_dir(dir);
-       key.len = len;
-       key.str = refname;
-       r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
-                   ref_entry_cmp_sslice);
+       if (!(flags & REF_ISSYMREF))
+               return 0;
 
-       if (r == NULL)
-               return -1;
+       resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
+       if (!resolves_to
+           || (d->refname
+               ? strcmp(resolves_to, d->refname)
+               : !string_list_has_string(d->refnames, resolves_to))) {
+               return 0;
+       }
 
-       return r - dir->entries;
+       fprintf(d->fp, d->msg_fmt, refname);
+       fputc('\n', d->fp);
+       return 0;
 }
 
-/*
- * Search for a directory entry directly within dir (without
- * recursing).  Sort dir if necessary.  subdirname must be a directory
- * name (i.e., end in '/').  If mkdir is set, then create the
- * directory if it is missing; otherwise, return NULL if the desired
- * directory cannot be found.  dir must already be complete.
- */
-static struct ref_dir *search_for_subdir(struct ref_dir *dir,
-                                        const char *subdirname, size_t len,
-                                        int mkdir)
+void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
 {
-       int entry_index = search_ref_dir(dir, subdirname, len);
-       struct ref_entry *entry;
-       if (entry_index == -1) {
-               if (!mkdir)
-                       return NULL;
-               /*
-                * Since dir is complete, the absence of a subdir
-                * means that the subdir really doesn't exist;
-                * therefore, create an empty record for it but mark
-                * the record complete.
-                */
-               entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
-               add_entry_to_dir(dir, entry);
-       } else {
-               entry = dir->entries[entry_index];
-       }
-       return get_ref_dir(entry);
+       struct warn_if_dangling_data data;
+
+       data.fp = fp;
+       data.refname = refname;
+       data.refnames = NULL;
+       data.msg_fmt = msg_fmt;
+       for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-/*
- * If refname is a reference name, find the ref_dir within the dir
- * tree that should hold refname.  If refname is a directory name
- * (i.e., ends in '/'), then return that ref_dir itself.  dir must
- * represent the top-level directory and must already be complete.
- * Sort ref_dirs and recurse into subdirectories as necessary.  If
- * mkdir is set, then create any missing directories; otherwise,
- * return NULL if the desired directory cannot be found.
- */
-static struct ref_dir *find_containing_dir(struct ref_dir *dir,
-                                          const char *refname, int mkdir)
+void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
 {
-       const char *slash;
-       for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
-               size_t dirnamelen = slash - refname + 1;
-               struct ref_dir *subdir;
-               subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
-               if (!subdir) {
-                       dir = NULL;
-                       break;
-               }
-               dir = subdir;
-       }
+       struct warn_if_dangling_data data;
 
-       return dir;
+       data.fp = fp;
+       data.refname = NULL;
+       data.refnames = refnames;
+       data.msg_fmt = msg_fmt;
+       for_each_rawref(warn_if_dangling_symref, &data);
 }
 
-/*
- * Find the value entry with the given name in dir, sorting ref_dirs
- * and recursing into subdirectories as necessary.  If the name is not
- * found or it corresponds to a directory entry, return NULL.
- */
-static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
+int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 {
-       int entry_index;
-       struct ref_entry *entry;
-       dir = find_containing_dir(dir, refname, 0);
-       if (!dir)
-               return NULL;
-       entry_index = search_ref_dir(dir, refname, strlen(refname));
-       if (entry_index == -1)
-               return NULL;
-       entry = dir->entries[entry_index];
-       return (entry->flag & REF_DIR) ? NULL : entry;
+       return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
-/*
- * Remove the entry with the given name from dir, recursing into
- * subdirectories as necessary.  If refname is the name of a directory
- * (i.e., ends with '/'), then remove the directory and its contents.
- * If the removal was successful, return the number of entries
- * remaining in the directory entry that contained the deleted entry.
- * If the name was not found, return -1.  Please note that this
- * function only deletes the entry from the cache; it does not delete
- * it from the filesystem or ensure that other cache entries (which
- * might be symbolic references to the removed entry) are updated.
- * Nor does it remove any containing dir entries that might be made
- * empty by the removal.  dir must represent the top-level directory
- * and must already be complete.
- */
-static int remove_entry(struct ref_dir *dir, const char *refname)
+int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       int refname_len = strlen(refname);
-       int entry_index;
-       struct ref_entry *entry;
-       int is_dir = refname[refname_len - 1] == '/';
-       if (is_dir) {
-               /*
-                * refname represents a reference directory.  Remove
-                * the trailing slash; otherwise we will get the
-                * directory *representing* refname rather than the
-                * one *containing* it.
-                */
-               char *dirname = xmemdupz(refname, refname_len - 1);
-               dir = find_containing_dir(dir, dirname, 0);
-               free(dirname);
-       } else {
-               dir = find_containing_dir(dir, refname, 0);
-       }
-       if (!dir)
-               return -1;
-       entry_index = search_ref_dir(dir, refname, refname_len);
-       if (entry_index == -1)
-               return -1;
-       entry = dir->entries[entry_index];
-
-       memmove(&dir->entries[entry_index],
-               &dir->entries[entry_index + 1],
-               (dir->nr - entry_index - 1) * sizeof(*dir->entries)
-               );
-       dir->nr--;
-       if (dir->sorted > entry_index)
-               dir->sorted--;
-       free_ref_entry(entry);
-       return dir->nr;
+       return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
 }
 
-/*
- * Add a ref_entry to the ref_dir (unsorted), recursing into
- * subdirectories as necessary.  dir must represent the top-level
- * directory.  Return 0 on success.
- */
-static int add_ref(struct ref_dir *dir, struct ref_entry *ref)
+int for_each_branch_ref(each_ref_fn fn, void *cb_data)
 {
-       dir = find_containing_dir(dir, ref->name, 1);
-       if (!dir)
-               return -1;
-       add_entry_to_dir(dir, ref);
-       return 0;
+       return for_each_ref_in("refs/heads/", fn, cb_data);
 }
 
-/*
- * Emit a warning and return true iff ref1 and ref2 have the same name
- * and the same sha1.  Die if they have the same name but different
- * sha1s.
- */
-static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
+int for_each_branch_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       if (strcmp(ref1->name, ref2->name))
-               return 0;
-
-       /* Duplicate name; make sure that they don't conflict: */
-
-       if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
-               /* This is impossible by construction */
-               die("Reference directory conflict: %s", ref1->name);
-
-       if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
-               die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
-
-       warning("Duplicated ref: %s", ref1->name);
-       return 1;
+       return for_each_ref_in_submodule(submodule, "refs/heads/", fn, cb_data);
 }
 
-/*
- * Sort the entries in dir non-recursively (if they are not already
- * sorted) and remove any duplicate entries.
- */
-static void sort_ref_dir(struct ref_dir *dir)
+int for_each_remote_ref(each_ref_fn fn, void *cb_data)
 {
-       int i, j;
-       struct ref_entry *last = NULL;
-
-       /*
-        * This check also prevents passing a zero-length array to qsort(),
-        * which is a problem on some platforms.
-        */
-       if (dir->sorted == dir->nr)
-               return;
-
-       qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
-
-       /* Remove any duplicates: */
-       for (i = 0, j = 0; j < dir->nr; j++) {
-               struct ref_entry *entry = dir->entries[j];
-               if (last && is_dup_ref(last, entry))
-                       free_ref_entry(entry);
-               else
-                       last = dir->entries[i++] = entry;
-       }
-       dir->sorted = dir->nr = i;
+       return for_each_ref_in("refs/remotes/", fn, cb_data);
 }
 
-/* Include broken references in a do_for_each_ref*() iteration: */
-#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
-
-/*
- * Return true iff the reference described by entry can be resolved to
- * an object in the database.  Emit a warning if the referred-to
- * object does not exist.
- */
-static int ref_resolves_to_object(struct ref_entry *entry)
+int for_each_remote_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
-       if (entry->flag & REF_ISBROKEN)
-               return 0;
-       if (!has_sha1_file(entry->u.value.oid.hash)) {
-               error("%s does not point to a valid object!", entry->name);
-               return 0;
-       }
-       return 1;
+       return for_each_ref_in_submodule(submodule, "refs/remotes/", fn, cb_data);
 }
 
-/*
- * current_ref is a performance hack: when iterating over references
- * using the for_each_ref*() functions, current_ref is set to the
- * current reference's entry before calling the callback function.  If
- * the callback function calls peel_ref(), then peel_ref() first
- * checks whether the reference to be peeled is the current reference
- * (it usually is) and if so, returns that reference's peeled version
- * if it is available.  This avoids a refname lookup in a common case.
- */
-static struct ref_entry *current_ref;
+int head_ref_namespaced(each_ref_fn fn, void *cb_data)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret = 0;
+       struct object_id oid;
+       int flag;
 
-typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
+       strbuf_addf(&buf, "%sHEAD", get_git_namespace());
+       if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
+               ret = fn(buf.buf, &oid, flag, cb_data);
+       strbuf_release(&buf);
 
-struct ref_entry_cb {
-       const char *base;
-       int trim;
-       int flags;
-       each_ref_fn *fn;
-       void *cb_data;
-};
-
-/*
- * Handle one reference in a do_for_each_ref*()-style iteration,
- * calling an each_ref_fn for each entry.
- */
-static int do_one_ref(struct ref_entry *entry, void *cb_data)
-{
-       struct ref_entry_cb *data = cb_data;
-       struct ref_entry *old_current_ref;
-       int retval;
-
-       if (!starts_with(entry->name, data->base))
-               return 0;
-
-       if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
-             !ref_resolves_to_object(entry))
-               return 0;
-
-       /* Store the old value, in case this is a recursive call: */
-       old_current_ref = current_ref;
-       current_ref = entry;
-       retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
-                         entry->flag, data->cb_data);
-       current_ref = old_current_ref;
-       return retval;
-}
-
-/*
- * Call fn for each reference in dir that has index in the range
- * offset <= index < dir->nr.  Recurse into subdirectories that are in
- * that index range, sorting them before iterating.  This function
- * does not sort dir itself; it should be sorted beforehand.  fn is
- * called for all references, including broken ones.
- */
-static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
-                                   each_ref_entry_fn fn, void *cb_data)
-{
-       int i;
-       assert(dir->sorted == dir->nr);
-       for (i = offset; i < dir->nr; i++) {
-               struct ref_entry *entry = dir->entries[i];
-               int retval;
-               if (entry->flag & REF_DIR) {
-                       struct ref_dir *subdir = get_ref_dir(entry);
-                       sort_ref_dir(subdir);
-                       retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
-               } else {
-                       retval = fn(entry, cb_data);
-               }
-               if (retval)
-                       return retval;
-       }
-       return 0;
-}
-
-/*
- * Call fn for each reference in the union of dir1 and dir2, in order
- * by refname.  Recurse into subdirectories.  If a value entry appears
- * in both dir1 and dir2, then only process the version that is in
- * dir2.  The input dirs must already be sorted, but subdirs will be
- * sorted as needed.  fn is called for all references, including
- * broken ones.
- */
-static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
-                                    struct ref_dir *dir2,
-                                    each_ref_entry_fn fn, void *cb_data)
-{
-       int retval;
-       int i1 = 0, i2 = 0;
-
-       assert(dir1->sorted == dir1->nr);
-       assert(dir2->sorted == dir2->nr);
-       while (1) {
-               struct ref_entry *e1, *e2;
-               int cmp;
-               if (i1 == dir1->nr) {
-                       return do_for_each_entry_in_dir(dir2, i2, fn, cb_data);
-               }
-               if (i2 == dir2->nr) {
-                       return do_for_each_entry_in_dir(dir1, i1, fn, cb_data);
-               }
-               e1 = dir1->entries[i1];
-               e2 = dir2->entries[i2];
-               cmp = strcmp(e1->name, e2->name);
-               if (cmp == 0) {
-                       if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
-                               /* Both are directories; descend them in parallel. */
-                               struct ref_dir *subdir1 = get_ref_dir(e1);
-                               struct ref_dir *subdir2 = get_ref_dir(e2);
-                               sort_ref_dir(subdir1);
-                               sort_ref_dir(subdir2);
-                               retval = do_for_each_entry_in_dirs(
-                                               subdir1, subdir2, fn, cb_data);
-                               i1++;
-                               i2++;
-                       } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
-                               /* Both are references; ignore the one from dir1. */
-                               retval = fn(e2, cb_data);
-                               i1++;
-                               i2++;
-                       } else {
-                               die("conflict between reference and directory: %s",
-                                   e1->name);
-                       }
-               } else {
-                       struct ref_entry *e;
-                       if (cmp < 0) {
-                               e = e1;
-                               i1++;
-                       } else {
-                               e = e2;
-                               i2++;
-                       }
-                       if (e->flag & REF_DIR) {
-                               struct ref_dir *subdir = get_ref_dir(e);
-                               sort_ref_dir(subdir);
-                               retval = do_for_each_entry_in_dir(
-                                               subdir, 0, fn, cb_data);
-                       } else {
-                               retval = fn(e, cb_data);
-                       }
-               }
-               if (retval)
-                       return retval;
-       }
-}
-
-/*
- * Load all of the refs from the dir into our in-memory cache. The hard work
- * of loading loose refs is done by get_ref_dir(), so we just need to recurse
- * through all of the sub-directories. We do not even need to care about
- * sorting, as traversal order does not matter to us.
- */
-static void prime_ref_dir(struct ref_dir *dir)
-{
-       int i;
-       for (i = 0; i < dir->nr; i++) {
-               struct ref_entry *entry = dir->entries[i];
-               if (entry->flag & REF_DIR)
-                       prime_ref_dir(get_ref_dir(entry));
-       }
-}
-
-struct nonmatching_ref_data {
-       const struct string_list *skip;
-       const char *conflicting_refname;
-};
-
-static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata)
-{
-       struct nonmatching_ref_data *data = vdata;
-
-       if (data->skip && string_list_has_string(data->skip, entry->name))
-               return 0;
-
-       data->conflicting_refname = entry->name;
-       return 1;
-}
-
-/*
- * Return 0 if a reference named refname could be created without
- * conflicting with the name of an existing reference in dir.
- * 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., "refs/foo/bar" conflicts
- * with both "refs/foo" and with "refs/foo/bar/baz" but not with
- * "refs/foo/bar" or "refs/foo/barbados".
- *
- * extras and skip must be sorted.
- */
-static int verify_refname_available(const char *refname,
-                                   const struct string_list *extras,
-                                   const struct string_list *skip,
-                                   struct ref_dir *dir,
-                                   struct strbuf *err)
-{
-       const char *slash;
-       int pos;
-       struct strbuf dirname = STRBUF_INIT;
-       int ret = -1;
-
-       /*
-        * For the sake of comments in this function, suppose that
-        * refname is "refs/foo/bar".
-        */
-
-       assert(err);
-
-       strbuf_grow(&dirname, strlen(refname) + 1);
-       for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
-               /* Expand dirname to the new prefix, not including the trailing slash: */
-               strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
-
-               /*
-                * We are still at a leading dir of the refname (e.g.,
-                * "refs/foo"; if there is a reference with that name,
-                * it is a conflict, *unless* it is in skip.
-                */
-               if (dir) {
-                       pos = search_ref_dir(dir, dirname.buf, dirname.len);
-                       if (pos >= 0 &&
-                           (!skip || !string_list_has_string(skip, dirname.buf))) {
-                               /*
-                                * We found a reference whose name is
-                                * a proper prefix of refname; e.g.,
-                                * "refs/foo", and is not in skip.
-                                */
-                               strbuf_addf(err, "'%s' exists; cannot create '%s'",
-                                           dirname.buf, refname);
-                               goto cleanup;
-                       }
-               }
-
-               if (extras && string_list_has_string(extras, dirname.buf) &&
-                   (!skip || !string_list_has_string(skip, dirname.buf))) {
-                       strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
-                                   refname, dirname.buf);
-                       goto cleanup;
-               }
-
-               /*
-                * Otherwise, we can try to continue our search with
-                * the next component. So try to look up the
-                * directory, e.g., "refs/foo/". If we come up empty,
-                * we know there is nothing under this whole prefix,
-                * but even in that case we still have to continue the
-                * search for conflicts with extras.
-                */
-               strbuf_addch(&dirname, '/');
-               if (dir) {
-                       pos = search_ref_dir(dir, dirname.buf, dirname.len);
-                       if (pos < 0) {
-                               /*
-                                * There was no directory "refs/foo/",
-                                * so there is nothing under this
-                                * whole prefix. So there is no need
-                                * to continue looking for conflicting
-                                * references. But we need to continue
-                                * looking for conflicting extras.
-                                */
-                               dir = NULL;
-                       } else {
-                               dir = get_ref_dir(dir->entries[pos]);
-                       }
-               }
-       }
-
-       /*
-        * We are at the leaf of our refname (e.g., "refs/foo/bar").
-        * There is no point in searching for a reference with that
-        * name, because a refname isn't considered to conflict with
-        * itself. But we still need to check for references whose
-        * names are in the "refs/foo/bar/" namespace, because they
-        * *do* conflict.
-        */
-       strbuf_addstr(&dirname, refname + dirname.len);
-       strbuf_addch(&dirname, '/');
-
-       if (dir) {
-               pos = search_ref_dir(dir, dirname.buf, dirname.len);
-
-               if (pos >= 0) {
-                       /*
-                        * We found a directory named "$refname/"
-                        * (e.g., "refs/foo/bar/"). It is a problem
-                        * iff it contains any ref that is not in
-                        * "skip".
-                        */
-                       struct nonmatching_ref_data data;
-
-                       data.skip = skip;
-                       data.conflicting_refname = NULL;
-                       dir = get_ref_dir(dir->entries[pos]);
-                       sort_ref_dir(dir);
-                       if (do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data)) {
-                               strbuf_addf(err, "'%s' exists; cannot create '%s'",
-                                           data.conflicting_refname, refname);
-                               goto cleanup;
-                       }
-               }
-       }
-
-       if (extras) {
-               /*
-                * Check for entries in extras that start with
-                * "$refname/". We do that by looking for the place
-                * where "$refname/" would be inserted in extras. If
-                * there is an entry at that position that starts with
-                * "$refname/" and is not in skip, then we have a
-                * conflict.
-                */
-               for (pos = string_list_find_insert_index(extras, dirname.buf, 0);
-                    pos < extras->nr; pos++) {
-                       const char *extra_refname = extras->items[pos].string;
-
-                       if (!starts_with(extra_refname, dirname.buf))
-                               break;
-
-                       if (!skip || !string_list_has_string(skip, extra_refname)) {
-                               strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
-                                           refname, extra_refname);
-                               goto cleanup;
-                       }
-               }
-       }
-
-       /* No conflicts were found */
-       ret = 0;
-
-cleanup:
-       strbuf_release(&dirname);
-       return ret;
-}
-
-struct packed_ref_cache {
-       struct ref_entry *root;
-
-       /*
-        * Count of references to the data structure in this instance,
-        * including the pointer from ref_cache::packed if any.  The
-        * data will not be freed as long as the reference count is
-        * nonzero.
-        */
-       unsigned int referrers;
-
-       /*
-        * Iff the packed-refs file associated with this instance is
-        * currently locked for writing, this points at the associated
-        * lock (which is owned by somebody else).  The referrer count
-        * is also incremented when the file is locked and decremented
-        * when it is unlocked.
-        */
-       struct lock_file *lock;
-
-       /* The metadata from when this packed-refs cache was read */
-       struct stat_validity validity;
-};
-
-/*
- * Future: need to be in "struct repository"
- * when doing a full libification.
- */
-static struct ref_cache {
-       struct ref_cache *next;
-       struct ref_entry *loose;
-       struct packed_ref_cache *packed;
-       /*
-        * The submodule name, or "" for the main repo.  We allocate
-        * length 1 rather than FLEX_ARRAY so that the main ref_cache
-        * is initialized correctly.
-        */
-       char name[1];
-} ref_cache, *submodule_ref_caches;
-
-/* Lock used for the main packed-refs file: */
-static struct lock_file packlock;
-
-/*
- * Increment the reference count of *packed_refs.
- */
-static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
-       packed_refs->referrers++;
-}
-
-/*
- * Decrease the reference count of *packed_refs.  If it goes to zero,
- * free *packed_refs and return true; otherwise return false.
- */
-static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
-{
-       if (!--packed_refs->referrers) {
-               free_ref_entry(packed_refs->root);
-               stat_validity_clear(&packed_refs->validity);
-               free(packed_refs);
-               return 1;
-       } else {
-               return 0;
-       }
-}
-
-static void clear_packed_ref_cache(struct ref_cache *refs)
-{
-       if (refs->packed) {
-               struct packed_ref_cache *packed_refs = refs->packed;
-
-               if (packed_refs->lock)
-                       die("internal error: packed-ref cache cleared while locked");
-               refs->packed = NULL;
-               release_packed_ref_cache(packed_refs);
-       }
-}
-
-static void clear_loose_ref_cache(struct ref_cache *refs)
-{
-       if (refs->loose) {
-               free_ref_entry(refs->loose);
-               refs->loose = NULL;
-       }
-}
-
-static struct ref_cache *create_ref_cache(const char *submodule)
-{
-       int len;
-       struct ref_cache *refs;
-       if (!submodule)
-               submodule = "";
-       len = strlen(submodule) + 1;
-       refs = xcalloc(1, sizeof(struct ref_cache) + len);
-       memcpy(refs->name, submodule, len);
-       return refs;
-}
-
-/*
- * Return a pointer to a ref_cache for the specified submodule. For
- * the main repository, use submodule==NULL. The returned structure
- * will be allocated and initialized but not necessarily populated; it
- * should not be freed.
- */
-static struct ref_cache *get_ref_cache(const char *submodule)
-{
-       struct ref_cache *refs;
-
-       if (!submodule || !*submodule)
-               return &ref_cache;
-
-       for (refs = submodule_ref_caches; refs; refs = refs->next)
-               if (!strcmp(submodule, refs->name))
-                       return refs;
-
-       refs = create_ref_cache(submodule);
-       refs->next = submodule_ref_caches;
-       submodule_ref_caches = refs;
-       return refs;
-}
-
-/* The length of a peeled reference line in packed-refs, including EOL: */
-#define PEELED_LINE_LENGTH 42
-
-/*
- * The packed-refs header line that we write out.  Perhaps other
- * traits will be added later.  The trailing space is required.
- */
-static const char PACKED_REFS_HEADER[] =
-       "# pack-refs with: peeled fully-peeled \n";
-
-/*
- * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
- * Return a pointer to the refname within the line (null-terminated),
- * or NULL if there was a problem.
- */
-static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
-{
-       const char *ref;
-
-       /*
-        * 42: the answer to everything.
-        *
-        * In this case, it happens to be the answer to
-        *  40 (length of sha1 hex representation)
-        *  +1 (space in between hex and name)
-        *  +1 (newline at the end of the line)
-        */
-       if (line->len <= 42)
-               return NULL;
-
-       if (get_sha1_hex(line->buf, sha1) < 0)
-               return NULL;
-       if (!isspace(line->buf[40]))
-               return NULL;
-
-       ref = line->buf + 41;
-       if (isspace(*ref))
-               return NULL;
-
-       if (line->buf[line->len - 1] != '\n')
-               return NULL;
-       line->buf[--line->len] = 0;
-
-       return ref;
-}
-
-/*
- * Read f, which is a packed-refs file, into dir.
- *
- * A comment line of the form "# pack-refs with: " may contain zero or
- * more traits. We interpret the traits as follows:
- *
- *   No traits:
- *
- *      Probably no references are peeled. But if the file contains a
- *      peeled value for a reference, we will use it.
- *
- *   peeled:
- *
- *      References under "refs/tags/", if they *can* be peeled, *are*
- *      peeled in this file. References outside of "refs/tags/" are
- *      probably not peeled even if they could have been, but if we find
- *      a peeled value for such a reference we will use it.
- *
- *   fully-peeled:
- *
- *      All references in the file that can be peeled are peeled.
- *      Inversely (and this is more important), any references in the
- *      file for which no peeled value is recorded is not peelable. This
- *      trait should typically be written alongside "peeled" for
- *      compatibility with older clients, but we do not require it
- *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
- */
-static void read_packed_refs(FILE *f, struct ref_dir *dir)
-{
-       struct ref_entry *last = NULL;
-       struct strbuf line = STRBUF_INIT;
-       enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
-
-       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
-               unsigned char sha1[20];
-               const char *refname;
-               const char *traits;
-
-               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
-                       if (strstr(traits, " fully-peeled "))
-                               peeled = PEELED_FULLY;
-                       else if (strstr(traits, " peeled "))
-                               peeled = PEELED_TAGS;
-                       /* perhaps other traits later as well */
-                       continue;
-               }
-
-               refname = parse_ref_line(&line, sha1);
-               if (refname) {
-                       int flag = REF_ISPACKED;
-
-                       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
-                               if (!refname_is_safe(refname))
-                                       die("packed refname is dangerous: %s", refname);
-                               hashclr(sha1);
-                               flag |= REF_BAD_NAME | REF_ISBROKEN;
-                       }
-                       last = create_ref_entry(refname, sha1, flag, 0);
-                       if (peeled == PEELED_FULLY ||
-                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
-                               last->flag |= REF_KNOWS_PEELED;
-                       add_ref(dir, last);
-                       continue;
-               }
-               if (last &&
-                   line.buf[0] == '^' &&
-                   line.len == PEELED_LINE_LENGTH &&
-                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
-                   !get_sha1_hex(line.buf + 1, sha1)) {
-                       hashcpy(last->u.value.peeled.hash, sha1);
-                       /*
-                        * Regardless of what the file header said,
-                        * we definitely know the value of *this*
-                        * reference:
-                        */
-                       last->flag |= REF_KNOWS_PEELED;
-               }
-       }
-
-       strbuf_release(&line);
-}
-
-/*
- * Get the packed_ref_cache for the specified ref_cache, creating it
- * if necessary.
- */
-static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
-{
-       char *packed_refs_file;
-
-       if (*refs->name)
-               packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
-       else
-               packed_refs_file = git_pathdup("packed-refs");
-
-       if (refs->packed &&
-           !stat_validity_check(&refs->packed->validity, packed_refs_file))
-               clear_packed_ref_cache(refs);
-
-       if (!refs->packed) {
-               FILE *f;
-
-               refs->packed = xcalloc(1, sizeof(*refs->packed));
-               acquire_packed_ref_cache(refs->packed);
-               refs->packed->root = create_dir_entry(refs, "", 0, 0);
-               f = fopen(packed_refs_file, "r");
-               if (f) {
-                       stat_validity_update(&refs->packed->validity, fileno(f));
-                       read_packed_refs(f, get_ref_dir(refs->packed->root));
-                       fclose(f);
-               }
-       }
-       free(packed_refs_file);
-       return refs->packed;
-}
-
-static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
-{
-       return get_ref_dir(packed_ref_cache->root);
-}
-
-static struct ref_dir *get_packed_refs(struct ref_cache *refs)
-{
-       return get_packed_ref_dir(get_packed_ref_cache(refs));
-}
-
-/*
- * Add a reference to the in-memory packed reference cache.  This may
- * only be called while the packed-refs file is locked (see
- * lock_packed_refs()).  To actually write the packed-refs file, call
- * commit_packed_refs().
- */
-static void add_packed_ref(const char *refname, const unsigned char *sha1)
-{
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(&ref_cache);
-
-       if (!packed_ref_cache->lock)
-               die("internal error: packed refs not locked");
-       add_ref(get_packed_ref_dir(packed_ref_cache),
-               create_ref_entry(refname, sha1, REF_ISPACKED, 1));
-}
-
-/*
- * Read the loose references from the namespace dirname into dir
- * (without recursing).  dirname must end with '/'.  dir must be the
- * directory entry corresponding to dirname.
- */
-static void read_loose_refs(const char *dirname, struct ref_dir *dir)
-{
-       struct ref_cache *refs = dir->ref_cache;
-       DIR *d;
-       struct dirent *de;
-       int dirnamelen = strlen(dirname);
-       struct strbuf refname;
-       struct strbuf path = STRBUF_INIT;
-       size_t path_baselen;
-
-       if (*refs->name)
-               strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
-       else
-               strbuf_git_path(&path, "%s", dirname);
-       path_baselen = path.len;
-
-       d = opendir(path.buf);
-       if (!d) {
-               strbuf_release(&path);
-               return;
-       }
-
-       strbuf_init(&refname, dirnamelen + 257);
-       strbuf_add(&refname, dirname, dirnamelen);
-
-       while ((de = readdir(d)) != NULL) {
-               unsigned char sha1[20];
-               struct stat st;
-               int flag;
-
-               if (de->d_name[0] == '.')
-                       continue;
-               if (ends_with(de->d_name, ".lock"))
-                       continue;
-               strbuf_addstr(&refname, de->d_name);
-               strbuf_addstr(&path, de->d_name);
-               if (stat(path.buf, &st) < 0) {
-                       ; /* silently ignore */
-               } else if (S_ISDIR(st.st_mode)) {
-                       strbuf_addch(&refname, '/');
-                       add_entry_to_dir(dir,
-                                        create_dir_entry(refs, refname.buf,
-                                                         refname.len, 1));
-               } else {
-                       int read_ok;
-
-                       if (*refs->name) {
-                               hashclr(sha1);
-                               flag = 0;
-                               read_ok = !resolve_gitlink_ref(refs->name,
-                                                              refname.buf, sha1);
-                       } else {
-                               read_ok = !read_ref_full(refname.buf,
-                                                        RESOLVE_REF_READING,
-                                                        sha1, &flag);
-                       }
-
-                       if (!read_ok) {
-                               hashclr(sha1);
-                               flag |= REF_ISBROKEN;
-                       } else if (is_null_sha1(sha1)) {
-                               /*
-                                * It is so astronomically unlikely
-                                * that NULL_SHA1 is the SHA-1 of an
-                                * actual object that we consider its
-                                * appearance in a loose reference
-                                * file to be repo corruption
-                                * (probably due to a software bug).
-                                */
-                               flag |= REF_ISBROKEN;
-                       }
-
-                       if (check_refname_format(refname.buf,
-                                                REFNAME_ALLOW_ONELEVEL)) {
-                               if (!refname_is_safe(refname.buf))
-                                       die("loose refname is dangerous: %s", refname.buf);
-                               hashclr(sha1);
-                               flag |= REF_BAD_NAME | REF_ISBROKEN;
-                       }
-                       add_entry_to_dir(dir,
-                                        create_ref_entry(refname.buf, sha1, flag, 0));
-               }
-               strbuf_setlen(&refname, dirnamelen);
-               strbuf_setlen(&path, path_baselen);
-       }
-       strbuf_release(&refname);
-       strbuf_release(&path);
-       closedir(d);
-}
-
-static struct ref_dir *get_loose_refs(struct ref_cache *refs)
-{
-       if (!refs->loose) {
-               /*
-                * Mark the top-level directory complete because we
-                * are about to read the only subdirectory that can
-                * hold references:
-                */
-               refs->loose = create_dir_entry(refs, "", 0, 0);
-               /*
-                * Create an incomplete entry for "refs/":
-                */
-               add_entry_to_dir(get_ref_dir(refs->loose),
-                                create_dir_entry(refs, "refs/", 5, 1));
-       }
-       return get_ref_dir(refs->loose);
-}
-
-/* We allow "recursive" symbolic refs. Only within reason, though */
-#define MAXDEPTH 5
-#define MAXREFLEN (1024)
-
-/*
- * Called by resolve_gitlink_ref_recursive() after it failed to read
- * from the loose refs in ref_cache refs. Find <refname> in the
- * packed-refs file for the submodule.
- */
-static int resolve_gitlink_packed_ref(struct ref_cache *refs,
-                                     const char *refname, unsigned char *sha1)
-{
-       struct ref_entry *ref;
-       struct ref_dir *dir = get_packed_refs(refs);
-
-       ref = find_ref(dir, refname);
-       if (ref == NULL)
-               return -1;
-
-       hashcpy(sha1, ref->u.value.oid.hash);
-       return 0;
-}
-
-static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
-                                        const char *refname, unsigned char *sha1,
-                                        int recursion)
-{
-       int fd, len;
-       char buffer[128], *p;
-       char *path;
-
-       if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
-               return -1;
-       path = *refs->name
-               ? git_pathdup_submodule(refs->name, "%s", refname)
-               : git_pathdup("%s", refname);
-       fd = open(path, O_RDONLY);
-       free(path);
-       if (fd < 0)
-               return resolve_gitlink_packed_ref(refs, refname, sha1);
-
-       len = read(fd, buffer, sizeof(buffer)-1);
-       close(fd);
-       if (len < 0)
-               return -1;
-       while (len && isspace(buffer[len-1]))
-               len--;
-       buffer[len] = 0;
-
-       /* Was it a detached head or an old-fashioned symlink? */
-       if (!get_sha1_hex(buffer, sha1))
-               return 0;
-
-       /* Symref? */
-       if (strncmp(buffer, "ref:", 4))
-               return -1;
-       p = buffer + 4;
-       while (isspace(*p))
-               p++;
-
-       return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
-}
-
-int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
-{
-       int len = strlen(path), retval;
-       char *submodule;
-       struct ref_cache *refs;
-
-       while (len && path[len-1] == '/')
-               len--;
-       if (!len)
-               return -1;
-       submodule = xstrndup(path, len);
-       refs = get_ref_cache(submodule);
-       free(submodule);
-
-       retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
-       return retval;
-}
-
-/*
- * Return the ref_entry for the given refname from the packed
- * references.  If it does not exist, return NULL.
- */
-static struct ref_entry *get_packed_ref(const char *refname)
-{
-       return find_ref(get_packed_refs(&ref_cache), refname);
-}
-
-/*
- * A loose ref file doesn't exist; check for a packed ref.  The
- * options are forwarded from resolve_safe_unsafe().
- */
-static int resolve_missing_loose_ref(const char *refname,
-                                    int resolve_flags,
-                                    unsigned char *sha1,
-                                    int *flags)
-{
-       struct ref_entry *entry;
-
-       /*
-        * The loose reference file does not exist; check for a packed
-        * reference.
-        */
-       entry = get_packed_ref(refname);
-       if (entry) {
-               hashcpy(sha1, entry->u.value.oid.hash);
-               if (flags)
-                       *flags |= REF_ISPACKED;
-               return 0;
-       }
-       /* The reference is not a packed reference, either. */
-       if (resolve_flags & RESOLVE_REF_READING) {
-               errno = ENOENT;
-               return -1;
-       } else {
-               hashclr(sha1);
-               return 0;
-       }
-}
-
-/* This function needs to return a meaningful errno on failure */
-static const char *resolve_ref_1(const char *refname,
-                                int resolve_flags,
-                                unsigned char *sha1,
-                                int *flags,
-                                struct strbuf *sb_refname,
-                                struct strbuf *sb_path,
-                                struct strbuf *sb_contents)
-{
-       int depth = MAXDEPTH;
-       int bad_name = 0;
-
-       if (flags)
-               *flags = 0;
-
-       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
-               if (flags)
-                       *flags |= REF_BAD_NAME;
-
-               if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
-                   !refname_is_safe(refname)) {
-                       errno = EINVAL;
-                       return NULL;
-               }
-               /*
-                * dwim_ref() uses REF_ISBROKEN to distinguish between
-                * missing refs and refs that were present but invalid,
-                * to complain about the latter to stderr.
-                *
-                * We don't know whether the ref exists, so don't set
-                * REF_ISBROKEN yet.
-                */
-               bad_name = 1;
-       }
-       for (;;) {
-               const char *path;
-               struct stat st;
-               char *buf;
-               int fd;
-
-               if (--depth < 0) {
-                       errno = ELOOP;
-                       return NULL;
-               }
-
-               strbuf_reset(sb_path);
-               strbuf_git_path(sb_path, "%s", refname);
-               path = sb_path->buf;
-
-               /*
-                * We might have to loop back here to avoid a race
-                * condition: first we lstat() the file, then we try
-                * to read it as a link or as a file.  But if somebody
-                * changes the type of the file (file <-> directory
-                * <-> symlink) between the lstat() and reading, then
-                * we don't want to report that as an error but rather
-                * try again starting with the lstat().
-                */
-       stat_ref:
-               if (lstat(path, &st) < 0) {
-                       if (errno != ENOENT)
-                               return NULL;
-                       if (resolve_missing_loose_ref(refname, resolve_flags,
-                                                     sha1, flags))
-                               return NULL;
-                       if (bad_name) {
-                               hashclr(sha1);
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                       }
-                       return refname;
-               }
-
-               /* Follow "normalized" - ie "refs/.." symlinks by hand */
-               if (S_ISLNK(st.st_mode)) {
-                       strbuf_reset(sb_contents);
-                       if (strbuf_readlink(sb_contents, path, 0) < 0) {
-                               if (errno == ENOENT || errno == EINVAL)
-                                       /* inconsistent with lstat; retry */
-                                       goto stat_ref;
-                               else
-                                       return NULL;
-                       }
-                       if (starts_with(sb_contents->buf, "refs/") &&
-                           !check_refname_format(sb_contents->buf, 0)) {
-                               strbuf_swap(sb_refname, sb_contents);
-                               refname = sb_refname->buf;
-                               if (flags)
-                                       *flags |= REF_ISSYMREF;
-                               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
-                                       hashclr(sha1);
-                                       return refname;
-                               }
-                               continue;
-                       }
-               }
-
-               /* Is it a directory? */
-               if (S_ISDIR(st.st_mode)) {
-                       errno = EISDIR;
-                       return NULL;
-               }
-
-               /*
-                * Anything else, just open it and try to use it as
-                * a ref
-                */
-               fd = open(path, O_RDONLY);
-               if (fd < 0) {
-                       if (errno == ENOENT)
-                               /* inconsistent with lstat; retry */
-                               goto stat_ref;
-                       else
-                               return NULL;
-               }
-               strbuf_reset(sb_contents);
-               if (strbuf_read(sb_contents, fd, 256) < 0) {
-                       int save_errno = errno;
-                       close(fd);
-                       errno = save_errno;
-                       return NULL;
-               }
-               close(fd);
-               strbuf_rtrim(sb_contents);
-
-               /*
-                * Is it a symbolic ref?
-                */
-               if (!starts_with(sb_contents->buf, "ref:")) {
-                       /*
-                        * Please note that FETCH_HEAD has a second
-                        * line containing other data.
-                        */
-                       if (get_sha1_hex(sb_contents->buf, sha1) ||
-                           (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                               errno = EINVAL;
-                               return NULL;
-                       }
-                       if (bad_name) {
-                               hashclr(sha1);
-                               if (flags)
-                                       *flags |= REF_ISBROKEN;
-                       }
-                       return refname;
-               }
-               if (flags)
-                       *flags |= REF_ISSYMREF;
-               buf = sb_contents->buf + 4;
-               while (isspace(*buf))
-                       buf++;
-               strbuf_reset(sb_refname);
-               strbuf_addstr(sb_refname, buf);
-               refname = sb_refname->buf;
-               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
-                       hashclr(sha1);
-                       return refname;
-               }
-               if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
-                       if (flags)
-                               *flags |= REF_ISBROKEN;
-
-                       if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
-                           !refname_is_safe(buf)) {
-                               errno = EINVAL;
-                               return NULL;
-                       }
-                       bad_name = 1;
-               }
-       }
-}
-
-const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
-                              unsigned char *sha1, int *flags)
-{
-       static struct strbuf sb_refname = STRBUF_INIT;
-       struct strbuf sb_contents = STRBUF_INIT;
-       struct strbuf sb_path = STRBUF_INIT;
-       const char *ret;
-
-       ret = resolve_ref_1(refname, resolve_flags, sha1, flags,
-                           &sb_refname, &sb_path, &sb_contents);
-       strbuf_release(&sb_path);
-       strbuf_release(&sb_contents);
-       return ret;
-}
-
-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));
-}
-
-/* The argument to filter_refs */
-struct ref_filter {
-       const char *pattern;
-       each_ref_fn *fn;
-       void *cb_data;
-};
-
-int read_ref_full(const char *refname, int resolve_flags, unsigned char *sha1, int *flags)
-{
-       if (resolve_ref_unsafe(refname, resolve_flags, sha1, flags))
-               return 0;
-       return -1;
-}
-
-int read_ref(const char *refname, unsigned char *sha1)
-{
-       return read_ref_full(refname, RESOLVE_REF_READING, sha1, NULL);
-}
-
-int ref_exists(const char *refname)
-{
-       unsigned char sha1[20];
-       return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, sha1, NULL);
-}
-
-static int filter_refs(const char *refname, const struct object_id *oid,
-                          int flags, void *data)
-{
-       struct ref_filter *filter = (struct ref_filter *)data;
-
-       if (wildmatch(filter->pattern, refname, 0, NULL))
-               return 0;
-       return filter->fn(refname, oid, flags, filter->cb_data);
-}
-
-enum peel_status {
-       /* object was peeled successfully: */
-       PEEL_PEELED = 0,
-
-       /*
-        * object cannot be peeled because the named object (or an
-        * object referred to by a tag in the peel chain), does not
-        * exist.
-        */
-       PEEL_INVALID = -1,
-
-       /* object cannot be peeled because it is not a tag: */
-       PEEL_NON_TAG = -2,
-
-       /* ref_entry contains no peeled value because it is a symref: */
-       PEEL_IS_SYMREF = -3,
-
-       /*
-        * ref_entry cannot be peeled because it is broken (i.e., the
-        * symbolic reference cannot even be resolved to an object
-        * name):
-        */
-       PEEL_BROKEN = -4
-};
-
-/*
- * Peel the named object; i.e., if the object is a tag, resolve the
- * tag recursively until a non-tag is found.  If successful, store the
- * result to sha1 and return PEEL_PEELED.  If the object is not a tag
- * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
- * and leave sha1 unchanged.
- */
-static enum peel_status peel_object(const unsigned char *name, unsigned char *sha1)
-{
-       struct object *o = lookup_unknown_object(name);
-
-       if (o->type == OBJ_NONE) {
-               int type = sha1_object_info(name, NULL);
-               if (type < 0 || !object_as_type(o, type, 0))
-                       return PEEL_INVALID;
-       }
-
-       if (o->type != OBJ_TAG)
-               return PEEL_NON_TAG;
-
-       o = deref_tag_noverify(o);
-       if (!o)
-               return PEEL_INVALID;
-
-       hashcpy(sha1, o->sha1);
-       return PEEL_PEELED;
-}
-
-/*
- * Peel the entry (if possible) and return its new peel_status.  If
- * repeel is true, re-peel the entry even if there is an old peeled
- * value that is already stored in it.
- *
- * It is OK to call this function with a packed reference entry that
- * might be stale and might even refer to an object that has since
- * been garbage-collected.  In such a case, if the entry has
- * REF_KNOWS_PEELED then leave the status unchanged and return
- * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
- */
-static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
-{
-       enum peel_status status;
-
-       if (entry->flag & REF_KNOWS_PEELED) {
-               if (repeel) {
-                       entry->flag &= ~REF_KNOWS_PEELED;
-                       oidclr(&entry->u.value.peeled);
-               } else {
-                       return is_null_oid(&entry->u.value.peeled) ?
-                               PEEL_NON_TAG : PEEL_PEELED;
-               }
-       }
-       if (entry->flag & REF_ISBROKEN)
-               return PEEL_BROKEN;
-       if (entry->flag & REF_ISSYMREF)
-               return PEEL_IS_SYMREF;
-
-       status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
-       if (status == PEEL_PEELED || status == PEEL_NON_TAG)
-               entry->flag |= REF_KNOWS_PEELED;
-       return status;
-}
-
-int peel_ref(const char *refname, unsigned char *sha1)
-{
-       int flag;
-       unsigned char base[20];
-
-       if (current_ref && (current_ref->name == refname
-                           || !strcmp(current_ref->name, refname))) {
-               if (peel_entry(current_ref, 0))
-                       return -1;
-               hashcpy(sha1, current_ref->u.value.peeled.hash);
-               return 0;
-       }
-
-       if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
-               return -1;
-
-       /*
-        * If the reference is packed, read its ref_entry from the
-        * cache in the hope that we already know its peeled value.
-        * We only try this optimization on packed references because
-        * (a) forcing the filling of the loose reference cache could
-        * be expensive and (b) loose references anyway usually do not
-        * have REF_KNOWS_PEELED.
-        */
-       if (flag & REF_ISPACKED) {
-               struct ref_entry *r = get_packed_ref(refname);
-               if (r) {
-                       if (peel_entry(r, 0))
-                               return -1;
-                       hashcpy(sha1, r->u.value.peeled.hash);
-                       return 0;
-               }
-       }
-
-       return peel_object(base, sha1);
-}
-
-struct warn_if_dangling_data {
-       FILE *fp;
-       const char *refname;
-       const struct string_list *refnames;
-       const char *msg_fmt;
-};
-
-static int warn_if_dangling_symref(const char *refname, const struct object_id *oid,
-                                  int flags, void *cb_data)
-{
-       struct warn_if_dangling_data *d = cb_data;
-       const char *resolves_to;
-       struct object_id junk;
-
-       if (!(flags & REF_ISSYMREF))
-               return 0;
-
-       resolves_to = resolve_ref_unsafe(refname, 0, junk.hash, NULL);
-       if (!resolves_to
-           || (d->refname
-               ? strcmp(resolves_to, d->refname)
-               : !string_list_has_string(d->refnames, resolves_to))) {
-               return 0;
-       }
-
-       fprintf(d->fp, d->msg_fmt, refname);
-       fputc('\n', d->fp);
-       return 0;
-}
-
-void warn_dangling_symref(FILE *fp, const char *msg_fmt, const char *refname)
-{
-       struct warn_if_dangling_data data;
-
-       data.fp = fp;
-       data.refname = refname;
-       data.refnames = NULL;
-       data.msg_fmt = msg_fmt;
-       for_each_rawref(warn_if_dangling_symref, &data);
-}
-
-void warn_dangling_symrefs(FILE *fp, const char *msg_fmt, const struct string_list *refnames)
-{
-       struct warn_if_dangling_data data;
-
-       data.fp = fp;
-       data.refname = NULL;
-       data.refnames = refnames;
-       data.msg_fmt = msg_fmt;
-       for_each_rawref(warn_if_dangling_symref, &data);
-}
-
-/*
- * Call fn for each reference in the specified ref_cache, omitting
- * references not in the containing_dir of base.  fn is called for all
- * references, including broken ones.  If fn ever returns a non-zero
- * value, stop the iteration and return that value; otherwise, return
- * 0.
- */
-static int do_for_each_entry(struct ref_cache *refs, const char *base,
-                            each_ref_entry_fn fn, void *cb_data)
-{
-       struct packed_ref_cache *packed_ref_cache;
-       struct ref_dir *loose_dir;
-       struct ref_dir *packed_dir;
-       int retval = 0;
-
-       /*
-        * We must make sure that all loose refs are read before accessing the
-        * packed-refs file; this avoids a race condition in which loose refs
-        * are migrated to the packed-refs file by a simultaneous process, but
-        * our in-memory view is from before the migration. get_packed_ref_cache()
-        * takes care of making sure our view is up to date with what is on
-        * disk.
-        */
-       loose_dir = get_loose_refs(refs);
-       if (base && *base) {
-               loose_dir = find_containing_dir(loose_dir, base, 0);
-       }
-       if (loose_dir)
-               prime_ref_dir(loose_dir);
-
-       packed_ref_cache = get_packed_ref_cache(refs);
-       acquire_packed_ref_cache(packed_ref_cache);
-       packed_dir = get_packed_ref_dir(packed_ref_cache);
-       if (base && *base) {
-               packed_dir = find_containing_dir(packed_dir, base, 0);
-       }
-
-       if (packed_dir && loose_dir) {
-               sort_ref_dir(packed_dir);
-               sort_ref_dir(loose_dir);
-               retval = do_for_each_entry_in_dirs(
-                               packed_dir, loose_dir, fn, cb_data);
-       } else if (packed_dir) {
-               sort_ref_dir(packed_dir);
-               retval = do_for_each_entry_in_dir(
-                               packed_dir, 0, fn, cb_data);
-       } else if (loose_dir) {
-               sort_ref_dir(loose_dir);
-               retval = do_for_each_entry_in_dir(
-                               loose_dir, 0, fn, cb_data);
-       }
-
-       release_packed_ref_cache(packed_ref_cache);
-       return retval;
-}
-
-/*
- * Call fn for each reference in the specified ref_cache for which the
- * refname begins with base.  If trim is non-zero, then trim that many
- * characters off the beginning of each refname before passing the
- * refname to fn.  flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
- * broken references in the iteration.  If fn ever returns a non-zero
- * value, stop the iteration and return that value; otherwise, return
- * 0.
- */
-static int do_for_each_ref(struct ref_cache *refs, const char *base,
-                          each_ref_fn fn, int trim, int flags, void *cb_data)
-{
-       struct ref_entry_cb data;
-       data.base = base;
-       data.trim = trim;
-       data.flags = flags;
-       data.fn = fn;
-       data.cb_data = cb_data;
-
-       if (ref_paranoia < 0)
-               ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
-       if (ref_paranoia)
-               data.flags |= DO_FOR_EACH_INCLUDE_BROKEN;
-
-       return do_for_each_entry(refs, base, do_one_ref, &data);
-}
-
-static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       struct object_id oid;
-       int flag;
-
-       if (submodule) {
-               if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
-                       return fn("HEAD", &oid, 0, cb_data);
-
-               return 0;
-       }
-
-       if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
-               return fn("HEAD", &oid, flag, cb_data);
-
-       return 0;
-}
-
-int head_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_head_ref(NULL, fn, cb_data);
-}
-
-int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return do_head_ref(submodule, fn, cb_data);
-}
-
-int for_each_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data);
-}
-
-int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
-{
-       unsigned int flag = 0;
-
-       if (broken)
-               flag = DO_FOR_EACH_INCLUDE_BROKEN;
-       return do_for_each_ref(&ref_cache, 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)
-{
-       return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
-}
-
-int for_each_tag_ref(each_ref_fn fn, void *cb_data)
-{
-       return for_each_ref_in("refs/tags/", 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);
-}
-
-int for_each_branch_ref(each_ref_fn fn, void *cb_data)
-{
-       return for_each_ref_in("refs/heads/", 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);
-}
-
-int for_each_remote_ref(each_ref_fn fn, void *cb_data)
-{
-       return for_each_ref_in("refs/remotes/", 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);
-}
-
-int for_each_replace_ref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, git_replace_ref_base, fn,
-                              strlen(git_replace_ref_base), 0, cb_data);
-}
-
-int head_ref_namespaced(each_ref_fn fn, void *cb_data)
-{
-       struct strbuf buf = STRBUF_INIT;
-       int ret = 0;
-       struct object_id oid;
-       int flag;
-
-       strbuf_addf(&buf, "%sHEAD", get_git_namespace());
-       if (!read_ref_full(buf.buf, RESOLVE_REF_READING, oid.hash, &flag))
-               ret = fn(buf.buf, &oid, flag, cb_data);
-       strbuf_release(&buf);
-
-       return ret;
-}
-
-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(&ref_cache, buf.buf, fn, 0, 0, cb_data);
-       strbuf_release(&buf);
-       return ret;
-}
+       return ret;
+}
 
 int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
        const char *prefix, void *cb_data)
@@ -2244,12 +336,6 @@ int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data)
        return for_each_glob_ref_in(fn, pattern, NULL, cb_data);
 }
 
-int for_each_rawref(each_ref_fn fn, void *cb_data)
-{
-       return do_for_each_ref(&ref_cache, "", fn, 0,
-                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
-}
-
 const char *prettify_refname(const char *name)
 {
        return name + (
@@ -2283,57 +369,6 @@ int refname_match(const char *abbrev_name, const char *full_name)
        return 0;
 }
 
-static void unlock_ref(struct ref_lock *lock)
-{
-       /* Do not free lock->lk -- atexit() still looks at them */
-       if (lock->lk)
-               rollback_lock_file(lock->lk);
-       free(lock->ref_name);
-       free(lock->orig_ref_name);
-       free(lock);
-}
-
-/*
- * Verify that the reference locked by lock has the value old_sha1.
- * Fail if the reference doesn't exist and mustexist is set. Return 0
- * 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,
-                      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)) {
-               int save_errno = errno;
-               strbuf_addf(err, "can't verify ref %s", lock->ref_name);
-               errno = save_errno;
-               return -1;
-       }
-       if (hashcmp(lock->old_oid.hash, old_sha1)) {
-               strbuf_addf(err, "ref %s is at %s but expected %s",
-                           lock->ref_name,
-                           sha1_to_hex(lock->old_oid.hash),
-                           sha1_to_hex(old_sha1));
-               errno = EBUSY;
-               return -1;
-       }
-       return 0;
-}
-
-static int remove_empty_directories(struct strbuf *path)
-{
-       /*
-        * we want to create a file but there is a directory there;
-        * if that is an empty directory (or a directory that contains
-        * only empty directories), remove them.
-        */
-       return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
-}
-
 /*
  * *string and *len will only be substituted, and *string returned (for
  * later free()ing) if the string passed in is a magic short-hand form
@@ -2413,485 +448,11 @@ int dwim_log(const char *str, int len, unsigned char *sha1, char **log)
                        *log = xstrdup(it);
                        hashcpy(sha1, hash);
                }
-               if (!warn_ambiguous_refs)
-                       break;
-       }
-       free(last_branch);
-       return logs_found;
-}
-
-/*
- * Locks a ref returning the lock on success and NULL on failure.
- * On failure errno is set to something meaningful.
- */
-static struct ref_lock *lock_ref_sha1_basic(const char *refname,
-                                           const unsigned char *old_sha1,
-                                           const struct string_list *extras,
-                                           const struct string_list *skip,
-                                           unsigned int flags, int *type_p,
-                                           struct strbuf *err)
-{
-       struct strbuf ref_file = STRBUF_INIT;
-       struct strbuf orig_ref_file = STRBUF_INIT;
-       const char *orig_refname = refname;
-       struct ref_lock *lock;
-       int last_errno = 0;
-       int type, lflags;
-       int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
-       int resolve_flags = 0;
-       int attempts_remaining = 3;
-
-       assert(err);
-
-       lock = xcalloc(1, sizeof(struct ref_lock));
-
-       if (mustexist)
-               resolve_flags |= RESOLVE_REF_READING;
-       if (flags & REF_DELETING) {
-               resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
-               if (flags & REF_NODEREF)
-                       resolve_flags |= RESOLVE_REF_NO_RECURSE;
-       }
-
-       refname = resolve_ref_unsafe(refname, resolve_flags,
-                                    lock->old_oid.hash, &type);
-       if (!refname && errno == EISDIR) {
-               /*
-                * we are trying to lock foo but we used to
-                * have foo/bar which now does not exist;
-                * it is normal for the empty directory 'foo'
-                * to remain.
-                */
-               strbuf_git_path(&orig_ref_file, "%s", orig_refname);
-               if (remove_empty_directories(&orig_ref_file)) {
-                       last_errno = errno;
-                       if (!verify_refname_available(orig_refname, extras, skip,
-                                                     get_loose_refs(&ref_cache), err))
-                               strbuf_addf(err, "there are still refs under '%s'",
-                                           orig_refname);
-                       goto error_return;
-               }
-               refname = resolve_ref_unsafe(orig_refname, resolve_flags,
-                                            lock->old_oid.hash, &type);
-       }
-       if (type_p)
-           *type_p = type;
-       if (!refname) {
-               last_errno = errno;
-               if (last_errno != ENOTDIR ||
-                   !verify_refname_available(orig_refname, extras, skip,
-                                             get_loose_refs(&ref_cache), err))
-                       strbuf_addf(err, "unable to resolve reference %s: %s",
-                                   orig_refname, strerror(last_errno));
-
-               goto error_return;
-       }
-       /*
-        * If the ref did not exist and we are creating it, make sure
-        * there is no existing packed ref whose name begins with our
-        * refname, nor a packed ref whose name is a proper prefix of
-        * our refname.
-        */
-       if (is_null_oid(&lock->old_oid) &&
-           verify_refname_available(refname, extras, skip,
-                                    get_packed_refs(&ref_cache), err)) {
-               last_errno = ENOTDIR;
-               goto error_return;
-       }
-
-       lock->lk = xcalloc(1, sizeof(struct lock_file));
-
-       lflags = 0;
-       if (flags & REF_NODEREF) {
-               refname = orig_refname;
-               lflags |= LOCK_NO_DEREF;
-       }
-       lock->ref_name = xstrdup(refname);
-       lock->orig_ref_name = xstrdup(orig_refname);
-       strbuf_git_path(&ref_file, "%s", 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:
-               last_errno = errno;
-               strbuf_addf(err, "unable to create directory for %s",
-                           ref_file.buf);
-               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 (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
-               last_errno = errno;
-               goto error_return;
-       }
-       goto out;
-
- error_return:
-       unlock_ref(lock);
-       lock = NULL;
-
- out:
-       strbuf_release(&ref_file);
-       strbuf_release(&orig_ref_file);
-       errno = last_errno;
-       return lock;
-}
-
-/*
- * Write an entry to the packed-refs file for the specified refname.
- * If peeled is non-NULL, write it as the entry's peeled value.
- */
-static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1,
-                              unsigned char *peeled)
-{
-       fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
-       if (peeled)
-               fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
-}
-
-/*
- * An each_ref_entry_fn that writes the entry to a packed-refs file.
- */
-static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
-{
-       enum peel_status peel_status = peel_entry(entry, 0);
-
-       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
-               error("internal error: %s is not a valid packed reference!",
-                     entry->name);
-       write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
-                          peel_status == PEEL_PEELED ?
-                          entry->u.value.peeled.hash : NULL);
-       return 0;
-}
-
-/*
- * Lock the packed-refs file for writing. Flags is passed to
- * hold_lock_file_for_update(). Return 0 on success. On errors, set
- * errno appropriately and return a nonzero value.
- */
-static int lock_packed_refs(int flags)
-{
-       static int timeout_configured = 0;
-       static int timeout_value = 1000;
-
-       struct packed_ref_cache *packed_ref_cache;
-
-       if (!timeout_configured) {
-               git_config_get_int("core.packedrefstimeout", &timeout_value);
-               timeout_configured = 1;
-       }
-
-       if (hold_lock_file_for_update_timeout(
-                           &packlock, git_path("packed-refs"),
-                           flags, timeout_value) < 0)
-               return -1;
-       /*
-        * Get the current packed-refs while holding the lock.  If the
-        * packed-refs file has been modified since we last read it,
-        * this will automatically invalidate the cache and re-read
-        * the packed-refs file.
-        */
-       packed_ref_cache = get_packed_ref_cache(&ref_cache);
-       packed_ref_cache->lock = &packlock;
-       /* Increment the reference count to prevent it from being freed: */
-       acquire_packed_ref_cache(packed_ref_cache);
-       return 0;
-}
-
-/*
- * Write the current version of the packed refs cache from memory to
- * disk. The packed-refs file must already be locked for writing (see
- * lock_packed_refs()). Return zero on success. On errors, set errno
- * and return a nonzero value
- */
-static int commit_packed_refs(void)
-{
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(&ref_cache);
-       int error = 0;
-       int save_errno = 0;
-       FILE *out;
-
-       if (!packed_ref_cache->lock)
-               die("internal error: packed-refs not locked");
-
-       out = fdopen_lock_file(packed_ref_cache->lock, "w");
-       if (!out)
-               die_errno("unable to fdopen packed-refs descriptor");
-
-       fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
-       do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
-                                0, write_packed_entry_fn, out);
-
-       if (commit_lock_file(packed_ref_cache->lock)) {
-               save_errno = errno;
-               error = -1;
-       }
-       packed_ref_cache->lock = NULL;
-       release_packed_ref_cache(packed_ref_cache);
-       errno = save_errno;
-       return error;
-}
-
-/*
- * Rollback the lockfile for the packed-refs file, and discard the
- * in-memory packed reference cache.  (The packed-refs file will be
- * read anew if it is needed again after this function is called.)
- */
-static void rollback_packed_refs(void)
-{
-       struct packed_ref_cache *packed_ref_cache =
-               get_packed_ref_cache(&ref_cache);
-
-       if (!packed_ref_cache->lock)
-               die("internal error: packed-refs not locked");
-       rollback_lock_file(packed_ref_cache->lock);
-       packed_ref_cache->lock = NULL;
-       release_packed_ref_cache(packed_ref_cache);
-       clear_packed_ref_cache(&ref_cache);
-}
-
-struct ref_to_prune {
-       struct ref_to_prune *next;
-       unsigned char sha1[20];
-       char name[FLEX_ARRAY];
-};
-
-struct pack_refs_cb_data {
-       unsigned int flags;
-       struct ref_dir *packed_refs;
-       struct ref_to_prune *ref_to_prune;
-};
-
-static int is_per_worktree_ref(const char *refname);
-
-/*
- * An each_ref_entry_fn that is run over loose references only.  If
- * the loose reference can be packed, add an entry in the packed ref
- * cache.  If the reference should be pruned, also add it to
- * ref_to_prune in the pack_refs_cb_data.
- */
-static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
-{
-       struct pack_refs_cb_data *cb = cb_data;
-       enum peel_status peel_status;
-       struct ref_entry *packed_entry;
-       int is_tag_ref = starts_with(entry->name, "refs/tags/");
-
-       /* Do not pack per-worktree refs: */
-       if (is_per_worktree_ref(entry->name))
-               return 0;
-
-       /* ALWAYS pack tags */
-       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
-               return 0;
-
-       /* Do not pack symbolic or broken refs: */
-       if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
-               return 0;
-
-       /* Add a packed ref cache entry equivalent to the loose entry. */
-       peel_status = peel_entry(entry, 1);
-       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
-               die("internal error peeling reference %s (%s)",
-                   entry->name, oid_to_hex(&entry->u.value.oid));
-       packed_entry = find_ref(cb->packed_refs, entry->name);
-       if (packed_entry) {
-               /* Overwrite existing packed entry with info from loose entry */
-               packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
-               oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
-       } else {
-               packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
-                                               REF_ISPACKED | REF_KNOWS_PEELED, 0);
-               add_ref(cb->packed_refs, packed_entry);
-       }
-       oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
-
-       /* Schedule the loose reference for pruning if requested. */
-       if ((cb->flags & PACK_REFS_PRUNE)) {
-               int namelen = strlen(entry->name) + 1;
-               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
-               hashcpy(n->sha1, entry->u.value.oid.hash);
-               memcpy(n->name, entry->name, namelen); /* includes NUL */
-               n->next = cb->ref_to_prune;
-               cb->ref_to_prune = n;
-       }
-       return 0;
-}
-
-/*
- * Remove empty parents, but spare refs/ and immediate subdirs.
- * Note: munges *name.
- */
-static void try_remove_empty_parents(char *name)
-{
-       char *p, *q;
-       int i;
-       p = name;
-       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
-               while (*p && *p != '/')
-                       p++;
-               /* tolerate duplicate slashes; see check_refname_format() */
-               while (*p == '/')
-                       p++;
-       }
-       for (q = p; *q; q++)
-               ;
-       while (1) {
-               while (q > p && *q != '/')
-                       q--;
-               while (q > p && *(q-1) == '/')
-                       q--;
-               if (q == p)
-                       break;
-               *q = '\0';
-               if (rmdir(git_path("%s", name)))
-                       break;
-       }
-}
-
-/* make sure nobody touched the ref, and unlink */
-static void prune_ref(struct ref_to_prune *r)
-{
-       struct ref_transaction *transaction;
-       struct strbuf err = STRBUF_INIT;
-
-       if (check_refname_format(r->name, 0))
-               return;
-
-       transaction = ref_transaction_begin(&err);
-       if (!transaction ||
-           ref_transaction_delete(transaction, r->name, r->sha1,
-                                  REF_ISPRUNING, NULL, &err) ||
-           ref_transaction_commit(transaction, &err)) {
-               ref_transaction_free(transaction);
-               error("%s", err.buf);
-               strbuf_release(&err);
-               return;
-       }
-       ref_transaction_free(transaction);
-       strbuf_release(&err);
-       try_remove_empty_parents(r->name);
-}
-
-static void prune_refs(struct ref_to_prune *r)
-{
-       while (r) {
-               prune_ref(r);
-               r = r->next;
-       }
-}
-
-int pack_refs(unsigned int flags)
-{
-       struct pack_refs_cb_data cbdata;
-
-       memset(&cbdata, 0, sizeof(cbdata));
-       cbdata.flags = flags;
-
-       lock_packed_refs(LOCK_DIE_ON_ERROR);
-       cbdata.packed_refs = get_packed_refs(&ref_cache);
-
-       do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
-                                pack_if_possible_fn, &cbdata);
-
-       if (commit_packed_refs())
-               die_errno("unable to overwrite old ref-pack file");
-
-       prune_refs(cbdata.ref_to_prune);
-       return 0;
-}
-
-/*
- * Rewrite the packed-refs file, omitting any refs listed in
- * 'refnames'. On error, leave packed-refs unchanged, write an error
- * message to 'err', and return a nonzero value.
- *
- * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
- */
-static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
-{
-       struct ref_dir *packed;
-       struct string_list_item *refname;
-       int ret, needs_repacking = 0, removed = 0;
-
-       assert(err);
-
-       /* Look for a packed ref */
-       for_each_string_list_item(refname, refnames) {
-               if (get_packed_ref(refname->string)) {
-                       needs_repacking = 1;
-                       break;
-               }
-       }
-
-       /* Avoid locking if we have nothing to do */
-       if (!needs_repacking)
-               return 0; /* no refname exists in packed refs */
-
-       if (lock_packed_refs(0)) {
-               unable_to_lock_message(git_path("packed-refs"), errno, err);
-               return -1;
-       }
-       packed = get_packed_refs(&ref_cache);
-
-       /* Remove refnames from the cache */
-       for_each_string_list_item(refname, refnames)
-               if (remove_entry(packed, refname->string) != -1)
-                       removed = 1;
-       if (!removed) {
-               /*
-                * All packed entries disappeared while we were
-                * acquiring the lock.
-                */
-               rollback_packed_refs();
-               return 0;
-       }
-
-       /* Write what remains */
-       ret = commit_packed_refs();
-       if (ret)
-               strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
-                           strerror(errno));
-       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;
+               if (!warn_ambiguous_refs)
+                       break;
        }
-       return 0;
+       free(last_branch);
+       return logs_found;
 }
 
 static int is_per_worktree_ref(const char *refname)
@@ -2979,623 +540,82 @@ static int delete_pseudoref(const char *pseudoref, const unsigned char *old_sha1
                fd = hold_lock_file_for_update(&lock, filename,
                                               LOCK_DIE_ON_ERROR);
                if (fd < 0)
-                       die_errno(_("Could not open '%s' for writing"), filename);
-               if (read_ref(pseudoref, actual_old_sha1))
-                       die("could not read ref '%s'", pseudoref);
-               if (hashcmp(actual_old_sha1, old_sha1)) {
-                       warning("Unexpected sha1 when deleting %s", pseudoref);
-                       rollback_lock_file(&lock);
-                       return -1;
-               }
-
-               unlink(filename);
-               rollback_lock_file(&lock);
-       } else {
-               unlink(filename);
-       }
-
-       return 0;
-}
-
-int delete_ref(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)
-               return delete_pseudoref(refname, old_sha1);
-
-       transaction = ref_transaction_begin(&err);
-       if (!transaction ||
-           ref_transaction_delete(transaction, refname, old_sha1,
-                                  flags, NULL, &err) ||
-           ref_transaction_commit(transaction, &err)) {
-               error("%s", err.buf);
-               ref_transaction_free(transaction);
-               strbuf_release(&err);
-               return 1;
-       }
-       ref_transaction_free(transaction);
-       strbuf_release(&err);
-       return 0;
-}
-
-int delete_refs(struct string_list *refnames)
-{
-       struct strbuf err = STRBUF_INIT;
-       int i, result = 0;
-
-       if (!refnames->nr)
-               return 0;
-
-       result = repack_without_refs(refnames, &err);
-       if (result) {
-               /*
-                * If we failed to rewrite the packed-refs file, then
-                * it is unsafe to try to remove loose refs, because
-                * doing so might expose an obsolete packed value for
-                * a reference that might even point at an object that
-                * has been garbage collected.
-                */
-               if (refnames->nr == 1)
-                       error(_("could not delete reference %s: %s"),
-                             refnames->items[0].string, err.buf);
-               else
-                       error(_("could not delete references: %s"), err.buf);
-
-               goto out;
-       }
-
-       for (i = 0; i < refnames->nr; i++) {
-               const char *refname = refnames->items[i].string;
-
-               if (delete_ref(refname, NULL, 0))
-                       result |= error(_("could not remove reference %s"), refname);
-       }
-
-out:
-       strbuf_release(&err);
-       return result;
-}
-
-/*
- * People using contrib's git-new-workdir have .git/logs/refs ->
- * /some/other/path/.git/logs/refs, and that may live on another device.
- *
- * 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"
-
-static int rename_tmp_log(const char *newrefname)
-{
-       int attempts_remaining = 4;
-       struct strbuf path = STRBUF_INIT;
-       int ret = -1;
-
- 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(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;
-               }
-       }
-       ret = 0;
-out:
-       strbuf_release(&path);
-       return ret;
-}
-
-static int rename_ref_available(const char *oldname, const char *newname)
-{
-       struct string_list skip = STRING_LIST_INIT_NODUP;
-       struct strbuf err = STRBUF_INIT;
-       int ret;
-
-       string_list_insert(&skip, oldname);
-       ret = !verify_refname_available(newname, NULL, &skip,
-                                       get_packed_refs(&ref_cache), &err)
-               && !verify_refname_available(newname, NULL, &skip,
-                                            get_loose_refs(&ref_cache), &err);
-       if (!ret)
-               error("%s", err.buf);
-
-       string_list_clear(&skip, 0);
-       strbuf_release(&err);
-       return ret;
-}
-
-static int write_ref_to_lockfile(struct ref_lock *lock,
-                                const unsigned char *sha1, struct strbuf *err);
-static int commit_ref_update(struct ref_lock *lock,
-                            const unsigned char *sha1, const char *logmsg,
-                            int flags, struct strbuf *err);
-
-int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
-{
-       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);
-       const char *symref = NULL;
-       struct strbuf err = STRBUF_INIT;
-
-       if (log && S_ISLNK(loginfo.st_mode))
-               return error("reflog for %s is a symlink", oldrefname);
-
-       symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
-                                   orig_sha1, &flag);
-       if (flag & REF_ISSYMREF)
-               return error("refname %s is a symbolic ref, renaming it is not supported",
-                       oldrefname);
-       if (!symref)
-               return error("refname %s not found", oldrefname);
-
-       if (!rename_ref_available(oldrefname, newrefname))
-               return 1;
-
-       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 (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
-               error("unable to delete old %s", oldrefname);
-               goto rollback;
-       }
-
-       if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
-           delete_ref(newrefname, sha1, REF_NODEREF)) {
-               if (errno==EISDIR) {
-                       struct strbuf path = STRBUF_INIT;
-                       int result;
-
-                       strbuf_git_path(&path, "%s", newrefname);
-                       result = remove_empty_directories(&path);
-                       strbuf_release(&path);
-
-                       if (result) {
-                               error("Directory not empty: %s", newrefname);
-                               goto rollback;
-                       }
-               } else {
-                       error("unable to delete existing %s", newrefname);
-                       goto rollback;
-               }
-       }
-
-       if (log && rename_tmp_log(newrefname))
-               goto rollback;
-
-       logmoved = log;
-
-       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err);
-       if (!lock) {
-               error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
-               strbuf_release(&err);
-               goto rollback;
-       }
-       hashcpy(lock->old_oid.hash, orig_sha1);
-
-       if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-           commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
-               error("unable to write current sha1 into %s: %s", newrefname, err.buf);
-               strbuf_release(&err);
-               goto rollback;
-       }
-
-       return 0;
-
- rollback:
-       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err);
-       if (!lock) {
-               error("unable to lock %s for rollback: %s", oldrefname, err.buf);
-               strbuf_release(&err);
-               goto rollbacklog;
-       }
-
-       flag = log_all_ref_updates;
-       log_all_ref_updates = 0;
-       if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
-           commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
-               error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
-               strbuf_release(&err);
-       }
-       log_all_ref_updates = flag;
-
- rollbacklog:
-       if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
-               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",
-                       oldrefname, strerror(errno));
-
-       return 1;
-}
-
-static int close_ref(struct ref_lock *lock)
-{
-       if (close_lock_file(lock->lk))
-               return -1;
-       return 0;
-}
-
-static int commit_ref(struct ref_lock *lock)
-{
-       if (commit_lock_file(lock->lk))
-               return -1;
-       return 0;
-}
-
-/*
- * copy the reflog message msg to buf, which has been allocated sufficiently
- * large, while cleaning up the whitespaces.  Especially, convert LF to space,
- * because reflog file is one line per entry.
- */
-static int copy_msg(char *buf, const char *msg)
-{
-       char *cp = buf;
-       char c;
-       int wasspace = 1;
-
-       *cp++ = '\t';
-       while ((c = *msg++)) {
-               if (wasspace && isspace(c))
-                       continue;
-               wasspace = isspace(c);
-               if (wasspace)
-                       c = ' ';
-               *cp++ = c;
-       }
-       while (buf < cp && isspace(cp[-1]))
-               cp--;
-       *cp++ = '\n';
-       return cp - buf;
-}
-
-static int should_autocreate_reflog(const char *refname)
-{
-       if (!log_all_ref_updates)
-               return 0;
-       return starts_with(refname, "refs/heads/") ||
-               starts_with(refname, "refs/remotes/") ||
-               starts_with(refname, "refs/notes/") ||
-               !strcmp(refname, "HEAD");
-}
-
-/*
- * 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.
- */
-static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
-{
-       int logfd, oflags = O_APPEND | O_WRONLY;
-
-       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;
-       }
-
-       logfd = open(logfile->buf, oflags, 0666);
-       if (logfd < 0) {
-               if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
-                       return 0;
-
-               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);
-               }
-
-               if (logfd < 0) {
-                       strbuf_addf(err, "unable to append to %s: %s",
-                                   logfile->buf, strerror(errno));
-                       return -1;
-               }
-       }
-
-       adjust_shared_perm(logfile->buf);
-       close(logfd);
-       return 0;
-}
-
-
-int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
-{
-       int ret;
-       struct strbuf sb = STRBUF_INIT;
-
-       ret = log_ref_setup(refname, &sb, err, force_create);
-       strbuf_release(&sb);
-       return ret;
-}
-
-static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
-                           const unsigned char *new_sha1,
-                           const char *committer, const char *msg)
-{
-       int msglen, written;
-       unsigned maxlen, len;
-       char *logrec;
-
-       msglen = msg ? strlen(msg) : 0;
-       maxlen = strlen(committer) + msglen + 100;
-       logrec = xmalloc(maxlen);
-       len = xsnprintf(logrec, maxlen, "%s %s %s\n",
-                       sha1_to_hex(old_sha1),
-                       sha1_to_hex(new_sha1),
-                       committer);
-       if (msglen)
-               len += copy_msg(logrec + len - 1, msg) - 1;
-
-       written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
-       free(logrec);
-       if (written != len)
-               return -1;
-
-       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)
-{
-       int logfd, result, oflags = O_APPEND | O_WRONLY;
-
-       if (log_all_ref_updates < 0)
-               log_all_ref_updates = !is_bare_repository();
-
-       result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
-
-       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));
-               close(logfd);
-               return -1;
-       }
-       if (close(logfd)) {
-               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
-                           strerror(errno));
-               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)
-{
-       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;
-}
-
-int is_branch(const char *refname)
-{
-       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
-}
-
-/*
- * Write sha1 into the open lockfile, then close the lockfile. On
- * errors, rollback the lockfile, fill in *err and
- * return -1.
- */
-static int write_ref_to_lockfile(struct ref_lock *lock,
-                                const unsigned char *sha1, struct strbuf *err)
-{
-       static char term = '\n';
-       struct object *o;
-       int fd;
-
-       o = parse_object(sha1);
-       if (!o) {
-               strbuf_addf(err,
-                           "Trying to write ref %s with nonexistent object %s",
-                           lock->ref_name, sha1_to_hex(sha1));
-               unlock_ref(lock);
-               return -1;
-       }
-       if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
-               strbuf_addf(err,
-                           "Trying to write non-commit object %s to branch %s",
-                           sha1_to_hex(sha1), lock->ref_name);
-               unlock_ref(lock);
-               return -1;
-       }
-       fd = get_lock_file_fd(lock->lk);
-       if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
-           write_in_full(fd, &term, 1) != 1 ||
-           close_ref(lock) < 0) {
-               strbuf_addf(err,
-                           "Couldn't write %s", get_lock_file_path(lock->lk));
-               unlock_ref(lock);
-               return -1;
-       }
-       return 0;
-}
-
-/*
- * Commit a change to a loose reference that has already been written
- * to the loose reference lockfile. Also update the reflogs if
- * necessary, using the specified lockmsg (which can be NULL).
- */
-static int commit_ref_update(struct ref_lock *lock,
-                            const unsigned char *sha1, const char *logmsg,
-                            int flags, struct strbuf *err)
-{
-       clear_loose_ref_cache(&ref_cache);
-       if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
-           (strcmp(lock->ref_name, lock->orig_ref_name) &&
-            log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
-               char *old_msg = strbuf_detach(err, NULL);
-               strbuf_addf(err, "Cannot update the ref '%s': %s",
-                           lock->ref_name, old_msg);
-               free(old_msg);
-               unlock_ref(lock);
-               return -1;
-       }
-       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
-               /*
-                * Special hack: If a branch is updated directly and HEAD
-                * points to it (may happen on the remote side of a push
-                * for example) then logically the HEAD reflog should be
-                * updated too.
-                * A generic solution implies reverse symref information,
-                * but finding all symrefs pointing to the given branch
-                * would be rather costly for this rare event (the direct
-                * update of a branch) to be worth it.  So let's cheat and
-                * check with HEAD only which should cover 99% of all usage
-                * scenarios (even 100% of the default ones).
-                */
-               unsigned char head_sha1[20];
-               int head_flag;
-               const char *head_ref;
-               head_ref = resolve_ref_unsafe("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)) {
-                               error("%s", log_err.buf);
-                               strbuf_release(&log_err);
-                       }
+                       die_errno(_("Could not open '%s' for writing"), filename);
+               if (read_ref(pseudoref, actual_old_sha1))
+                       die("could not read ref '%s'", pseudoref);
+               if (hashcmp(actual_old_sha1, old_sha1)) {
+                       warning("Unexpected sha1 when deleting %s", pseudoref);
+                       rollback_lock_file(&lock);
+                       return -1;
                }
-       }
-       if (commit_ref(lock)) {
-               error("Couldn't set %s", lock->ref_name);
-               unlock_ref(lock);
-               return -1;
+
+               unlink(filename);
+               rollback_lock_file(&lock);
+       } else {
+               unlink(filename);
        }
 
-       unlock_ref(lock);
        return 0;
 }
 
-int create_symref(const char *ref_target, const char *refs_heads_master,
-                 const char *logmsg)
+int delete_ref(const char *refname, const unsigned char *old_sha1,
+              unsigned int flags)
 {
-       char *lockpath = NULL;
-       char ref[1000];
-       int fd, len, written;
-       char *git_HEAD = git_pathdup("%s", ref_target);
-       unsigned char old_sha1[20], new_sha1[20];
+       struct ref_transaction *transaction;
        struct strbuf err = STRBUF_INIT;
 
-       if (logmsg && read_ref(ref_target, old_sha1))
-               hashclr(old_sha1);
-
-       if (safe_create_leading_directories(git_HEAD) < 0)
-               return error("unable to create directory for %s", git_HEAD);
+       if (ref_type(refname) == REF_TYPE_PSEUDOREF)
+               return delete_pseudoref(refname, old_sha1);
 
-#ifndef NO_SYMLINK_HEAD
-       if (prefer_symlink_refs) {
-               unlink(git_HEAD);
-               if (!symlink(refs_heads_master, git_HEAD))
-                       goto done;
-               fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+       transaction = ref_transaction_begin(&err);
+       if (!transaction ||
+           ref_transaction_delete(transaction, refname, old_sha1,
+                                  flags, NULL, &err) ||
+           ref_transaction_commit(transaction, &err)) {
+               error("%s", err.buf);
+               ref_transaction_free(transaction);
+               strbuf_release(&err);
+               return 1;
        }
-#endif
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       return 0;
+}
 
-       len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
-       if (sizeof(ref) <= len) {
-               error("refname too long: %s", refs_heads_master);
-               goto error_free_return;
-       }
-       lockpath = mkpathdup("%s.lock", git_HEAD);
-       fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
-       if (fd < 0) {
-               error("Unable to open %s for writing", lockpath);
-               goto error_free_return;
-       }
-       written = write_in_full(fd, ref, len);
-       if (close(fd) != 0 || written != len) {
-               error("Unable to write to %s", lockpath);
-               goto error_unlink_return;
-       }
-       if (rename(lockpath, git_HEAD) < 0) {
-               error("Unable to create %s", git_HEAD);
-               goto error_unlink_return;
-       }
-       if (adjust_shared_perm(git_HEAD)) {
-               error("Unable to fix permissions on %s", lockpath);
-       error_unlink_return:
-               unlink_or_warn(lockpath);
-       error_free_return:
-               free(lockpath);
-               free(git_HEAD);
-               return -1;
-       }
-       free(lockpath);
+int copy_reflog_msg(char *buf, const char *msg)
+{
+       char *cp = buf;
+       char c;
+       int wasspace = 1;
 
-#ifndef NO_SYMLINK_HEAD
-       done:
-#endif
-       if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
-               log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
-               error("%s", err.buf);
-               strbuf_release(&err);
+       *cp++ = '\t';
+       while ((c = *msg++)) {
+               if (wasspace && isspace(c))
+                       continue;
+               wasspace = isspace(c);
+               if (wasspace)
+                       c = ' ';
+               *cp++ = c;
        }
+       while (buf < cp && isspace(cp[-1]))
+               cp--;
+       *cp++ = '\n';
+       return cp - buf;
+}
 
-       free(git_HEAD);
-       return 0;
+int should_autocreate_reflog(const char *refname)
+{
+       if (!log_all_ref_updates)
+               return 0;
+       return starts_with(refname, "refs/heads/") ||
+               starts_with(refname, "refs/remotes/") ||
+               starts_with(refname, "refs/notes/") ||
+               !strcmp(refname, "HEAD");
+}
+
+int is_branch(const char *refname)
+{
+       return !strcmp(refname, "HEAD") || starts_with(refname, "refs/heads/");
 }
 
 struct read_ref_at_cb {
@@ -3716,287 +736,6 @@ int read_ref_at(const char *refname, unsigned int flags, unsigned long at_time,
        return 1;
 }
 
-int reflog_exists(const char *refname)
-{
-       struct stat st;
-
-       return !lstat(git_path("logs/%s", refname), &st) &&
-               S_ISREG(st.st_mode);
-}
-
-int delete_reflog(const char *refname)
-{
-       return remove_path(git_path("logs/%s", refname));
-}
-
-static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
-{
-       unsigned char osha1[20], nsha1[20];
-       char *email_end, *message;
-       unsigned long timestamp;
-       int tz;
-
-       /* 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, '>')) ||
-           email_end[1] != ' ' ||
-           !(timestamp = strtoul(email_end + 2, &message, 10)) ||
-           !message || message[0] != ' ' ||
-           (message[1] != '+' && message[1] != '-') ||
-           !isdigit(message[2]) || !isdigit(message[3]) ||
-           !isdigit(message[4]) || !isdigit(message[5]))
-               return 0; /* corrupt? */
-       email_end[1] = '\0';
-       tz = strtol(message + 1, NULL, 10);
-       if (message[6] != '\t')
-               message += 6;
-       else
-               message += 7;
-       return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data);
-}
-
-static char *find_beginning_of_line(char *bob, char *scan)
-{
-       while (bob < scan && *(--scan) != '\n')
-               ; /* keep scanning backwards */
-       /*
-        * Return either beginning of the buffer, or LF at the end of
-        * the previous line.
-        */
-       return scan;
-}
-
-int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data)
-{
-       struct strbuf sb = STRBUF_INIT;
-       FILE *logfp;
-       long pos;
-       int ret = 0, at_tail = 1;
-
-       logfp = fopen(git_path("logs/%s", refname), "r");
-       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));
-       pos = ftell(logfp);
-       while (!ret && 0 < pos) {
-               int cnt;
-               size_t nread;
-               char buf[BUFSIZ];
-               char *endp, *scanp;
-
-               /* 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));
-               nread = fread(buf, cnt, 1, logfp);
-               if (nread != 1)
-                       return error("cannot read %d bytes from reflog for %s: %s",
-                                    cnt, refname, strerror(errno));
-               pos -= cnt;
-
-               scanp = endp = buf + cnt;
-               if (at_tail && scanp[-1] == '\n')
-                       /* Looking at the final LF at the end of the file */
-                       scanp--;
-               at_tail = 0;
-
-               while (buf < scanp) {
-                       /*
-                        * terminating LF of the previous line, or the beginning
-                        * of the buffer.
-                        */
-                       char *bp;
-
-                       bp = find_beginning_of_line(buf, scanp);
-
-                       if (*bp == '\n') {
-                               /*
-                                * The newline is the end of the previous line,
-                                * so we know we have complete line starting
-                                * at (bp + 1). Prefix it onto any prior data
-                                * we collected for the line and process it.
-                                */
-                               strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
-                               scanp = bp;
-                               endp = bp + 1;
-                               ret = show_one_reflog_ent(&sb, fn, cb_data);
-                               strbuf_reset(&sb);
-                               if (ret)
-                                       break;
-                       } else if (!pos) {
-                               /*
-                                * We are at the start of the buffer, and the
-                                * start of the file; there is no previous
-                                * line, and we have everything for this one.
-                                * Process it, and we can end the loop.
-                                */
-                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
-                               ret = show_one_reflog_ent(&sb, fn, cb_data);
-                               strbuf_reset(&sb);
-                               break;
-                       }
-
-                       if (bp == buf) {
-                               /*
-                                * We are at the start of the buffer, and there
-                                * is more file to read backwards. Which means
-                                * we are in the middle of a line. Note that we
-                                * may get here even if *bp was a newline; that
-                                * just means we are at the exact end of the
-                                * previous line, rather than some spot in the
-                                * middle.
-                                *
-                                * Save away what we have to be combined with
-                                * the data from the next read.
-                                */
-                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
-                               break;
-                       }
-               }
-
-       }
-       if (!ret && sb.len)
-               die("BUG: reverse reflog parser had leftover data");
-
-       fclose(logfp);
-       strbuf_release(&sb);
-       return ret;
-}
-
-int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)
-{
-       FILE *logfp;
-       struct strbuf sb = STRBUF_INIT;
-       int ret = 0;
-
-       logfp = fopen(git_path("logs/%s", refname), "r");
-       if (!logfp)
-               return -1;
-
-       while (!ret && !strbuf_getwholeline(&sb, logfp, '\n'))
-               ret = show_one_reflog_ent(&sb, fn, cb_data);
-       fclose(logfp);
-       strbuf_release(&sb);
-       return ret;
-}
-/*
- * Call fn for each reflog in the namespace indicated by name.  name
- * must be empty or end with '/'.  Name will be used as a scratch
- * space, but its contents will be restored before return.
- */
-static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
-{
-       DIR *d = opendir(git_path("logs/%s", name->buf));
-       int retval = 0;
-       struct dirent *de;
-       int oldlen = name->len;
-
-       if (!d)
-               return name->len ? errno : 0;
-
-       while ((de = readdir(d)) != NULL) {
-               struct stat st;
-
-               if (de->d_name[0] == '.')
-                       continue;
-               if (ends_with(de->d_name, ".lock"))
-                       continue;
-               strbuf_addstr(name, de->d_name);
-               if (stat(git_path("logs/%s", name->buf), &st) < 0) {
-                       ; /* silently ignore */
-               } else {
-                       if (S_ISDIR(st.st_mode)) {
-                               strbuf_addch(name, '/');
-                               retval = do_for_each_reflog(name, fn, cb_data);
-                       } else {
-                               struct object_id oid;
-
-                               if (read_ref_full(name->buf, 0, oid.hash, NULL))
-                                       retval = error("bad ref for %s", name->buf);
-                               else
-                                       retval = fn(name->buf, &oid, 0, cb_data);
-                       }
-                       if (retval)
-                               break;
-               }
-               strbuf_setlen(name, oldlen);
-       }
-       closedir(d);
-       return retval;
-}
-
-int for_each_reflog(each_ref_fn fn, void *cb_data)
-{
-       int retval;
-       struct strbuf name;
-       strbuf_init(&name, PATH_MAX);
-       retval = do_for_each_reflog(&name, fn, cb_data);
-       strbuf_release(&name);
-       return retval;
-}
-
-/**
- * Information needed for a single ref update. Set new_sha1 to the new
- * value or to null_sha1 to delete the ref. To check the old value
- * while the ref is locked, set (flags & REF_HAVE_OLD) and set
- * old_sha1 to the old value, or to null_sha1 to ensure the ref does
- * not exist before update.
- */
-struct ref_update {
-       /*
-        * If (flags & REF_HAVE_NEW), set the reference to this value:
-        */
-       unsigned char new_sha1[20];
-       /*
-        * If (flags & REF_HAVE_OLD), check that the reference
-        * previously had this value:
-        */
-       unsigned char old_sha1[20];
-       /*
-        * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
-        * REF_DELETING, and REF_ISPRUNING:
-        */
-       unsigned int flags;
-       struct ref_lock *lock;
-       int type;
-       char *msg;
-       const char refname[FLEX_ARRAY];
-};
-
-/*
- * Transaction states.
- * OPEN:   The transaction is in a valid state and can accept new updates.
- *         An OPEN transaction can be committed.
- * CLOSED: A closed transaction is no longer active and no other operations
- *         than free can be used on it in this state.
- *         A transaction can either become closed by successfully committing
- *         an active transaction or if there is a failure while building
- *         the transaction thus rendering it failed/inactive.
- */
-enum ref_transaction_state {
-       REF_TRANSACTION_OPEN   = 0,
-       REF_TRANSACTION_CLOSED = 1
-};
-
-/*
- * Data structure for holding a reference transaction, which can
- * consist of checks and updates to multiple references, carried out
- * as atomically as possible.  This structure is opaque to callers.
- */
-struct ref_transaction {
-       struct ref_update **updates;
-       size_t alloc;
-       size_t nr;
-       enum ref_transaction_state state;
-};
-
 struct ref_transaction *ref_transaction_begin(struct strbuf *err)
 {
        assert(err);
@@ -4147,274 +886,6 @@ int update_ref(const char *msg, const char *refname,
        return 0;
 }
 
-static int ref_update_reject_duplicates(struct string_list *refnames,
-                                       struct strbuf *err)
-{
-       int i, n = refnames->nr;
-
-       assert(err);
-
-       for (i = 1; i < n; i++)
-               if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) {
-                       strbuf_addf(err,
-                                   "Multiple updates for ref '%s' not allowed.",
-                                   refnames->items[i].string);
-                       return 1;
-               }
-       return 0;
-}
-
-int ref_transaction_commit(struct ref_transaction *transaction,
-                          struct strbuf *err)
-{
-       int ret = 0, i;
-       int n = transaction->nr;
-       struct ref_update **updates = transaction->updates;
-       struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
-       struct string_list_item *ref_to_delete;
-       struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
-
-       assert(err);
-
-       if (transaction->state != REF_TRANSACTION_OPEN)
-               die("BUG: commit called for transaction that is not open");
-
-       if (!n) {
-               transaction->state = REF_TRANSACTION_CLOSED;
-               return 0;
-       }
-
-       /* Fail if a refname appears more than once in the transaction: */
-       for (i = 0; i < n; i++)
-               string_list_append(&affected_refnames, updates[i]->refname);
-       string_list_sort(&affected_refnames);
-       if (ref_update_reject_duplicates(&affected_refnames, err)) {
-               ret = TRANSACTION_GENERIC_ERROR;
-               goto cleanup;
-       }
-
-       /*
-        * Acquire all locks, verify old values if provided, check
-        * that new values are valid, and write new values to the
-        * lockfiles, ready to be activated. Only keep one lockfile
-        * open at a time to avoid running out of file descriptors.
-        */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
-
-               if ((update->flags & REF_HAVE_NEW) &&
-                   is_null_sha1(update->new_sha1))
-                       update->flags |= REF_DELETING;
-               update->lock = lock_ref_sha1_basic(
-                               update->refname,
-                               ((update->flags & REF_HAVE_OLD) ?
-                                update->old_sha1 : NULL),
-                               &affected_refnames, NULL,
-                               update->flags,
-                               &update->type,
-                               err);
-               if (!update->lock) {
-                       char *reason;
-
-                       ret = (errno == ENOTDIR)
-                               ? TRANSACTION_NAME_CONFLICT
-                               : TRANSACTION_GENERIC_ERROR;
-                       reason = strbuf_detach(err, NULL);
-                       strbuf_addf(err, "cannot lock ref '%s': %s",
-                                   update->refname, reason);
-                       free(reason);
-                       goto cleanup;
-               }
-               if ((update->flags & REF_HAVE_NEW) &&
-                   !(update->flags & REF_DELETING)) {
-                       int overwriting_symref = ((update->type & REF_ISSYMREF) &&
-                                                 (update->flags & REF_NODEREF));
-
-                       if (!overwriting_symref &&
-                           !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
-                               /*
-                                * The reference already has the desired
-                                * value, so we don't need to write it.
-                                */
-                       } else if (write_ref_to_lockfile(update->lock,
-                                                        update->new_sha1,
-                                                        err)) {
-                               char *write_err = strbuf_detach(err, NULL);
-
-                               /*
-                                * The lock was freed upon failure of
-                                * write_ref_to_lockfile():
-                                */
-                               update->lock = NULL;
-                               strbuf_addf(err,
-                                           "cannot update the ref '%s': %s",
-                                           update->refname, write_err);
-                               free(write_err);
-                               ret = TRANSACTION_GENERIC_ERROR;
-                               goto cleanup;
-                       } else {
-                               update->flags |= REF_NEEDS_COMMIT;
-                       }
-               }
-               if (!(update->flags & REF_NEEDS_COMMIT)) {
-                       /*
-                        * We didn't have to write anything to the lockfile.
-                        * Close it to free up the file descriptor:
-                        */
-                       if (close_ref(update->lock)) {
-                               strbuf_addf(err, "Couldn't close %s.lock",
-                                           update->refname);
-                               goto cleanup;
-                       }
-               }
-       }
-
-       /* Perform updates first so live commits remain referenced */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
-
-               if (update->flags & REF_NEEDS_COMMIT) {
-                       if (commit_ref_update(update->lock,
-                                             update->new_sha1, update->msg,
-                                             update->flags, err)) {
-                               /* freed by commit_ref_update(): */
-                               update->lock = NULL;
-                               ret = TRANSACTION_GENERIC_ERROR;
-                               goto cleanup;
-                       } else {
-                               /* freed by commit_ref_update(): */
-                               update->lock = NULL;
-                       }
-               }
-       }
-
-       /* Perform deletes now that updates are safely completed */
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
-
-               if (update->flags & REF_DELETING) {
-                       if (delete_ref_loose(update->lock, update->type, err)) {
-                               ret = TRANSACTION_GENERIC_ERROR;
-                               goto cleanup;
-                       }
-
-                       if (!(update->flags & REF_ISPRUNING))
-                               string_list_append(&refs_to_delete,
-                                                  update->lock->ref_name);
-               }
-       }
-
-       if (repack_without_refs(&refs_to_delete, err)) {
-               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));
-       clear_loose_ref_cache(&ref_cache);
-
-cleanup:
-       transaction->state = REF_TRANSACTION_CLOSED;
-
-       for (i = 0; i < n; i++)
-               if (updates[i]->lock)
-                       unlock_ref(updates[i]->lock);
-       string_list_clear(&refs_to_delete, 0);
-       string_list_clear(&affected_refnames, 0);
-       return ret;
-}
-
-static int ref_present(const char *refname,
-                      const struct object_id *oid, int flags, void *cb_data)
-{
-       struct string_list *affected_refnames = cb_data;
-
-       return string_list_has_string(affected_refnames, refname);
-}
-
-int initial_ref_transaction_commit(struct ref_transaction *transaction,
-                                  struct strbuf *err)
-{
-       struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
-       struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
-       int ret = 0, i;
-       int n = transaction->nr;
-       struct ref_update **updates = transaction->updates;
-       struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
-
-       assert(err);
-
-       if (transaction->state != REF_TRANSACTION_OPEN)
-               die("BUG: commit called for transaction that is not open");
-
-       /* Fail if a refname appears more than once in the transaction: */
-       for (i = 0; i < n; i++)
-               string_list_append(&affected_refnames, updates[i]->refname);
-       string_list_sort(&affected_refnames);
-       if (ref_update_reject_duplicates(&affected_refnames, err)) {
-               ret = TRANSACTION_GENERIC_ERROR;
-               goto cleanup;
-       }
-
-       /*
-        * It's really undefined to call this function in an active
-        * repository or when there are existing references: we are
-        * only locking and changing packed-refs, so (1) any
-        * simultaneous processes might try to change a reference at
-        * the same time we do, and (2) any existing loose versions of
-        * the references that we are setting would have precedence
-        * over our values. But some remote helpers create the remote
-        * "HEAD" and "master" branches before calling this function,
-        * 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))
-               die("BUG: initial ref transaction called with existing refs");
-
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
-
-               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,
-                                            loose_refs, err) ||
-                   verify_refname_available(update->refname,
-                                            &affected_refnames, NULL,
-                                            packed_refs, err)) {
-                       ret = TRANSACTION_NAME_CONFLICT;
-                       goto cleanup;
-               }
-       }
-
-       if (lock_packed_refs(0)) {
-               strbuf_addf(err, "unable to lock packed-refs file: %s",
-                           strerror(errno));
-               ret = TRANSACTION_GENERIC_ERROR;
-               goto cleanup;
-       }
-
-       for (i = 0; i < n; i++) {
-               struct ref_update *update = updates[i];
-
-               if ((update->flags & REF_HAVE_NEW) &&
-                   !is_null_sha1(update->new_sha1))
-                       add_packed_ref(update->refname, update->new_sha1);
-       }
-
-       if (commit_packed_refs()) {
-               strbuf_addf(err, "unable to commit packed-refs file: %s",
-                           strerror(errno));
-               ret = TRANSACTION_GENERIC_ERROR;
-               goto cleanup;
-       }
-
-cleanup:
-       transaction->state = REF_TRANSACTION_CLOSED;
-       string_list_clear(&affected_refnames, 0);
-       return ret;
-}
-
 char *shorten_unambiguous_ref(const char *refname, int strict)
 {
        int i;
@@ -4568,144 +1039,46 @@ int ref_is_hidden(const char *refname, const char *refname_full)
        return 0;
 }
 
-struct expire_reflog_cb {
-       unsigned int flags;
-       reflog_expiry_should_prune_fn *should_prune_fn;
-       void *policy_cb;
-       FILE *newlog;
-       unsigned char last_kept_sha1[20];
-};
-
-static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
-                            const char *email, unsigned long timestamp, int tz,
-                            const char *message, void *cb_data)
-{
-       struct expire_reflog_cb *cb = cb_data;
-       struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
-
-       if (cb->flags & EXPIRE_REFLOGS_REWRITE)
-               osha1 = cb->last_kept_sha1;
-
-       if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
-                                  message, policy_cb)) {
-               if (!cb->newlog)
-                       printf("would prune %s", message);
-               else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
-                       printf("prune %s", message);
-       } else {
-               if (cb->newlog) {
-                       fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
-                               sha1_to_hex(osha1), sha1_to_hex(nsha1),
-                               email, timestamp, tz, message);
-                       hashcpy(cb->last_kept_sha1, nsha1);
-               }
-               if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
-                       printf("keep %s", message);
-       }
-       return 0;
-}
-
-int reflog_expire(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)
+const char *find_descendant_ref(const char *dirname,
+                               const struct string_list *extras,
+                               const struct string_list *skip)
 {
-       static struct lock_file reflog_lock;
-       struct expire_reflog_cb cb;
-       struct ref_lock *lock;
-       char *log_file;
-       int status = 0;
-       int type;
-       struct strbuf err = STRBUF_INIT;
+       int pos;
 
-       memset(&cb, 0, sizeof(cb));
-       cb.flags = flags;
-       cb.policy_cb = policy_cb_data;
-       cb.should_prune_fn = should_prune_fn;
+       if (!extras)
+               return NULL;
 
        /*
-        * The reflog file is locked by holding the lock on the
-        * reference itself, plus we might need to update the
-        * reference if --updateref was specified:
+        * Look at the place where dirname would be inserted into
+        * extras. If there is an entry at that position that starts
+        * with dirname (remember, dirname includes the trailing
+        * slash) and is not in skip, then we have a conflict.
         */
-       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err);
-       if (!lock) {
-               error("cannot lock ref '%s': %s", refname, err.buf);
-               strbuf_release(&err);
-               return -1;
-       }
-       if (!reflog_exists(refname)) {
-               unlock_ref(lock);
-               return 0;
-       }
+       for (pos = string_list_find_insert_index(extras, dirname, 0);
+            pos < extras->nr; pos++) {
+               const char *extra_refname = extras->items[pos].string;
 
-       log_file = git_pathdup("logs/%s", refname);
-       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
-               /*
-                * Even though holding $GIT_DIR/logs/$reflog.lock has
-                * no locking implications, we use the lock_file
-                * machinery here anyway because it does a lot of the
-                * work we need, including cleaning up if the program
-                * exits unexpectedly.
-                */
-               if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
-                       struct strbuf err = STRBUF_INIT;
-                       unable_to_lock_message(log_file, errno, &err);
-                       error("%s", err.buf);
-                       strbuf_release(&err);
-                       goto failure;
-               }
-               cb.newlog = fdopen_lock_file(&reflog_lock, "w");
-               if (!cb.newlog) {
-                       error("cannot fdopen %s (%s)",
-                             get_lock_file_path(&reflog_lock), strerror(errno));
-                       goto failure;
-               }
+               if (!starts_with(extra_refname, dirname))
+                       break;
+
+               if (!skip || !string_list_has_string(skip, extra_refname))
+                       return extra_refname;
        }
+       return NULL;
+}
 
-       (*prepare_fn)(refname, sha1, cb.policy_cb);
-       for_each_reflog_ent(refname, expire_reflog_ent, &cb);
-       (*cleanup_fn)(cb.policy_cb);
+int rename_ref_available(const char *oldname, const char *newname)
+{
+       struct string_list skip = STRING_LIST_INIT_NODUP;
+       struct strbuf err = STRBUF_INIT;
+       int ret;
 
-       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
-               /*
-                * It doesn't make sense to adjust a reference pointed
-                * to by a symbolic ref based on expiring entries in
-                * the symbolic reference's reflog. Nor can we update
-                * a reference if there are no remaining reflog
-                * entries.
-                */
-               int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) &&
-                       !(type & REF_ISSYMREF) &&
-                       !is_null_sha1(cb.last_kept_sha1);
-
-               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 ||
-                           write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
-                           close_ref(lock) < 0)) {
-                       status |= error("couldn't write %s",
-                                       get_lock_file_path(lock->lk));
-                       rollback_lock_file(&reflog_lock);
-               } else if (commit_lock_file(&reflog_lock)) {
-                       status |= error("unable to commit reflog '%s' (%s)",
-                                       log_file, strerror(errno));
-               } else if (update && commit_ref(lock)) {
-                       status |= error("couldn't set %s", lock->ref_name);
-               }
-       }
-       free(log_file);
-       unlock_ref(lock);
-       return status;
-
- failure:
-       rollback_lock_file(&reflog_lock);
-       free(log_file);
-       unlock_ref(lock);
-       return -1;
+       string_list_insert(&skip, oldname);
+       ret = !verify_refname_available(newname, NULL, &skip, &err);
+       if (!ret)
+               error("%s", err.buf);
+
+       string_list_clear(&skip, 0);
+       strbuf_release(&err);
+       return ret;
 }
diff --git a/refs/files-backend.c b/refs/files-backend.c
new file mode 100644 (file)
index 0000000..c648b5e
--- /dev/null
@@ -0,0 +1,3512 @@
+#include "../cache.h"
+#include "../refs.h"
+#include "refs-internal.h"
+#include "../lockfile.h"
+#include "../object.h"
+#include "../dir.h"
+
+struct ref_lock {
+       char *ref_name;
+       char *orig_ref_name;
+       struct lock_file *lk;
+       struct object_id old_oid;
+};
+
+struct ref_entry;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a single cached reference.  This data structure only
+ * occurs embedded in a union in struct ref_entry, and only when
+ * (ref_entry->flag & REF_DIR) is zero.
+ */
+struct ref_value {
+       /*
+        * The name of the object to which this reference resolves
+        * (which may be a tag object).  If REF_ISBROKEN, this is
+        * null.  If REF_ISSYMREF, then this is the name of the object
+        * referred to by the last reference in the symlink chain.
+        */
+       struct object_id oid;
+
+       /*
+        * If REF_KNOWS_PEELED, then this field holds the peeled value
+        * of this reference, or null if the reference is known not to
+        * be peelable.  See the documentation for peel_ref() for an
+        * exact definition of "peelable".
+        */
+       struct object_id peeled;
+};
+
+struct ref_cache;
+
+/*
+ * Information used (along with the information in ref_entry) to
+ * describe a level in the hierarchy of references.  This data
+ * structure only occurs embedded in a union in struct ref_entry, and
+ * only when (ref_entry.flag & REF_DIR) is set.  In that case,
+ * (ref_entry.flag & REF_INCOMPLETE) determines whether the references
+ * in the directory have already been read:
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) unset -- a directory of loose
+ *         or packed references, already read.
+ *
+ *     (ref_entry.flag & REF_INCOMPLETE) set -- a directory of loose
+ *         references that hasn't been read yet (nor has any of its
+ *         subdirectories).
+ *
+ * Entries within a directory are stored within a growable array of
+ * pointers to ref_entries (entries, nr, alloc).  Entries 0 <= i <
+ * sorted are sorted by their component name in strcmp() order and the
+ * remaining entries are unsorted.
+ *
+ * Loose references are read lazily, one directory at a time.  When a
+ * directory of loose references is read, then all of the references
+ * in that directory are stored, and REF_INCOMPLETE stubs are created
+ * for any subdirectories, but the subdirectories themselves are not
+ * read.  The reading is triggered by get_ref_dir().
+ */
+struct ref_dir {
+       int nr, alloc;
+
+       /*
+        * Entries with index 0 <= i < sorted are sorted by name.  New
+        * entries are appended to the list unsorted, and are sorted
+        * only when required; thus we avoid the need to sort the list
+        * after the addition of every reference.
+        */
+       int sorted;
+
+       /* A pointer to the ref_cache that contains this ref_dir. */
+       struct ref_cache *ref_cache;
+
+       struct ref_entry **entries;
+};
+
+/*
+ * Bit values for ref_entry::flag.  REF_ISSYMREF=0x01,
+ * REF_ISPACKED=0x02, REF_ISBROKEN=0x04 and REF_BAD_NAME=0x08 are
+ * public values; see refs.h.
+ */
+
+/*
+ * The field ref_entry->u.value.peeled of this value entry contains
+ * the correct peeled value for the reference, which might be
+ * null_sha1 if the reference is not a tag or if it is broken.
+ */
+#define REF_KNOWS_PEELED 0x10
+
+/* ref_entry represents a directory of references */
+#define REF_DIR 0x20
+
+/*
+ * Entry has not yet been read from disk (used only for REF_DIR
+ * entries representing loose references)
+ */
+#define REF_INCOMPLETE 0x40
+
+/*
+ * A ref_entry represents either a reference or a "subdirectory" of
+ * references.
+ *
+ * Each directory in the reference namespace is represented by a
+ * ref_entry with (flags & REF_DIR) set and containing a subdir member
+ * that holds the entries in that directory that have been read so
+ * far.  If (flags & REF_INCOMPLETE) is set, then the directory and
+ * its subdirectories haven't been read yet.  REF_INCOMPLETE is only
+ * used for loose reference directories.
+ *
+ * References are represented by a ref_entry with (flags & REF_DIR)
+ * unset and a value member that describes the reference's value.  The
+ * flag member is at the ref_entry level, but it is also needed to
+ * interpret the contents of the value field (in other words, a
+ * ref_value object is not very much use without the enclosing
+ * ref_entry).
+ *
+ * Reference names cannot end with slash and directories' names are
+ * always stored with a trailing slash (except for the top-level
+ * directory, which is always denoted by "").  This has two nice
+ * consequences: (1) when the entries in each subdir are sorted
+ * lexicographically by name (as they usually are), the references in
+ * a whole tree can be generated in lexicographic order by traversing
+ * the tree in left-to-right, depth-first order; (2) the names of
+ * references and subdirectories cannot conflict, and therefore the
+ * presence of an empty subdirectory does not block the creation of a
+ * similarly-named reference.  (The fact that reference names with the
+ * same leading components can conflict *with each other* is a
+ * separate issue that is regulated by verify_refname_available().)
+ *
+ * Please note that the name field contains the fully-qualified
+ * reference (or subdirectory) name.  Space could be saved by only
+ * storing the relative names.  But that would require the full names
+ * to be generated on the fly when iterating in do_for_each_ref(), and
+ * would break callback functions, who have always been able to assume
+ * that the name strings that they are passed will not be freed during
+ * the iteration.
+ */
+struct ref_entry {
+       unsigned char flag; /* ISSYMREF? ISPACKED? */
+       union {
+               struct ref_value value; /* if not (flags&REF_DIR) */
+               struct ref_dir subdir; /* if (flags&REF_DIR) */
+       } u;
+       /*
+        * The full name of the reference (e.g., "refs/heads/master")
+        * or the full name of the directory with a trailing slash
+        * (e.g., "refs/heads/"):
+        */
+       char name[FLEX_ARRAY];
+};
+
+static void read_loose_refs(const char *dirname, struct ref_dir *dir);
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+                                         const char *dirname, size_t len,
+                                         int incomplete);
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
+
+static struct ref_dir *get_ref_dir(struct ref_entry *entry)
+{
+       struct ref_dir *dir;
+       assert(entry->flag & REF_DIR);
+       dir = &entry->u.subdir;
+       if (entry->flag & REF_INCOMPLETE) {
+               read_loose_refs(entry->name, dir);
+
+               /*
+                * Manually add refs/bisect, which, being
+                * per-worktree, might not appear in the directory
+                * listing for refs/ in the main repo.
+                */
+               if (!strcmp(entry->name, "refs/")) {
+                       int pos = search_ref_dir(dir, "refs/bisect/", 12);
+                       if (pos < 0) {
+                               struct ref_entry *child_entry;
+                               child_entry = create_dir_entry(dir->ref_cache,
+                                                              "refs/bisect/",
+                                                              12, 1);
+                               add_entry_to_dir(dir, child_entry);
+                               read_loose_refs("refs/bisect",
+                                               &child_entry->u.subdir);
+                       }
+               }
+               entry->flag &= ~REF_INCOMPLETE;
+       }
+       return dir;
+}
+
+static struct ref_entry *create_ref_entry(const char *refname,
+                                         const unsigned char *sha1, int flag,
+                                         int check_name)
+{
+       int len;
+       struct ref_entry *ref;
+
+       if (check_name &&
+           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+               die("Reference has invalid format: '%s'", refname);
+       len = strlen(refname) + 1;
+       ref = xmalloc(sizeof(struct ref_entry) + len);
+       hashcpy(ref->u.value.oid.hash, sha1);
+       oidclr(&ref->u.value.peeled);
+       memcpy(ref->name, refname, len);
+       ref->flag = flag;
+       return ref;
+}
+
+static void clear_ref_dir(struct ref_dir *dir);
+
+static void free_ref_entry(struct ref_entry *entry)
+{
+       if (entry->flag & REF_DIR) {
+               /*
+                * Do not use get_ref_dir() here, as that might
+                * trigger the reading of loose refs.
+                */
+               clear_ref_dir(&entry->u.subdir);
+       }
+       free(entry);
+}
+
+/*
+ * Add a ref_entry to the end of dir (unsorted).  Entry is always
+ * stored directly in dir; no recursion into subdirectories is
+ * done.
+ */
+static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry)
+{
+       ALLOC_GROW(dir->entries, dir->nr + 1, dir->alloc);
+       dir->entries[dir->nr++] = entry;
+       /* optimize for the case that entries are added in order */
+       if (dir->nr == 1 ||
+           (dir->nr == dir->sorted + 1 &&
+            strcmp(dir->entries[dir->nr - 2]->name,
+                   dir->entries[dir->nr - 1]->name) < 0))
+               dir->sorted = dir->nr;
+}
+
+/*
+ * Clear and free all entries in dir, recursively.
+ */
+static void clear_ref_dir(struct ref_dir *dir)
+{
+       int i;
+       for (i = 0; i < dir->nr; i++)
+               free_ref_entry(dir->entries[i]);
+       free(dir->entries);
+       dir->sorted = dir->nr = dir->alloc = 0;
+       dir->entries = NULL;
+}
+
+/*
+ * Create a struct ref_entry object for the specified dirname.
+ * dirname is the name of the directory with a trailing slash (e.g.,
+ * "refs/heads/") or "" for the top-level directory.
+ */
+static struct ref_entry *create_dir_entry(struct ref_cache *ref_cache,
+                                         const char *dirname, size_t len,
+                                         int incomplete)
+{
+       struct ref_entry *direntry;
+       direntry = xcalloc(1, sizeof(struct ref_entry) + len + 1);
+       memcpy(direntry->name, dirname, len);
+       direntry->name[len] = '\0';
+       direntry->u.subdir.ref_cache = ref_cache;
+       direntry->flag = REF_DIR | (incomplete ? REF_INCOMPLETE : 0);
+       return direntry;
+}
+
+static int ref_entry_cmp(const void *a, const void *b)
+{
+       struct ref_entry *one = *(struct ref_entry **)a;
+       struct ref_entry *two = *(struct ref_entry **)b;
+       return strcmp(one->name, two->name);
+}
+
+static void sort_ref_dir(struct ref_dir *dir);
+
+struct string_slice {
+       size_t len;
+       const char *str;
+};
+
+static int ref_entry_cmp_sslice(const void *key_, const void *ent_)
+{
+       const struct string_slice *key = key_;
+       const struct ref_entry *ent = *(const struct ref_entry * const *)ent_;
+       int cmp = strncmp(key->str, ent->name, key->len);
+       if (cmp)
+               return cmp;
+       return '\0' - (unsigned char)ent->name[key->len];
+}
+
+/*
+ * Return the index of the entry with the given refname from the
+ * ref_dir (non-recursively), sorting dir if necessary.  Return -1 if
+ * no such entry is found.  dir must already be complete.
+ */
+static int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len)
+{
+       struct ref_entry **r;
+       struct string_slice key;
+
+       if (refname == NULL || !dir->nr)
+               return -1;
+
+       sort_ref_dir(dir);
+       key.len = len;
+       key.str = refname;
+       r = bsearch(&key, dir->entries, dir->nr, sizeof(*dir->entries),
+                   ref_entry_cmp_sslice);
+
+       if (r == NULL)
+               return -1;
+
+       return r - dir->entries;
+}
+
+/*
+ * Search for a directory entry directly within dir (without
+ * recursing).  Sort dir if necessary.  subdirname must be a directory
+ * name (i.e., end in '/').  If mkdir is set, then create the
+ * directory if it is missing; otherwise, return NULL if the desired
+ * directory cannot be found.  dir must already be complete.
+ */
+static struct ref_dir *search_for_subdir(struct ref_dir *dir,
+                                        const char *subdirname, size_t len,
+                                        int mkdir)
+{
+       int entry_index = search_ref_dir(dir, subdirname, len);
+       struct ref_entry *entry;
+       if (entry_index == -1) {
+               if (!mkdir)
+                       return NULL;
+               /*
+                * Since dir is complete, the absence of a subdir
+                * means that the subdir really doesn't exist;
+                * therefore, create an empty record for it but mark
+                * the record complete.
+                */
+               entry = create_dir_entry(dir->ref_cache, subdirname, len, 0);
+               add_entry_to_dir(dir, entry);
+       } else {
+               entry = dir->entries[entry_index];
+       }
+       return get_ref_dir(entry);
+}
+
+/*
+ * If refname is a reference name, find the ref_dir within the dir
+ * tree that should hold refname.  If refname is a directory name
+ * (i.e., ends in '/'), then return that ref_dir itself.  dir must
+ * represent the top-level directory and must already be complete.
+ * Sort ref_dirs and recurse into subdirectories as necessary.  If
+ * mkdir is set, then create any missing directories; otherwise,
+ * return NULL if the desired directory cannot be found.
+ */
+static struct ref_dir *find_containing_dir(struct ref_dir *dir,
+                                          const char *refname, int mkdir)
+{
+       const char *slash;
+       for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+               size_t dirnamelen = slash - refname + 1;
+               struct ref_dir *subdir;
+               subdir = search_for_subdir(dir, refname, dirnamelen, mkdir);
+               if (!subdir) {
+                       dir = NULL;
+                       break;
+               }
+               dir = subdir;
+       }
+
+       return dir;
+}
+
+/*
+ * Find the value entry with the given name in dir, sorting ref_dirs
+ * and recursing into subdirectories as necessary.  If the name is not
+ * found or it corresponds to a directory entry, return NULL.
+ */
+static struct ref_entry *find_ref(struct ref_dir *dir, const char *refname)
+{
+       int entry_index;
+       struct ref_entry *entry;
+       dir = find_containing_dir(dir, refname, 0);
+       if (!dir)
+               return NULL;
+       entry_index = search_ref_dir(dir, refname, strlen(refname));
+       if (entry_index == -1)
+               return NULL;
+       entry = dir->entries[entry_index];
+       return (entry->flag & REF_DIR) ? NULL : entry;
+}
+
+/*
+ * Remove the entry with the given name from dir, recursing into
+ * subdirectories as necessary.  If refname is the name of a directory
+ * (i.e., ends with '/'), then remove the directory and its contents.
+ * If the removal was successful, return the number of entries
+ * remaining in the directory entry that contained the deleted entry.
+ * If the name was not found, return -1.  Please note that this
+ * function only deletes the entry from the cache; it does not delete
+ * it from the filesystem or ensure that other cache entries (which
+ * might be symbolic references to the removed entry) are updated.
+ * Nor does it remove any containing dir entries that might be made
+ * empty by the removal.  dir must represent the top-level directory
+ * and must already be complete.
+ */
+static int remove_entry(struct ref_dir *dir, const char *refname)
+{
+       int refname_len = strlen(refname);
+       int entry_index;
+       struct ref_entry *entry;
+       int is_dir = refname[refname_len - 1] == '/';
+       if (is_dir) {
+               /*
+                * refname represents a reference directory.  Remove
+                * the trailing slash; otherwise we will get the
+                * directory *representing* refname rather than the
+                * one *containing* it.
+                */
+               char *dirname = xmemdupz(refname, refname_len - 1);
+               dir = find_containing_dir(dir, dirname, 0);
+               free(dirname);
+       } else {
+               dir = find_containing_dir(dir, refname, 0);
+       }
+       if (!dir)
+               return -1;
+       entry_index = search_ref_dir(dir, refname, refname_len);
+       if (entry_index == -1)
+               return -1;
+       entry = dir->entries[entry_index];
+
+       memmove(&dir->entries[entry_index],
+               &dir->entries[entry_index + 1],
+               (dir->nr - entry_index - 1) * sizeof(*dir->entries)
+               );
+       dir->nr--;
+       if (dir->sorted > entry_index)
+               dir->sorted--;
+       free_ref_entry(entry);
+       return dir->nr;
+}
+
+/*
+ * Add a ref_entry to the ref_dir (unsorted), recursing into
+ * subdirectories as necessary.  dir must represent the top-level
+ * directory.  Return 0 on success.
+ */
+static int add_ref(struct ref_dir *dir, struct ref_entry *ref)
+{
+       dir = find_containing_dir(dir, ref->name, 1);
+       if (!dir)
+               return -1;
+       add_entry_to_dir(dir, ref);
+       return 0;
+}
+
+/*
+ * Emit a warning and return true iff ref1 and ref2 have the same name
+ * and the same sha1.  Die if they have the same name but different
+ * sha1s.
+ */
+static int is_dup_ref(const struct ref_entry *ref1, const struct ref_entry *ref2)
+{
+       if (strcmp(ref1->name, ref2->name))
+               return 0;
+
+       /* Duplicate name; make sure that they don't conflict: */
+
+       if ((ref1->flag & REF_DIR) || (ref2->flag & REF_DIR))
+               /* This is impossible by construction */
+               die("Reference directory conflict: %s", ref1->name);
+
+       if (oidcmp(&ref1->u.value.oid, &ref2->u.value.oid))
+               die("Duplicated ref, and SHA1s don't match: %s", ref1->name);
+
+       warning("Duplicated ref: %s", ref1->name);
+       return 1;
+}
+
+/*
+ * Sort the entries in dir non-recursively (if they are not already
+ * sorted) and remove any duplicate entries.
+ */
+static void sort_ref_dir(struct ref_dir *dir)
+{
+       int i, j;
+       struct ref_entry *last = NULL;
+
+       /*
+        * This check also prevents passing a zero-length array to qsort(),
+        * which is a problem on some platforms.
+        */
+       if (dir->sorted == dir->nr)
+               return;
+
+       qsort(dir->entries, dir->nr, sizeof(*dir->entries), ref_entry_cmp);
+
+       /* Remove any duplicates: */
+       for (i = 0, j = 0; j < dir->nr; j++) {
+               struct ref_entry *entry = dir->entries[j];
+               if (last && is_dup_ref(last, entry))
+                       free_ref_entry(entry);
+               else
+                       last = dir->entries[i++] = entry;
+       }
+       dir->sorted = dir->nr = i;
+}
+
+/* Include broken references in a do_for_each_ref*() iteration: */
+#define DO_FOR_EACH_INCLUDE_BROKEN 0x01
+
+/*
+ * Return true iff the reference described by entry can be resolved to
+ * an object in the database.  Emit a warning if the referred-to
+ * object does not exist.
+ */
+static int ref_resolves_to_object(struct ref_entry *entry)
+{
+       if (entry->flag & REF_ISBROKEN)
+               return 0;
+       if (!has_sha1_file(entry->u.value.oid.hash)) {
+               error("%s does not point to a valid object!", entry->name);
+               return 0;
+       }
+       return 1;
+}
+
+/*
+ * current_ref is a performance hack: when iterating over references
+ * using the for_each_ref*() functions, current_ref is set to the
+ * current reference's entry before calling the callback function.  If
+ * the callback function calls peel_ref(), then peel_ref() first
+ * checks whether the reference to be peeled is the current reference
+ * (it usually is) and if so, returns that reference's peeled version
+ * if it is available.  This avoids a refname lookup in a common case.
+ */
+static struct ref_entry *current_ref;
+
+typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
+
+struct ref_entry_cb {
+       const char *base;
+       int trim;
+       int flags;
+       each_ref_fn *fn;
+       void *cb_data;
+};
+
+/*
+ * Handle one reference in a do_for_each_ref*()-style iteration,
+ * calling an each_ref_fn for each entry.
+ */
+static int do_one_ref(struct ref_entry *entry, void *cb_data)
+{
+       struct ref_entry_cb *data = cb_data;
+       struct ref_entry *old_current_ref;
+       int retval;
+
+       if (!starts_with(entry->name, data->base))
+               return 0;
+
+       if (!(data->flags & DO_FOR_EACH_INCLUDE_BROKEN) &&
+             !ref_resolves_to_object(entry))
+               return 0;
+
+       /* Store the old value, in case this is a recursive call: */
+       old_current_ref = current_ref;
+       current_ref = entry;
+       retval = data->fn(entry->name + data->trim, &entry->u.value.oid,
+                         entry->flag, data->cb_data);
+       current_ref = old_current_ref;
+       return retval;
+}
+
+/*
+ * Call fn for each reference in dir that has index in the range
+ * offset <= index < dir->nr.  Recurse into subdirectories that are in
+ * that index range, sorting them before iterating.  This function
+ * does not sort dir itself; it should be sorted beforehand.  fn is
+ * called for all references, including broken ones.
+ */
+static int do_for_each_entry_in_dir(struct ref_dir *dir, int offset,
+                                   each_ref_entry_fn fn, void *cb_data)
+{
+       int i;
+       assert(dir->sorted == dir->nr);
+       for (i = offset; i < dir->nr; i++) {
+               struct ref_entry *entry = dir->entries[i];
+               int retval;
+               if (entry->flag & REF_DIR) {
+                       struct ref_dir *subdir = get_ref_dir(entry);
+                       sort_ref_dir(subdir);
+                       retval = do_for_each_entry_in_dir(subdir, 0, fn, cb_data);
+               } else {
+                       retval = fn(entry, cb_data);
+               }
+               if (retval)
+                       return retval;
+       }
+       return 0;
+}
+
+/*
+ * Call fn for each reference in the union of dir1 and dir2, in order
+ * by refname.  Recurse into subdirectories.  If a value entry appears
+ * in both dir1 and dir2, then only process the version that is in
+ * dir2.  The input dirs must already be sorted, but subdirs will be
+ * sorted as needed.  fn is called for all references, including
+ * broken ones.
+ */
+static int do_for_each_entry_in_dirs(struct ref_dir *dir1,
+                                    struct ref_dir *dir2,
+                                    each_ref_entry_fn fn, void *cb_data)
+{
+       int retval;
+       int i1 = 0, i2 = 0;
+
+       assert(dir1->sorted == dir1->nr);
+       assert(dir2->sorted == dir2->nr);
+       while (1) {
+               struct ref_entry *e1, *e2;
+               int cmp;
+               if (i1 == dir1->nr) {
+                       return do_for_each_entry_in_dir(dir2, i2, fn, cb_data);
+               }
+               if (i2 == dir2->nr) {
+                       return do_for_each_entry_in_dir(dir1, i1, fn, cb_data);
+               }
+               e1 = dir1->entries[i1];
+               e2 = dir2->entries[i2];
+               cmp = strcmp(e1->name, e2->name);
+               if (cmp == 0) {
+                       if ((e1->flag & REF_DIR) && (e2->flag & REF_DIR)) {
+                               /* Both are directories; descend them in parallel. */
+                               struct ref_dir *subdir1 = get_ref_dir(e1);
+                               struct ref_dir *subdir2 = get_ref_dir(e2);
+                               sort_ref_dir(subdir1);
+                               sort_ref_dir(subdir2);
+                               retval = do_for_each_entry_in_dirs(
+                                               subdir1, subdir2, fn, cb_data);
+                               i1++;
+                               i2++;
+                       } else if (!(e1->flag & REF_DIR) && !(e2->flag & REF_DIR)) {
+                               /* Both are references; ignore the one from dir1. */
+                               retval = fn(e2, cb_data);
+                               i1++;
+                               i2++;
+                       } else {
+                               die("conflict between reference and directory: %s",
+                                   e1->name);
+                       }
+               } else {
+                       struct ref_entry *e;
+                       if (cmp < 0) {
+                               e = e1;
+                               i1++;
+                       } else {
+                               e = e2;
+                               i2++;
+                       }
+                       if (e->flag & REF_DIR) {
+                               struct ref_dir *subdir = get_ref_dir(e);
+                               sort_ref_dir(subdir);
+                               retval = do_for_each_entry_in_dir(
+                                               subdir, 0, fn, cb_data);
+                       } else {
+                               retval = fn(e, cb_data);
+                       }
+               }
+               if (retval)
+                       return retval;
+       }
+}
+
+/*
+ * Load all of the refs from the dir into our in-memory cache. The hard work
+ * of loading loose refs is done by get_ref_dir(), so we just need to recurse
+ * through all of the sub-directories. We do not even need to care about
+ * sorting, as traversal order does not matter to us.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+       int i;
+       for (i = 0; i < dir->nr; i++) {
+               struct ref_entry *entry = dir->entries[i];
+               if (entry->flag & REF_DIR)
+                       prime_ref_dir(get_ref_dir(entry));
+       }
+}
+
+struct nonmatching_ref_data {
+       const struct string_list *skip;
+       const char *conflicting_refname;
+};
+
+static int nonmatching_ref_fn(struct ref_entry *entry, void *vdata)
+{
+       struct nonmatching_ref_data *data = vdata;
+
+       if (data->skip && string_list_has_string(data->skip, entry->name))
+               return 0;
+
+       data->conflicting_refname = entry->name;
+       return 1;
+}
+
+/*
+ * Return 0 if a reference named refname could be created without
+ * conflicting with the name of an existing reference in dir.
+ * See verify_refname_available for more information.
+ */
+static int verify_refname_available_dir(const char *refname,
+                                       const struct string_list *extras,
+                                       const struct string_list *skip,
+                                       struct ref_dir *dir,
+                                       struct strbuf *err)
+{
+       const char *slash;
+       const char *extra_refname;
+       int pos;
+       struct strbuf dirname = STRBUF_INIT;
+       int ret = -1;
+
+       /*
+        * For the sake of comments in this function, suppose that
+        * refname is "refs/foo/bar".
+        */
+
+       assert(err);
+
+       strbuf_grow(&dirname, strlen(refname) + 1);
+       for (slash = strchr(refname, '/'); slash; slash = strchr(slash + 1, '/')) {
+               /* Expand dirname to the new prefix, not including the trailing slash: */
+               strbuf_add(&dirname, refname + dirname.len, slash - refname - dirname.len);
+
+               /*
+                * We are still at a leading dir of the refname (e.g.,
+                * "refs/foo"; if there is a reference with that name,
+                * it is a conflict, *unless* it is in skip.
+                */
+               if (dir) {
+                       pos = search_ref_dir(dir, dirname.buf, dirname.len);
+                       if (pos >= 0 &&
+                           (!skip || !string_list_has_string(skip, dirname.buf))) {
+                               /*
+                                * We found a reference whose name is
+                                * a proper prefix of refname; e.g.,
+                                * "refs/foo", and is not in skip.
+                                */
+                               strbuf_addf(err, "'%s' exists; cannot create '%s'",
+                                           dirname.buf, refname);
+                               goto cleanup;
+                       }
+               }
+
+               if (extras && string_list_has_string(extras, dirname.buf) &&
+                   (!skip || !string_list_has_string(skip, dirname.buf))) {
+                       strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+                                   refname, dirname.buf);
+                       goto cleanup;
+               }
+
+               /*
+                * Otherwise, we can try to continue our search with
+                * the next component. So try to look up the
+                * directory, e.g., "refs/foo/". If we come up empty,
+                * we know there is nothing under this whole prefix,
+                * but even in that case we still have to continue the
+                * search for conflicts with extras.
+                */
+               strbuf_addch(&dirname, '/');
+               if (dir) {
+                       pos = search_ref_dir(dir, dirname.buf, dirname.len);
+                       if (pos < 0) {
+                               /*
+                                * There was no directory "refs/foo/",
+                                * so there is nothing under this
+                                * whole prefix. So there is no need
+                                * to continue looking for conflicting
+                                * references. But we need to continue
+                                * looking for conflicting extras.
+                                */
+                               dir = NULL;
+                       } else {
+                               dir = get_ref_dir(dir->entries[pos]);
+                       }
+               }
+       }
+
+       /*
+        * We are at the leaf of our refname (e.g., "refs/foo/bar").
+        * There is no point in searching for a reference with that
+        * name, because a refname isn't considered to conflict with
+        * itself. But we still need to check for references whose
+        * names are in the "refs/foo/bar/" namespace, because they
+        * *do* conflict.
+        */
+       strbuf_addstr(&dirname, refname + dirname.len);
+       strbuf_addch(&dirname, '/');
+
+       if (dir) {
+               pos = search_ref_dir(dir, dirname.buf, dirname.len);
+
+               if (pos >= 0) {
+                       /*
+                        * We found a directory named "$refname/"
+                        * (e.g., "refs/foo/bar/"). It is a problem
+                        * iff it contains any ref that is not in
+                        * "skip".
+                        */
+                       struct nonmatching_ref_data data;
+
+                       data.skip = skip;
+                       data.conflicting_refname = NULL;
+                       dir = get_ref_dir(dir->entries[pos]);
+                       sort_ref_dir(dir);
+                       if (do_for_each_entry_in_dir(dir, 0, nonmatching_ref_fn, &data)) {
+                               strbuf_addf(err, "'%s' exists; cannot create '%s'",
+                                           data.conflicting_refname, refname);
+                               goto cleanup;
+                       }
+               }
+       }
+
+       extra_refname = find_descendant_ref(dirname.buf, extras, skip);
+       if (extra_refname)
+               strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+                           refname, extra_refname);
+       else
+               ret = 0;
+
+cleanup:
+       strbuf_release(&dirname);
+       return ret;
+}
+
+struct packed_ref_cache {
+       struct ref_entry *root;
+
+       /*
+        * Count of references to the data structure in this instance,
+        * including the pointer from ref_cache::packed if any.  The
+        * data will not be freed as long as the reference count is
+        * nonzero.
+        */
+       unsigned int referrers;
+
+       /*
+        * Iff the packed-refs file associated with this instance is
+        * currently locked for writing, this points at the associated
+        * lock (which is owned by somebody else).  The referrer count
+        * is also incremented when the file is locked and decremented
+        * when it is unlocked.
+        */
+       struct lock_file *lock;
+
+       /* The metadata from when this packed-refs cache was read */
+       struct stat_validity validity;
+};
+
+/*
+ * Future: need to be in "struct repository"
+ * when doing a full libification.
+ */
+static struct ref_cache {
+       struct ref_cache *next;
+       struct ref_entry *loose;
+       struct packed_ref_cache *packed;
+       /*
+        * The submodule name, or "" for the main repo.  We allocate
+        * length 1 rather than FLEX_ARRAY so that the main ref_cache
+        * is initialized correctly.
+        */
+       char name[1];
+} ref_cache, *submodule_ref_caches;
+
+/* Lock used for the main packed-refs file: */
+static struct lock_file packlock;
+
+/*
+ * Increment the reference count of *packed_refs.
+ */
+static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       packed_refs->referrers++;
+}
+
+/*
+ * Decrease the reference count of *packed_refs.  If it goes to zero,
+ * free *packed_refs and return true; otherwise return false.
+ */
+static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
+{
+       if (!--packed_refs->referrers) {
+               free_ref_entry(packed_refs->root);
+               stat_validity_clear(&packed_refs->validity);
+               free(packed_refs);
+               return 1;
+       } else {
+               return 0;
+       }
+}
+
+static void clear_packed_ref_cache(struct ref_cache *refs)
+{
+       if (refs->packed) {
+               struct packed_ref_cache *packed_refs = refs->packed;
+
+               if (packed_refs->lock)
+                       die("internal error: packed-ref cache cleared while locked");
+               refs->packed = NULL;
+               release_packed_ref_cache(packed_refs);
+       }
+}
+
+static void clear_loose_ref_cache(struct ref_cache *refs)
+{
+       if (refs->loose) {
+               free_ref_entry(refs->loose);
+               refs->loose = NULL;
+       }
+}
+
+static struct ref_cache *create_ref_cache(const char *submodule)
+{
+       int len;
+       struct ref_cache *refs;
+       if (!submodule)
+               submodule = "";
+       len = strlen(submodule) + 1;
+       refs = xcalloc(1, sizeof(struct ref_cache) + len);
+       memcpy(refs->name, submodule, len);
+       return refs;
+}
+
+/*
+ * Return a pointer to a ref_cache for the specified submodule. For
+ * the main repository, use submodule==NULL. The returned structure
+ * will be allocated and initialized but not necessarily populated; it
+ * should not be freed.
+ */
+static struct ref_cache *get_ref_cache(const char *submodule)
+{
+       struct ref_cache *refs;
+
+       if (!submodule || !*submodule)
+               return &ref_cache;
+
+       for (refs = submodule_ref_caches; refs; refs = refs->next)
+               if (!strcmp(submodule, refs->name))
+                       return refs;
+
+       refs = create_ref_cache(submodule);
+       refs->next = submodule_ref_caches;
+       submodule_ref_caches = refs;
+       return refs;
+}
+
+/* The length of a peeled reference line in packed-refs, including EOL: */
+#define PEELED_LINE_LENGTH 42
+
+/*
+ * The packed-refs header line that we write out.  Perhaps other
+ * traits will be added later.  The trailing space is required.
+ */
+static const char PACKED_REFS_HEADER[] =
+       "# pack-refs with: peeled fully-peeled \n";
+
+/*
+ * Parse one line from a packed-refs file.  Write the SHA1 to sha1.
+ * Return a pointer to the refname within the line (null-terminated),
+ * or NULL if there was a problem.
+ */
+static const char *parse_ref_line(struct strbuf *line, unsigned char *sha1)
+{
+       const char *ref;
+
+       /*
+        * 42: the answer to everything.
+        *
+        * In this case, it happens to be the answer to
+        *  40 (length of sha1 hex representation)
+        *  +1 (space in between hex and name)
+        *  +1 (newline at the end of the line)
+        */
+       if (line->len <= 42)
+               return NULL;
+
+       if (get_sha1_hex(line->buf, sha1) < 0)
+               return NULL;
+       if (!isspace(line->buf[40]))
+               return NULL;
+
+       ref = line->buf + 41;
+       if (isspace(*ref))
+               return NULL;
+
+       if (line->buf[line->len - 1] != '\n')
+               return NULL;
+       line->buf[--line->len] = 0;
+
+       return ref;
+}
+
+/*
+ * Read f, which is a packed-refs file, into dir.
+ *
+ * A comment line of the form "# pack-refs with: " may contain zero or
+ * more traits. We interpret the traits as follows:
+ *
+ *   No traits:
+ *
+ *      Probably no references are peeled. But if the file contains a
+ *      peeled value for a reference, we will use it.
+ *
+ *   peeled:
+ *
+ *      References under "refs/tags/", if they *can* be peeled, *are*
+ *      peeled in this file. References outside of "refs/tags/" are
+ *      probably not peeled even if they could have been, but if we find
+ *      a peeled value for such a reference we will use it.
+ *
+ *   fully-peeled:
+ *
+ *      All references in the file that can be peeled are peeled.
+ *      Inversely (and this is more important), any references in the
+ *      file for which no peeled value is recorded is not peelable. This
+ *      trait should typically be written alongside "peeled" for
+ *      compatibility with older clients, but we do not require it
+ *      (i.e., "peeled" is a no-op if "fully-peeled" is set).
+ */
+static void read_packed_refs(FILE *f, struct ref_dir *dir)
+{
+       struct ref_entry *last = NULL;
+       struct strbuf line = STRBUF_INIT;
+       enum { PEELED_NONE, PEELED_TAGS, PEELED_FULLY } peeled = PEELED_NONE;
+
+       while (strbuf_getwholeline(&line, f, '\n') != EOF) {
+               unsigned char sha1[20];
+               const char *refname;
+               const char *traits;
+
+               if (skip_prefix(line.buf, "# pack-refs with:", &traits)) {
+                       if (strstr(traits, " fully-peeled "))
+                               peeled = PEELED_FULLY;
+                       else if (strstr(traits, " peeled "))
+                               peeled = PEELED_TAGS;
+                       /* perhaps other traits later as well */
+                       continue;
+               }
+
+               refname = parse_ref_line(&line, sha1);
+               if (refname) {
+                       int flag = REF_ISPACKED;
+
+                       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+                               if (!refname_is_safe(refname))
+                                       die("packed refname is dangerous: %s", refname);
+                               hashclr(sha1);
+                               flag |= REF_BAD_NAME | REF_ISBROKEN;
+                       }
+                       last = create_ref_entry(refname, sha1, flag, 0);
+                       if (peeled == PEELED_FULLY ||
+                           (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
+                               last->flag |= REF_KNOWS_PEELED;
+                       add_ref(dir, last);
+                       continue;
+               }
+               if (last &&
+                   line.buf[0] == '^' &&
+                   line.len == PEELED_LINE_LENGTH &&
+                   line.buf[PEELED_LINE_LENGTH - 1] == '\n' &&
+                   !get_sha1_hex(line.buf + 1, sha1)) {
+                       hashcpy(last->u.value.peeled.hash, sha1);
+                       /*
+                        * Regardless of what the file header said,
+                        * we definitely know the value of *this*
+                        * reference:
+                        */
+                       last->flag |= REF_KNOWS_PEELED;
+               }
+       }
+
+       strbuf_release(&line);
+}
+
+/*
+ * Get the packed_ref_cache for the specified ref_cache, creating it
+ * if necessary.
+ */
+static struct packed_ref_cache *get_packed_ref_cache(struct ref_cache *refs)
+{
+       char *packed_refs_file;
+
+       if (*refs->name)
+               packed_refs_file = git_pathdup_submodule(refs->name, "packed-refs");
+       else
+               packed_refs_file = git_pathdup("packed-refs");
+
+       if (refs->packed &&
+           !stat_validity_check(&refs->packed->validity, packed_refs_file))
+               clear_packed_ref_cache(refs);
+
+       if (!refs->packed) {
+               FILE *f;
+
+               refs->packed = xcalloc(1, sizeof(*refs->packed));
+               acquire_packed_ref_cache(refs->packed);
+               refs->packed->root = create_dir_entry(refs, "", 0, 0);
+               f = fopen(packed_refs_file, "r");
+               if (f) {
+                       stat_validity_update(&refs->packed->validity, fileno(f));
+                       read_packed_refs(f, get_ref_dir(refs->packed->root));
+                       fclose(f);
+               }
+       }
+       free(packed_refs_file);
+       return refs->packed;
+}
+
+static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
+{
+       return get_ref_dir(packed_ref_cache->root);
+}
+
+static struct ref_dir *get_packed_refs(struct ref_cache *refs)
+{
+       return get_packed_ref_dir(get_packed_ref_cache(refs));
+}
+
+/*
+ * Add a reference to the in-memory packed reference cache.  This may
+ * only be called while the packed-refs file is locked (see
+ * lock_packed_refs()).  To actually write the packed-refs file, call
+ * commit_packed_refs().
+ */
+static void add_packed_ref(const char *refname, const unsigned char *sha1)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed refs not locked");
+       add_ref(get_packed_ref_dir(packed_ref_cache),
+               create_ref_entry(refname, sha1, REF_ISPACKED, 1));
+}
+
+/*
+ * Read the loose references from the namespace dirname into dir
+ * (without recursing).  dirname must end with '/'.  dir must be the
+ * directory entry corresponding to dirname.
+ */
+static void read_loose_refs(const char *dirname, struct ref_dir *dir)
+{
+       struct ref_cache *refs = dir->ref_cache;
+       DIR *d;
+       struct dirent *de;
+       int dirnamelen = strlen(dirname);
+       struct strbuf refname;
+       struct strbuf path = STRBUF_INIT;
+       size_t path_baselen;
+
+       if (*refs->name)
+               strbuf_git_path_submodule(&path, refs->name, "%s", dirname);
+       else
+               strbuf_git_path(&path, "%s", dirname);
+       path_baselen = path.len;
+
+       d = opendir(path.buf);
+       if (!d) {
+               strbuf_release(&path);
+               return;
+       }
+
+       strbuf_init(&refname, dirnamelen + 257);
+       strbuf_add(&refname, dirname, dirnamelen);
+
+       while ((de = readdir(d)) != NULL) {
+               unsigned char sha1[20];
+               struct stat st;
+               int flag;
+
+               if (de->d_name[0] == '.')
+                       continue;
+               if (ends_with(de->d_name, ".lock"))
+                       continue;
+               strbuf_addstr(&refname, de->d_name);
+               strbuf_addstr(&path, de->d_name);
+               if (stat(path.buf, &st) < 0) {
+                       ; /* silently ignore */
+               } else if (S_ISDIR(st.st_mode)) {
+                       strbuf_addch(&refname, '/');
+                       add_entry_to_dir(dir,
+                                        create_dir_entry(refs, refname.buf,
+                                                         refname.len, 1));
+               } else {
+                       int read_ok;
+
+                       if (*refs->name) {
+                               hashclr(sha1);
+                               flag = 0;
+                               read_ok = !resolve_gitlink_ref(refs->name,
+                                                              refname.buf, sha1);
+                       } else {
+                               read_ok = !read_ref_full(refname.buf,
+                                                        RESOLVE_REF_READING,
+                                                        sha1, &flag);
+                       }
+
+                       if (!read_ok) {
+                               hashclr(sha1);
+                               flag |= REF_ISBROKEN;
+                       } else if (is_null_sha1(sha1)) {
+                               /*
+                                * It is so astronomically unlikely
+                                * that NULL_SHA1 is the SHA-1 of an
+                                * actual object that we consider its
+                                * appearance in a loose reference
+                                * file to be repo corruption
+                                * (probably due to a software bug).
+                                */
+                               flag |= REF_ISBROKEN;
+                       }
+
+                       if (check_refname_format(refname.buf,
+                                                REFNAME_ALLOW_ONELEVEL)) {
+                               if (!refname_is_safe(refname.buf))
+                                       die("loose refname is dangerous: %s", refname.buf);
+                               hashclr(sha1);
+                               flag |= REF_BAD_NAME | REF_ISBROKEN;
+                       }
+                       add_entry_to_dir(dir,
+                                        create_ref_entry(refname.buf, sha1, flag, 0));
+               }
+               strbuf_setlen(&refname, dirnamelen);
+               strbuf_setlen(&path, path_baselen);
+       }
+       strbuf_release(&refname);
+       strbuf_release(&path);
+       closedir(d);
+}
+
+static struct ref_dir *get_loose_refs(struct ref_cache *refs)
+{
+       if (!refs->loose) {
+               /*
+                * Mark the top-level directory complete because we
+                * are about to read the only subdirectory that can
+                * hold references:
+                */
+               refs->loose = create_dir_entry(refs, "", 0, 0);
+               /*
+                * Create an incomplete entry for "refs/":
+                */
+               add_entry_to_dir(get_ref_dir(refs->loose),
+                                create_dir_entry(refs, "refs/", 5, 1));
+       }
+       return get_ref_dir(refs->loose);
+}
+
+/* We allow "recursive" symbolic refs. Only within reason, though */
+#define MAXDEPTH 5
+#define MAXREFLEN (1024)
+
+/*
+ * Called by resolve_gitlink_ref_recursive() after it failed to read
+ * from the loose refs in ref_cache refs. Find <refname> in the
+ * packed-refs file for the submodule.
+ */
+static int resolve_gitlink_packed_ref(struct ref_cache *refs,
+                                     const char *refname, unsigned char *sha1)
+{
+       struct ref_entry *ref;
+       struct ref_dir *dir = get_packed_refs(refs);
+
+       ref = find_ref(dir, refname);
+       if (ref == NULL)
+               return -1;
+
+       hashcpy(sha1, ref->u.value.oid.hash);
+       return 0;
+}
+
+static int resolve_gitlink_ref_recursive(struct ref_cache *refs,
+                                        const char *refname, unsigned char *sha1,
+                                        int recursion)
+{
+       int fd, len;
+       char buffer[128], *p;
+       char *path;
+
+       if (recursion > MAXDEPTH || strlen(refname) > MAXREFLEN)
+               return -1;
+       path = *refs->name
+               ? git_pathdup_submodule(refs->name, "%s", refname)
+               : git_pathdup("%s", refname);
+       fd = open(path, O_RDONLY);
+       free(path);
+       if (fd < 0)
+               return resolve_gitlink_packed_ref(refs, refname, sha1);
+
+       len = read(fd, buffer, sizeof(buffer)-1);
+       close(fd);
+       if (len < 0)
+               return -1;
+       while (len && isspace(buffer[len-1]))
+               len--;
+       buffer[len] = 0;
+
+       /* Was it a detached head or an old-fashioned symlink? */
+       if (!get_sha1_hex(buffer, sha1))
+               return 0;
+
+       /* Symref? */
+       if (strncmp(buffer, "ref:", 4))
+               return -1;
+       p = buffer + 4;
+       while (isspace(*p))
+               p++;
+
+       return resolve_gitlink_ref_recursive(refs, p, sha1, recursion+1);
+}
+
+int resolve_gitlink_ref(const char *path, const char *refname, unsigned char *sha1)
+{
+       int len = strlen(path), retval;
+       char *submodule;
+       struct ref_cache *refs;
+
+       while (len && path[len-1] == '/')
+               len--;
+       if (!len)
+               return -1;
+       submodule = xstrndup(path, len);
+       refs = get_ref_cache(submodule);
+       free(submodule);
+
+       retval = resolve_gitlink_ref_recursive(refs, refname, sha1, 0);
+       return retval;
+}
+
+/*
+ * Return the ref_entry for the given refname from the packed
+ * references.  If it does not exist, return NULL.
+ */
+static struct ref_entry *get_packed_ref(const char *refname)
+{
+       return find_ref(get_packed_refs(&ref_cache), refname);
+}
+
+/*
+ * A loose ref file doesn't exist; check for a packed ref.  The
+ * options are forwarded from resolve_safe_unsafe().
+ */
+static int resolve_missing_loose_ref(const char *refname,
+                                    int resolve_flags,
+                                    unsigned char *sha1,
+                                    int *flags)
+{
+       struct ref_entry *entry;
+
+       /*
+        * The loose reference file does not exist; check for a packed
+        * reference.
+        */
+       entry = get_packed_ref(refname);
+       if (entry) {
+               hashcpy(sha1, entry->u.value.oid.hash);
+               if (flags)
+                       *flags |= REF_ISPACKED;
+               return 0;
+       }
+       /* The reference is not a packed reference, either. */
+       if (resolve_flags & RESOLVE_REF_READING) {
+               errno = ENOENT;
+               return -1;
+       } else {
+               hashclr(sha1);
+               return 0;
+       }
+}
+
+/* This function needs to return a meaningful errno on failure */
+static const char *resolve_ref_1(const char *refname,
+                                int resolve_flags,
+                                unsigned char *sha1,
+                                int *flags,
+                                struct strbuf *sb_refname,
+                                struct strbuf *sb_path,
+                                struct strbuf *sb_contents)
+{
+       int depth = MAXDEPTH;
+       int bad_name = 0;
+
+       if (flags)
+               *flags = 0;
+
+       if (check_refname_format(refname, REFNAME_ALLOW_ONELEVEL)) {
+               if (flags)
+                       *flags |= REF_BAD_NAME;
+
+               if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+                   !refname_is_safe(refname)) {
+                       errno = EINVAL;
+                       return NULL;
+               }
+               /*
+                * dwim_ref() uses REF_ISBROKEN to distinguish between
+                * missing refs and refs that were present but invalid,
+                * to complain about the latter to stderr.
+                *
+                * We don't know whether the ref exists, so don't set
+                * REF_ISBROKEN yet.
+                */
+               bad_name = 1;
+       }
+       for (;;) {
+               const char *path;
+               struct stat st;
+               char *buf;
+               int fd;
+
+               if (--depth < 0) {
+                       errno = ELOOP;
+                       return NULL;
+               }
+
+               strbuf_reset(sb_path);
+               strbuf_git_path(sb_path, "%s", refname);
+               path = sb_path->buf;
+
+               /*
+                * We might have to loop back here to avoid a race
+                * condition: first we lstat() the file, then we try
+                * to read it as a link or as a file.  But if somebody
+                * changes the type of the file (file <-> directory
+                * <-> symlink) between the lstat() and reading, then
+                * we don't want to report that as an error but rather
+                * try again starting with the lstat().
+                */
+       stat_ref:
+               if (lstat(path, &st) < 0) {
+                       if (errno != ENOENT)
+                               return NULL;
+                       if (resolve_missing_loose_ref(refname, resolve_flags,
+                                                     sha1, flags))
+                               return NULL;
+                       if (bad_name) {
+                               hashclr(sha1);
+                               if (flags)
+                                       *flags |= REF_ISBROKEN;
+                       }
+                       return refname;
+               }
+
+               /* Follow "normalized" - ie "refs/.." symlinks by hand */
+               if (S_ISLNK(st.st_mode)) {
+                       strbuf_reset(sb_contents);
+                       if (strbuf_readlink(sb_contents, path, 0) < 0) {
+                               if (errno == ENOENT || errno == EINVAL)
+                                       /* inconsistent with lstat; retry */
+                                       goto stat_ref;
+                               else
+                                       return NULL;
+                       }
+                       if (starts_with(sb_contents->buf, "refs/") &&
+                           !check_refname_format(sb_contents->buf, 0)) {
+                               strbuf_swap(sb_refname, sb_contents);
+                               refname = sb_refname->buf;
+                               if (flags)
+                                       *flags |= REF_ISSYMREF;
+                               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+                                       hashclr(sha1);
+                                       return refname;
+                               }
+                               continue;
+                       }
+               }
+
+               /* Is it a directory? */
+               if (S_ISDIR(st.st_mode)) {
+                       errno = EISDIR;
+                       return NULL;
+               }
+
+               /*
+                * Anything else, just open it and try to use it as
+                * a ref
+                */
+               fd = open(path, O_RDONLY);
+               if (fd < 0) {
+                       if (errno == ENOENT)
+                               /* inconsistent with lstat; retry */
+                               goto stat_ref;
+                       else
+                               return NULL;
+               }
+               strbuf_reset(sb_contents);
+               if (strbuf_read(sb_contents, fd, 256) < 0) {
+                       int save_errno = errno;
+                       close(fd);
+                       errno = save_errno;
+                       return NULL;
+               }
+               close(fd);
+               strbuf_rtrim(sb_contents);
+
+               /*
+                * Is it a symbolic ref?
+                */
+               if (!starts_with(sb_contents->buf, "ref:")) {
+                       /*
+                        * Please note that FETCH_HEAD has a second
+                        * line containing other data.
+                        */
+                       if (get_sha1_hex(sb_contents->buf, sha1) ||
+                           (sb_contents->buf[40] != '\0' && !isspace(sb_contents->buf[40]))) {
+                               if (flags)
+                                       *flags |= REF_ISBROKEN;
+                               errno = EINVAL;
+                               return NULL;
+                       }
+                       if (bad_name) {
+                               hashclr(sha1);
+                               if (flags)
+                                       *flags |= REF_ISBROKEN;
+                       }
+                       return refname;
+               }
+               if (flags)
+                       *flags |= REF_ISSYMREF;
+               buf = sb_contents->buf + 4;
+               while (isspace(*buf))
+                       buf++;
+               strbuf_reset(sb_refname);
+               strbuf_addstr(sb_refname, buf);
+               refname = sb_refname->buf;
+               if (resolve_flags & RESOLVE_REF_NO_RECURSE) {
+                       hashclr(sha1);
+                       return refname;
+               }
+               if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) {
+                       if (flags)
+                               *flags |= REF_ISBROKEN;
+
+                       if (!(resolve_flags & RESOLVE_REF_ALLOW_BAD_NAME) ||
+                           !refname_is_safe(buf)) {
+                               errno = EINVAL;
+                               return NULL;
+                       }
+                       bad_name = 1;
+               }
+       }
+}
+
+const char *resolve_ref_unsafe(const char *refname, int resolve_flags,
+                              unsigned char *sha1, int *flags)
+{
+       static struct strbuf sb_refname = STRBUF_INIT;
+       struct strbuf sb_contents = STRBUF_INIT;
+       struct strbuf sb_path = STRBUF_INIT;
+       const char *ret;
+
+       ret = resolve_ref_1(refname, resolve_flags, sha1, flags,
+                           &sb_refname, &sb_path, &sb_contents);
+       strbuf_release(&sb_path);
+       strbuf_release(&sb_contents);
+       return ret;
+}
+
+/*
+ * Peel the entry (if possible) and return its new peel_status.  If
+ * repeel is true, re-peel the entry even if there is an old peeled
+ * value that is already stored in it.
+ *
+ * It is OK to call this function with a packed reference entry that
+ * might be stale and might even refer to an object that has since
+ * been garbage-collected.  In such a case, if the entry has
+ * REF_KNOWS_PEELED then leave the status unchanged and return
+ * PEEL_PEELED or PEEL_NON_TAG; otherwise, return PEEL_INVALID.
+ */
+static enum peel_status peel_entry(struct ref_entry *entry, int repeel)
+{
+       enum peel_status status;
+
+       if (entry->flag & REF_KNOWS_PEELED) {
+               if (repeel) {
+                       entry->flag &= ~REF_KNOWS_PEELED;
+                       oidclr(&entry->u.value.peeled);
+               } else {
+                       return is_null_oid(&entry->u.value.peeled) ?
+                               PEEL_NON_TAG : PEEL_PEELED;
+               }
+       }
+       if (entry->flag & REF_ISBROKEN)
+               return PEEL_BROKEN;
+       if (entry->flag & REF_ISSYMREF)
+               return PEEL_IS_SYMREF;
+
+       status = peel_object(entry->u.value.oid.hash, entry->u.value.peeled.hash);
+       if (status == PEEL_PEELED || status == PEEL_NON_TAG)
+               entry->flag |= REF_KNOWS_PEELED;
+       return status;
+}
+
+int peel_ref(const char *refname, unsigned char *sha1)
+{
+       int flag;
+       unsigned char base[20];
+
+       if (current_ref && (current_ref->name == refname
+                           || !strcmp(current_ref->name, refname))) {
+               if (peel_entry(current_ref, 0))
+                       return -1;
+               hashcpy(sha1, current_ref->u.value.peeled.hash);
+               return 0;
+       }
+
+       if (read_ref_full(refname, RESOLVE_REF_READING, base, &flag))
+               return -1;
+
+       /*
+        * If the reference is packed, read its ref_entry from the
+        * cache in the hope that we already know its peeled value.
+        * We only try this optimization on packed references because
+        * (a) forcing the filling of the loose reference cache could
+        * be expensive and (b) loose references anyway usually do not
+        * have REF_KNOWS_PEELED.
+        */
+       if (flag & REF_ISPACKED) {
+               struct ref_entry *r = get_packed_ref(refname);
+               if (r) {
+                       if (peel_entry(r, 0))
+                               return -1;
+                       hashcpy(sha1, r->u.value.peeled.hash);
+                       return 0;
+               }
+       }
+
+       return peel_object(base, sha1);
+}
+
+/*
+ * Call fn for each reference in the specified ref_cache, omitting
+ * references not in the containing_dir of base.  fn is called for all
+ * references, including broken ones.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_entry(struct ref_cache *refs, const char *base,
+                            each_ref_entry_fn fn, void *cb_data)
+{
+       struct packed_ref_cache *packed_ref_cache;
+       struct ref_dir *loose_dir;
+       struct ref_dir *packed_dir;
+       int retval = 0;
+
+       /*
+        * We must make sure that all loose refs are read before accessing the
+        * packed-refs file; this avoids a race condition in which loose refs
+        * are migrated to the packed-refs file by a simultaneous process, but
+        * our in-memory view is from before the migration. get_packed_ref_cache()
+        * takes care of making sure our view is up to date with what is on
+        * disk.
+        */
+       loose_dir = get_loose_refs(refs);
+       if (base && *base) {
+               loose_dir = find_containing_dir(loose_dir, base, 0);
+       }
+       if (loose_dir)
+               prime_ref_dir(loose_dir);
+
+       packed_ref_cache = get_packed_ref_cache(refs);
+       acquire_packed_ref_cache(packed_ref_cache);
+       packed_dir = get_packed_ref_dir(packed_ref_cache);
+       if (base && *base) {
+               packed_dir = find_containing_dir(packed_dir, base, 0);
+       }
+
+       if (packed_dir && loose_dir) {
+               sort_ref_dir(packed_dir);
+               sort_ref_dir(loose_dir);
+               retval = do_for_each_entry_in_dirs(
+                               packed_dir, loose_dir, fn, cb_data);
+       } else if (packed_dir) {
+               sort_ref_dir(packed_dir);
+               retval = do_for_each_entry_in_dir(
+                               packed_dir, 0, fn, cb_data);
+       } else if (loose_dir) {
+               sort_ref_dir(loose_dir);
+               retval = do_for_each_entry_in_dir(
+                               loose_dir, 0, fn, cb_data);
+       }
+
+       release_packed_ref_cache(packed_ref_cache);
+       return retval;
+}
+
+/*
+ * Call fn for each reference in the specified ref_cache for which the
+ * refname begins with base.  If trim is non-zero, then trim that many
+ * characters off the beginning of each refname before passing the
+ * refname to fn.  flags can be DO_FOR_EACH_INCLUDE_BROKEN to include
+ * broken references in the iteration.  If fn ever returns a non-zero
+ * value, stop the iteration and return that value; otherwise, return
+ * 0.
+ */
+static int do_for_each_ref(struct ref_cache *refs, const char *base,
+                          each_ref_fn fn, int trim, int flags, void *cb_data)
+{
+       struct ref_entry_cb data;
+       data.base = base;
+       data.trim = trim;
+       data.flags = flags;
+       data.fn = fn;
+       data.cb_data = cb_data;
+
+       if (ref_paranoia < 0)
+               ref_paranoia = git_env_bool("GIT_REF_PARANOIA", 0);
+       if (ref_paranoia)
+               data.flags |= DO_FOR_EACH_INCLUDE_BROKEN;
+
+       return do_for_each_entry(refs, base, do_one_ref, &data);
+}
+
+static int do_head_ref(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       struct object_id oid;
+       int flag;
+
+       if (submodule) {
+               if (resolve_gitlink_ref(submodule, "HEAD", oid.hash) == 0)
+                       return fn("HEAD", &oid, 0, cb_data);
+
+               return 0;
+       }
+
+       if (!read_ref_full("HEAD", RESOLVE_REF_READING, oid.hash, &flag))
+               return fn("HEAD", &oid, flag, cb_data);
+
+       return 0;
+}
+
+int head_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(NULL, fn, cb_data);
+}
+
+int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_head_ref(submodule, fn, cb_data);
+}
+
+int for_each_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(&ref_cache, "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(get_ref_cache(submodule), "", fn, 0, 0, cb_data);
+}
+
+int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(&ref_cache, prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_fullref_in(const char *prefix, each_ref_fn fn, void *cb_data, unsigned int broken)
+{
+       unsigned int flag = 0;
+
+       if (broken)
+               flag = DO_FOR_EACH_INCLUDE_BROKEN;
+       return do_for_each_ref(&ref_cache, 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)
+{
+       return do_for_each_ref(get_ref_cache(submodule), prefix, fn, strlen(prefix), 0, cb_data);
+}
+
+int for_each_replace_ref(each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(&ref_cache, 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)
+{
+       struct strbuf buf = STRBUF_INIT;
+       int ret;
+       strbuf_addf(&buf, "%srefs/", get_git_namespace());
+       ret = do_for_each_ref(&ref_cache, buf.buf, fn, 0, 0, cb_data);
+       strbuf_release(&buf);
+       return ret;
+}
+
+int for_each_rawref(each_ref_fn fn, void *cb_data)
+{
+       return do_for_each_ref(&ref_cache, "", fn, 0,
+                              DO_FOR_EACH_INCLUDE_BROKEN, cb_data);
+}
+
+static void unlock_ref(struct ref_lock *lock)
+{
+       /* Do not free lock->lk -- atexit() still looks at them */
+       if (lock->lk)
+               rollback_lock_file(lock->lk);
+       free(lock->ref_name);
+       free(lock->orig_ref_name);
+       free(lock);
+}
+
+/*
+ * Verify that the reference locked by lock has the value old_sha1.
+ * Fail if the reference doesn't exist and mustexist is set. Return 0
+ * 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,
+                      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)) {
+               int save_errno = errno;
+               strbuf_addf(err, "can't verify ref %s", lock->ref_name);
+               errno = save_errno;
+               return -1;
+       }
+       if (hashcmp(lock->old_oid.hash, old_sha1)) {
+               strbuf_addf(err, "ref %s is at %s but expected %s",
+                           lock->ref_name,
+                           sha1_to_hex(lock->old_oid.hash),
+                           sha1_to_hex(old_sha1));
+               errno = EBUSY;
+               return -1;
+       }
+       return 0;
+}
+
+static int remove_empty_directories(struct strbuf *path)
+{
+       /*
+        * we want to create a file but there is a directory there;
+        * if that is an empty directory (or a directory that contains
+        * only empty directories), remove them.
+        */
+       return remove_dir_recursively(path, REMOVE_DIR_EMPTY_ONLY);
+}
+
+/*
+ * Locks a ref returning the lock on success and NULL on failure.
+ * On failure errno is set to something meaningful.
+ */
+static struct ref_lock *lock_ref_sha1_basic(const char *refname,
+                                           const unsigned char *old_sha1,
+                                           const struct string_list *extras,
+                                           const struct string_list *skip,
+                                           unsigned int flags, int *type_p,
+                                           struct strbuf *err)
+{
+       struct strbuf ref_file = STRBUF_INIT;
+       struct strbuf orig_ref_file = STRBUF_INIT;
+       const char *orig_refname = refname;
+       struct ref_lock *lock;
+       int last_errno = 0;
+       int type, lflags;
+       int mustexist = (old_sha1 && !is_null_sha1(old_sha1));
+       int resolve_flags = 0;
+       int attempts_remaining = 3;
+
+       assert(err);
+
+       lock = xcalloc(1, sizeof(struct ref_lock));
+
+       if (mustexist)
+               resolve_flags |= RESOLVE_REF_READING;
+       if (flags & REF_DELETING) {
+               resolve_flags |= RESOLVE_REF_ALLOW_BAD_NAME;
+               if (flags & REF_NODEREF)
+                       resolve_flags |= RESOLVE_REF_NO_RECURSE;
+       }
+
+       refname = resolve_ref_unsafe(refname, resolve_flags,
+                                    lock->old_oid.hash, &type);
+       if (!refname && errno == EISDIR) {
+               /*
+                * we are trying to lock foo but we used to
+                * have foo/bar which now does not exist;
+                * it is normal for the empty directory 'foo'
+                * to remain.
+                */
+               strbuf_git_path(&orig_ref_file, "%s", orig_refname);
+               if (remove_empty_directories(&orig_ref_file)) {
+                       last_errno = errno;
+                       if (!verify_refname_available_dir(orig_refname, extras, skip,
+                                                         get_loose_refs(&ref_cache), err))
+                               strbuf_addf(err, "there are still refs under '%s'",
+                                           orig_refname);
+                       goto error_return;
+               }
+               refname = resolve_ref_unsafe(orig_refname, resolve_flags,
+                                            lock->old_oid.hash, &type);
+       }
+       if (type_p)
+           *type_p = type;
+       if (!refname) {
+               last_errno = errno;
+               if (last_errno != ENOTDIR ||
+                   !verify_refname_available_dir(orig_refname, extras, skip,
+                                                 get_loose_refs(&ref_cache), err))
+                       strbuf_addf(err, "unable to resolve reference %s: %s",
+                                   orig_refname, strerror(last_errno));
+
+               goto error_return;
+       }
+       /*
+        * If the ref did not exist and we are creating it, make sure
+        * there is no existing packed ref whose name begins with our
+        * refname, nor a packed ref whose name is a proper prefix of
+        * our refname.
+        */
+       if (is_null_oid(&lock->old_oid) &&
+           verify_refname_available_dir(refname, extras, skip,
+                                        get_packed_refs(&ref_cache), err)) {
+               last_errno = ENOTDIR;
+               goto error_return;
+       }
+
+       lock->lk = xcalloc(1, sizeof(struct lock_file));
+
+       lflags = 0;
+       if (flags & REF_NODEREF) {
+               refname = orig_refname;
+               lflags |= LOCK_NO_DEREF;
+       }
+       lock->ref_name = xstrdup(refname);
+       lock->orig_ref_name = xstrdup(orig_refname);
+       strbuf_git_path(&ref_file, "%s", 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:
+               last_errno = errno;
+               strbuf_addf(err, "unable to create directory for %s",
+                           ref_file.buf);
+               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 (old_sha1 && verify_lock(lock, old_sha1, mustexist, err)) {
+               last_errno = errno;
+               goto error_return;
+       }
+       goto out;
+
+ error_return:
+       unlock_ref(lock);
+       lock = NULL;
+
+ out:
+       strbuf_release(&ref_file);
+       strbuf_release(&orig_ref_file);
+       errno = last_errno;
+       return lock;
+}
+
+/*
+ * Write an entry to the packed-refs file for the specified refname.
+ * If peeled is non-NULL, write it as the entry's peeled value.
+ */
+static void write_packed_entry(FILE *fh, char *refname, unsigned char *sha1,
+                              unsigned char *peeled)
+{
+       fprintf_or_die(fh, "%s %s\n", sha1_to_hex(sha1), refname);
+       if (peeled)
+               fprintf_or_die(fh, "^%s\n", sha1_to_hex(peeled));
+}
+
+/*
+ * An each_ref_entry_fn that writes the entry to a packed-refs file.
+ */
+static int write_packed_entry_fn(struct ref_entry *entry, void *cb_data)
+{
+       enum peel_status peel_status = peel_entry(entry, 0);
+
+       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+               error("internal error: %s is not a valid packed reference!",
+                     entry->name);
+       write_packed_entry(cb_data, entry->name, entry->u.value.oid.hash,
+                          peel_status == PEEL_PEELED ?
+                          entry->u.value.peeled.hash : NULL);
+       return 0;
+}
+
+/*
+ * Lock the packed-refs file for writing. Flags is passed to
+ * hold_lock_file_for_update(). Return 0 on success. On errors, set
+ * errno appropriately and return a nonzero value.
+ */
+static int lock_packed_refs(int flags)
+{
+       static int timeout_configured = 0;
+       static int timeout_value = 1000;
+
+       struct packed_ref_cache *packed_ref_cache;
+
+       if (!timeout_configured) {
+               git_config_get_int("core.packedrefstimeout", &timeout_value);
+               timeout_configured = 1;
+       }
+
+       if (hold_lock_file_for_update_timeout(
+                           &packlock, git_path("packed-refs"),
+                           flags, timeout_value) < 0)
+               return -1;
+       /*
+        * Get the current packed-refs while holding the lock.  If the
+        * packed-refs file has been modified since we last read it,
+        * this will automatically invalidate the cache and re-read
+        * the packed-refs file.
+        */
+       packed_ref_cache = get_packed_ref_cache(&ref_cache);
+       packed_ref_cache->lock = &packlock;
+       /* Increment the reference count to prevent it from being freed: */
+       acquire_packed_ref_cache(packed_ref_cache);
+       return 0;
+}
+
+/*
+ * Write the current version of the packed refs cache from memory to
+ * disk. The packed-refs file must already be locked for writing (see
+ * lock_packed_refs()). Return zero on success. On errors, set errno
+ * and return a nonzero value
+ */
+static int commit_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+       int error = 0;
+       int save_errno = 0;
+       FILE *out;
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+
+       out = fdopen_lock_file(packed_ref_cache->lock, "w");
+       if (!out)
+               die_errno("unable to fdopen packed-refs descriptor");
+
+       fprintf_or_die(out, "%s", PACKED_REFS_HEADER);
+       do_for_each_entry_in_dir(get_packed_ref_dir(packed_ref_cache),
+                                0, write_packed_entry_fn, out);
+
+       if (commit_lock_file(packed_ref_cache->lock)) {
+               save_errno = errno;
+               error = -1;
+       }
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       errno = save_errno;
+       return error;
+}
+
+/*
+ * Rollback the lockfile for the packed-refs file, and discard the
+ * in-memory packed reference cache.  (The packed-refs file will be
+ * read anew if it is needed again after this function is called.)
+ */
+static void rollback_packed_refs(void)
+{
+       struct packed_ref_cache *packed_ref_cache =
+               get_packed_ref_cache(&ref_cache);
+
+       if (!packed_ref_cache->lock)
+               die("internal error: packed-refs not locked");
+       rollback_lock_file(packed_ref_cache->lock);
+       packed_ref_cache->lock = NULL;
+       release_packed_ref_cache(packed_ref_cache);
+       clear_packed_ref_cache(&ref_cache);
+}
+
+struct ref_to_prune {
+       struct ref_to_prune *next;
+       unsigned char sha1[20];
+       char name[FLEX_ARRAY];
+};
+
+struct pack_refs_cb_data {
+       unsigned int flags;
+       struct ref_dir *packed_refs;
+       struct ref_to_prune *ref_to_prune;
+};
+
+/*
+ * An each_ref_entry_fn that is run over loose references only.  If
+ * the loose reference can be packed, add an entry in the packed ref
+ * cache.  If the reference should be pruned, also add it to
+ * ref_to_prune in the pack_refs_cb_data.
+ */
+static int pack_if_possible_fn(struct ref_entry *entry, void *cb_data)
+{
+       struct pack_refs_cb_data *cb = cb_data;
+       enum peel_status peel_status;
+       struct ref_entry *packed_entry;
+       int is_tag_ref = starts_with(entry->name, "refs/tags/");
+
+       /* Do not pack per-worktree refs: */
+       if (ref_type(entry->name) != REF_TYPE_NORMAL)
+               return 0;
+
+       /* ALWAYS pack tags */
+       if (!(cb->flags & PACK_REFS_ALL) && !is_tag_ref)
+               return 0;
+
+       /* Do not pack symbolic or broken refs: */
+       if ((entry->flag & REF_ISSYMREF) || !ref_resolves_to_object(entry))
+               return 0;
+
+       /* Add a packed ref cache entry equivalent to the loose entry. */
+       peel_status = peel_entry(entry, 1);
+       if (peel_status != PEEL_PEELED && peel_status != PEEL_NON_TAG)
+               die("internal error peeling reference %s (%s)",
+                   entry->name, oid_to_hex(&entry->u.value.oid));
+       packed_entry = find_ref(cb->packed_refs, entry->name);
+       if (packed_entry) {
+               /* Overwrite existing packed entry with info from loose entry */
+               packed_entry->flag = REF_ISPACKED | REF_KNOWS_PEELED;
+               oidcpy(&packed_entry->u.value.oid, &entry->u.value.oid);
+       } else {
+               packed_entry = create_ref_entry(entry->name, entry->u.value.oid.hash,
+                                               REF_ISPACKED | REF_KNOWS_PEELED, 0);
+               add_ref(cb->packed_refs, packed_entry);
+       }
+       oidcpy(&packed_entry->u.value.peeled, &entry->u.value.peeled);
+
+       /* Schedule the loose reference for pruning if requested. */
+       if ((cb->flags & PACK_REFS_PRUNE)) {
+               int namelen = strlen(entry->name) + 1;
+               struct ref_to_prune *n = xcalloc(1, sizeof(*n) + namelen);
+               hashcpy(n->sha1, entry->u.value.oid.hash);
+               memcpy(n->name, entry->name, namelen); /* includes NUL */
+               n->next = cb->ref_to_prune;
+               cb->ref_to_prune = n;
+       }
+       return 0;
+}
+
+/*
+ * Remove empty parents, but spare refs/ and immediate subdirs.
+ * Note: munges *name.
+ */
+static void try_remove_empty_parents(char *name)
+{
+       char *p, *q;
+       int i;
+       p = name;
+       for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */
+               while (*p && *p != '/')
+                       p++;
+               /* tolerate duplicate slashes; see check_refname_format() */
+               while (*p == '/')
+                       p++;
+       }
+       for (q = p; *q; q++)
+               ;
+       while (1) {
+               while (q > p && *q != '/')
+                       q--;
+               while (q > p && *(q-1) == '/')
+                       q--;
+               if (q == p)
+                       break;
+               *q = '\0';
+               if (rmdir(git_path("%s", name)))
+                       break;
+       }
+}
+
+/* make sure nobody touched the ref, and unlink */
+static void prune_ref(struct ref_to_prune *r)
+{
+       struct ref_transaction *transaction;
+       struct strbuf err = STRBUF_INIT;
+
+       if (check_refname_format(r->name, 0))
+               return;
+
+       transaction = ref_transaction_begin(&err);
+       if (!transaction ||
+           ref_transaction_delete(transaction, r->name, r->sha1,
+                                  REF_ISPRUNING, NULL, &err) ||
+           ref_transaction_commit(transaction, &err)) {
+               ref_transaction_free(transaction);
+               error("%s", err.buf);
+               strbuf_release(&err);
+               return;
+       }
+       ref_transaction_free(transaction);
+       strbuf_release(&err);
+       try_remove_empty_parents(r->name);
+}
+
+static void prune_refs(struct ref_to_prune *r)
+{
+       while (r) {
+               prune_ref(r);
+               r = r->next;
+       }
+}
+
+int pack_refs(unsigned int flags)
+{
+       struct pack_refs_cb_data cbdata;
+
+       memset(&cbdata, 0, sizeof(cbdata));
+       cbdata.flags = flags;
+
+       lock_packed_refs(LOCK_DIE_ON_ERROR);
+       cbdata.packed_refs = get_packed_refs(&ref_cache);
+
+       do_for_each_entry_in_dir(get_loose_refs(&ref_cache), 0,
+                                pack_if_possible_fn, &cbdata);
+
+       if (commit_packed_refs())
+               die_errno("unable to overwrite old ref-pack file");
+
+       prune_refs(cbdata.ref_to_prune);
+       return 0;
+}
+
+/*
+ * Rewrite the packed-refs file, omitting any refs listed in
+ * 'refnames'. On error, leave packed-refs unchanged, write an error
+ * message to 'err', and return a nonzero value.
+ *
+ * The refs in 'refnames' needn't be sorted. `err` must not be NULL.
+ */
+static int repack_without_refs(struct string_list *refnames, struct strbuf *err)
+{
+       struct ref_dir *packed;
+       struct string_list_item *refname;
+       int ret, needs_repacking = 0, removed = 0;
+
+       assert(err);
+
+       /* Look for a packed ref */
+       for_each_string_list_item(refname, refnames) {
+               if (get_packed_ref(refname->string)) {
+                       needs_repacking = 1;
+                       break;
+               }
+       }
+
+       /* Avoid locking if we have nothing to do */
+       if (!needs_repacking)
+               return 0; /* no refname exists in packed refs */
+
+       if (lock_packed_refs(0)) {
+               unable_to_lock_message(git_path("packed-refs"), errno, err);
+               return -1;
+       }
+       packed = get_packed_refs(&ref_cache);
+
+       /* Remove refnames from the cache */
+       for_each_string_list_item(refname, refnames)
+               if (remove_entry(packed, refname->string) != -1)
+                       removed = 1;
+       if (!removed) {
+               /*
+                * All packed entries disappeared while we were
+                * acquiring the lock.
+                */
+               rollback_packed_refs();
+               return 0;
+       }
+
+       /* Write what remains */
+       ret = commit_packed_refs();
+       if (ret)
+               strbuf_addf(err, "unable to overwrite old ref-pack file: %s",
+                           strerror(errno));
+       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;
+}
+
+int delete_refs(struct string_list *refnames)
+{
+       struct strbuf err = STRBUF_INIT;
+       int i, result = 0;
+
+       if (!refnames->nr)
+               return 0;
+
+       result = repack_without_refs(refnames, &err);
+       if (result) {
+               /*
+                * If we failed to rewrite the packed-refs file, then
+                * it is unsafe to try to remove loose refs, because
+                * doing so might expose an obsolete packed value for
+                * a reference that might even point at an object that
+                * has been garbage collected.
+                */
+               if (refnames->nr == 1)
+                       error(_("could not delete reference %s: %s"),
+                             refnames->items[0].string, err.buf);
+               else
+                       error(_("could not delete references: %s"), err.buf);
+
+               goto out;
+       }
+
+       for (i = 0; i < refnames->nr; i++) {
+               const char *refname = refnames->items[i].string;
+
+               if (delete_ref(refname, NULL, 0))
+                       result |= error(_("could not remove reference %s"), refname);
+       }
+
+out:
+       strbuf_release(&err);
+       return result;
+}
+
+/*
+ * People using contrib's git-new-workdir have .git/logs/refs ->
+ * /some/other/path/.git/logs/refs, and that may live on another device.
+ *
+ * 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"
+
+static int rename_tmp_log(const char *newrefname)
+{
+       int attempts_remaining = 4;
+       struct strbuf path = STRBUF_INIT;
+       int ret = -1;
+
+ 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(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;
+               }
+       }
+       ret = 0;
+out:
+       strbuf_release(&path);
+       return ret;
+}
+
+int verify_refname_available(const char *newname,
+                            struct string_list *extras,
+                            struct string_list *skip,
+                            struct strbuf *err)
+{
+       struct ref_dir *packed_refs = get_packed_refs(&ref_cache);
+       struct ref_dir *loose_refs = get_loose_refs(&ref_cache);
+
+       if (verify_refname_available_dir(newname, extras, skip,
+                                        packed_refs, err) ||
+           verify_refname_available_dir(newname, extras, skip,
+                                        loose_refs, err))
+               return -1;
+
+       return 0;
+}
+
+static int write_ref_to_lockfile(struct ref_lock *lock,
+                                const unsigned char *sha1, struct strbuf *err);
+static int commit_ref_update(struct ref_lock *lock,
+                            const unsigned char *sha1, const char *logmsg,
+                            int flags, struct strbuf *err);
+
+int rename_ref(const char *oldrefname, const char *newrefname, const char *logmsg)
+{
+       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);
+       const char *symref = NULL;
+       struct strbuf err = STRBUF_INIT;
+
+       if (log && S_ISLNK(loginfo.st_mode))
+               return error("reflog for %s is a symlink", oldrefname);
+
+       symref = resolve_ref_unsafe(oldrefname, RESOLVE_REF_READING,
+                                   orig_sha1, &flag);
+       if (flag & REF_ISSYMREF)
+               return error("refname %s is a symbolic ref, renaming it is not supported",
+                       oldrefname);
+       if (!symref)
+               return error("refname %s not found", oldrefname);
+
+       if (!rename_ref_available(oldrefname, newrefname))
+               return 1;
+
+       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 (delete_ref(oldrefname, orig_sha1, REF_NODEREF)) {
+               error("unable to delete old %s", oldrefname);
+               goto rollback;
+       }
+
+       if (!read_ref_full(newrefname, RESOLVE_REF_READING, sha1, NULL) &&
+           delete_ref(newrefname, sha1, REF_NODEREF)) {
+               if (errno==EISDIR) {
+                       struct strbuf path = STRBUF_INIT;
+                       int result;
+
+                       strbuf_git_path(&path, "%s", newrefname);
+                       result = remove_empty_directories(&path);
+                       strbuf_release(&path);
+
+                       if (result) {
+                               error("Directory not empty: %s", newrefname);
+                               goto rollback;
+                       }
+               } else {
+                       error("unable to delete existing %s", newrefname);
+                       goto rollback;
+               }
+       }
+
+       if (log && rename_tmp_log(newrefname))
+               goto rollback;
+
+       logmoved = log;
+
+       lock = lock_ref_sha1_basic(newrefname, NULL, NULL, NULL, 0, NULL, &err);
+       if (!lock) {
+               error("unable to rename '%s' to '%s': %s", oldrefname, newrefname, err.buf);
+               strbuf_release(&err);
+               goto rollback;
+       }
+       hashcpy(lock->old_oid.hash, orig_sha1);
+
+       if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+           commit_ref_update(lock, orig_sha1, logmsg, 0, &err)) {
+               error("unable to write current sha1 into %s: %s", newrefname, err.buf);
+               strbuf_release(&err);
+               goto rollback;
+       }
+
+       return 0;
+
+ rollback:
+       lock = lock_ref_sha1_basic(oldrefname, NULL, NULL, NULL, 0, NULL, &err);
+       if (!lock) {
+               error("unable to lock %s for rollback: %s", oldrefname, err.buf);
+               strbuf_release(&err);
+               goto rollbacklog;
+       }
+
+       flag = log_all_ref_updates;
+       log_all_ref_updates = 0;
+       if (write_ref_to_lockfile(lock, orig_sha1, &err) ||
+           commit_ref_update(lock, orig_sha1, NULL, 0, &err)) {
+               error("unable to write current sha1 into %s: %s", oldrefname, err.buf);
+               strbuf_release(&err);
+       }
+       log_all_ref_updates = flag;
+
+ rollbacklog:
+       if (logmoved && rename(git_path("logs/%s", newrefname), git_path("logs/%s", oldrefname)))
+               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",
+                       oldrefname, strerror(errno));
+
+       return 1;
+}
+
+static int close_ref(struct ref_lock *lock)
+{
+       if (close_lock_file(lock->lk))
+               return -1;
+       return 0;
+}
+
+static int commit_ref(struct ref_lock *lock)
+{
+       if (commit_lock_file(lock->lk))
+               return -1;
+       return 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.
+ */
+static int log_ref_setup(const char *refname, struct strbuf *logfile, struct strbuf *err, int force_create)
+{
+       int logfd, oflags = O_APPEND | O_WRONLY;
+
+       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;
+       }
+
+       logfd = open(logfile->buf, oflags, 0666);
+       if (logfd < 0) {
+               if (!(oflags & O_CREAT) && (errno == ENOENT || errno == EISDIR))
+                       return 0;
+
+               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);
+               }
+
+               if (logfd < 0) {
+                       strbuf_addf(err, "unable to append to %s: %s",
+                                   logfile->buf, strerror(errno));
+                       return -1;
+               }
+       }
+
+       adjust_shared_perm(logfile->buf);
+       close(logfd);
+       return 0;
+}
+
+
+int safe_create_reflog(const char *refname, int force_create, struct strbuf *err)
+{
+       int ret;
+       struct strbuf sb = STRBUF_INIT;
+
+       ret = log_ref_setup(refname, &sb, err, force_create);
+       strbuf_release(&sb);
+       return ret;
+}
+
+static int log_ref_write_fd(int fd, const unsigned char *old_sha1,
+                           const unsigned char *new_sha1,
+                           const char *committer, const char *msg)
+{
+       int msglen, written;
+       unsigned maxlen, len;
+       char *logrec;
+
+       msglen = msg ? strlen(msg) : 0;
+       maxlen = strlen(committer) + msglen + 100;
+       logrec = xmalloc(maxlen);
+       len = xsnprintf(logrec, maxlen, "%s %s %s\n",
+                       sha1_to_hex(old_sha1),
+                       sha1_to_hex(new_sha1),
+                       committer);
+       if (msglen)
+               len += copy_reflog_msg(logrec + len - 1, msg) - 1;
+
+       written = len <= maxlen ? write_in_full(fd, logrec, len) : -1;
+       free(logrec);
+       if (written != len)
+               return -1;
+
+       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)
+{
+       int logfd, result, oflags = O_APPEND | O_WRONLY;
+
+       if (log_all_ref_updates < 0)
+               log_all_ref_updates = !is_bare_repository();
+
+       result = log_ref_setup(refname, logfile, err, flags & REF_FORCE_CREATE_REFLOG);
+
+       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));
+               close(logfd);
+               return -1;
+       }
+       if (close(logfd)) {
+               strbuf_addf(err, "unable to append to %s: %s", logfile->buf,
+                           strerror(errno));
+               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
+ * return -1.
+ */
+static int write_ref_to_lockfile(struct ref_lock *lock,
+                                const unsigned char *sha1, struct strbuf *err)
+{
+       static char term = '\n';
+       struct object *o;
+       int fd;
+
+       o = parse_object(sha1);
+       if (!o) {
+               strbuf_addf(err,
+                           "Trying to write ref %s with nonexistent object %s",
+                           lock->ref_name, sha1_to_hex(sha1));
+               unlock_ref(lock);
+               return -1;
+       }
+       if (o->type != OBJ_COMMIT && is_branch(lock->ref_name)) {
+               strbuf_addf(err,
+                           "Trying to write non-commit object %s to branch %s",
+                           sha1_to_hex(sha1), lock->ref_name);
+               unlock_ref(lock);
+               return -1;
+       }
+       fd = get_lock_file_fd(lock->lk);
+       if (write_in_full(fd, sha1_to_hex(sha1), 40) != 40 ||
+           write_in_full(fd, &term, 1) != 1 ||
+           close_ref(lock) < 0) {
+               strbuf_addf(err,
+                           "Couldn't write %s", get_lock_file_path(lock->lk));
+               unlock_ref(lock);
+               return -1;
+       }
+       return 0;
+}
+
+/*
+ * Commit a change to a loose reference that has already been written
+ * to the loose reference lockfile. Also update the reflogs if
+ * necessary, using the specified lockmsg (which can be NULL).
+ */
+static int commit_ref_update(struct ref_lock *lock,
+                            const unsigned char *sha1, const char *logmsg,
+                            int flags, struct strbuf *err)
+{
+       clear_loose_ref_cache(&ref_cache);
+       if (log_ref_write(lock->ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0 ||
+           (strcmp(lock->ref_name, lock->orig_ref_name) &&
+            log_ref_write(lock->orig_ref_name, lock->old_oid.hash, sha1, logmsg, flags, err) < 0)) {
+               char *old_msg = strbuf_detach(err, NULL);
+               strbuf_addf(err, "Cannot update the ref '%s': %s",
+                           lock->ref_name, old_msg);
+               free(old_msg);
+               unlock_ref(lock);
+               return -1;
+       }
+       if (strcmp(lock->orig_ref_name, "HEAD") != 0) {
+               /*
+                * Special hack: If a branch is updated directly and HEAD
+                * points to it (may happen on the remote side of a push
+                * for example) then logically the HEAD reflog should be
+                * updated too.
+                * A generic solution implies reverse symref information,
+                * but finding all symrefs pointing to the given branch
+                * would be rather costly for this rare event (the direct
+                * update of a branch) to be worth it.  So let's cheat and
+                * check with HEAD only which should cover 99% of all usage
+                * scenarios (even 100% of the default ones).
+                */
+               unsigned char head_sha1[20];
+               int head_flag;
+               const char *head_ref;
+               head_ref = resolve_ref_unsafe("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)) {
+                               error("%s", log_err.buf);
+                               strbuf_release(&log_err);
+                       }
+               }
+       }
+       if (commit_ref(lock)) {
+               error("Couldn't set %s", lock->ref_name);
+               unlock_ref(lock);
+               return -1;
+       }
+
+       unlock_ref(lock);
+       return 0;
+}
+
+int create_symref(const char *ref_target, const char *refs_heads_master,
+                 const char *logmsg)
+{
+       char *lockpath = NULL;
+       char ref[1000];
+       int fd, len, written;
+       char *git_HEAD = git_pathdup("%s", ref_target);
+       unsigned char old_sha1[20], new_sha1[20];
+       struct strbuf err = STRBUF_INIT;
+
+       if (logmsg && read_ref(ref_target, old_sha1))
+               hashclr(old_sha1);
+
+       if (safe_create_leading_directories(git_HEAD) < 0)
+               return error("unable to create directory for %s", git_HEAD);
+
+#ifndef NO_SYMLINK_HEAD
+       if (prefer_symlink_refs) {
+               unlink(git_HEAD);
+               if (!symlink(refs_heads_master, git_HEAD))
+                       goto done;
+               fprintf(stderr, "no symlink - falling back to symbolic ref\n");
+       }
+#endif
+
+       len = snprintf(ref, sizeof(ref), "ref: %s\n", refs_heads_master);
+       if (sizeof(ref) <= len) {
+               error("refname too long: %s", refs_heads_master);
+               goto error_free_return;
+       }
+       lockpath = mkpathdup("%s.lock", git_HEAD);
+       fd = open(lockpath, O_CREAT | O_EXCL | O_WRONLY, 0666);
+       if (fd < 0) {
+               error("Unable to open %s for writing", lockpath);
+               goto error_free_return;
+       }
+       written = write_in_full(fd, ref, len);
+       if (close(fd) != 0 || written != len) {
+               error("Unable to write to %s", lockpath);
+               goto error_unlink_return;
+       }
+       if (rename(lockpath, git_HEAD) < 0) {
+               error("Unable to create %s", git_HEAD);
+               goto error_unlink_return;
+       }
+       if (adjust_shared_perm(git_HEAD)) {
+               error("Unable to fix permissions on %s", lockpath);
+       error_unlink_return:
+               unlink_or_warn(lockpath);
+       error_free_return:
+               free(lockpath);
+               free(git_HEAD);
+               return -1;
+       }
+       free(lockpath);
+
+#ifndef NO_SYMLINK_HEAD
+       done:
+#endif
+       if (logmsg && !read_ref(refs_heads_master, new_sha1) &&
+               log_ref_write(ref_target, old_sha1, new_sha1, logmsg, 0, &err)) {
+               error("%s", err.buf);
+               strbuf_release(&err);
+       }
+
+       free(git_HEAD);
+       return 0;
+}
+
+int reflog_exists(const char *refname)
+{
+       struct stat st;
+
+       return !lstat(git_path("logs/%s", refname), &st) &&
+               S_ISREG(st.st_mode);
+}
+
+int delete_reflog(const char *refname)
+{
+       return remove_path(git_path("logs/%s", refname));
+}
+
+static int show_one_reflog_ent(struct strbuf *sb, each_reflog_ent_fn fn, void *cb_data)
+{
+       unsigned char osha1[20], nsha1[20];
+       char *email_end, *message;
+       unsigned long timestamp;
+       int tz;
+
+       /* 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, '>')) ||
+           email_end[1] != ' ' ||
+           !(timestamp = strtoul(email_end + 2, &message, 10)) ||
+           !message || message[0] != ' ' ||
+           (message[1] != '+' && message[1] != '-') ||
+           !isdigit(message[2]) || !isdigit(message[3]) ||
+           !isdigit(message[4]) || !isdigit(message[5]))
+               return 0; /* corrupt? */
+       email_end[1] = '\0';
+       tz = strtol(message + 1, NULL, 10);
+       if (message[6] != '\t')
+               message += 6;
+       else
+               message += 7;
+       return fn(osha1, nsha1, sb->buf + 82, timestamp, tz, message, cb_data);
+}
+
+static char *find_beginning_of_line(char *bob, char *scan)
+{
+       while (bob < scan && *(--scan) != '\n')
+               ; /* keep scanning backwards */
+       /*
+        * Return either beginning of the buffer, or LF at the end of
+        * the previous line.
+        */
+       return scan;
+}
+
+int for_each_reflog_ent_reverse(const char *refname, each_reflog_ent_fn fn, void *cb_data)
+{
+       struct strbuf sb = STRBUF_INIT;
+       FILE *logfp;
+       long pos;
+       int ret = 0, at_tail = 1;
+
+       logfp = fopen(git_path("logs/%s", refname), "r");
+       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));
+       pos = ftell(logfp);
+       while (!ret && 0 < pos) {
+               int cnt;
+               size_t nread;
+               char buf[BUFSIZ];
+               char *endp, *scanp;
+
+               /* 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));
+               nread = fread(buf, cnt, 1, logfp);
+               if (nread != 1)
+                       return error("cannot read %d bytes from reflog for %s: %s",
+                                    cnt, refname, strerror(errno));
+               pos -= cnt;
+
+               scanp = endp = buf + cnt;
+               if (at_tail && scanp[-1] == '\n')
+                       /* Looking at the final LF at the end of the file */
+                       scanp--;
+               at_tail = 0;
+
+               while (buf < scanp) {
+                       /*
+                        * terminating LF of the previous line, or the beginning
+                        * of the buffer.
+                        */
+                       char *bp;
+
+                       bp = find_beginning_of_line(buf, scanp);
+
+                       if (*bp == '\n') {
+                               /*
+                                * The newline is the end of the previous line,
+                                * so we know we have complete line starting
+                                * at (bp + 1). Prefix it onto any prior data
+                                * we collected for the line and process it.
+                                */
+                               strbuf_splice(&sb, 0, 0, bp + 1, endp - (bp + 1));
+                               scanp = bp;
+                               endp = bp + 1;
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               if (ret)
+                                       break;
+                       } else if (!pos) {
+                               /*
+                                * We are at the start of the buffer, and the
+                                * start of the file; there is no previous
+                                * line, and we have everything for this one.
+                                * Process it, and we can end the loop.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
+                               ret = show_one_reflog_ent(&sb, fn, cb_data);
+                               strbuf_reset(&sb);
+                               break;
+                       }
+
+                       if (bp == buf) {
+                               /*
+                                * We are at the start of the buffer, and there
+                                * is more file to read backwards. Which means
+                                * we are in the middle of a line. Note that we
+                                * may get here even if *bp was a newline; that
+                                * just means we are at the exact end of the
+                                * previous line, rather than some spot in the
+                                * middle.
+                                *
+                                * Save away what we have to be combined with
+                                * the data from the next read.
+                                */
+                               strbuf_splice(&sb, 0, 0, buf, endp - buf);
+                               break;
+                       }
+               }
+
+       }
+       if (!ret && sb.len)
+               die("BUG: reverse reflog parser had leftover data");
+
+       fclose(logfp);
+       strbuf_release(&sb);
+       return ret;
+}
+
+int for_each_reflog_ent(const char *refname, each_reflog_ent_fn fn, void *cb_data)
+{
+       FILE *logfp;
+       struct strbuf sb = STRBUF_INIT;
+       int ret = 0;
+
+       logfp = fopen(git_path("logs/%s", refname), "r");
+       if (!logfp)
+               return -1;
+
+       while (!ret && !strbuf_getwholeline(&sb, logfp, '\n'))
+               ret = show_one_reflog_ent(&sb, fn, cb_data);
+       fclose(logfp);
+       strbuf_release(&sb);
+       return ret;
+}
+/*
+ * Call fn for each reflog in the namespace indicated by name.  name
+ * must be empty or end with '/'.  Name will be used as a scratch
+ * space, but its contents will be restored before return.
+ */
+static int do_for_each_reflog(struct strbuf *name, each_ref_fn fn, void *cb_data)
+{
+       DIR *d = opendir(git_path("logs/%s", name->buf));
+       int retval = 0;
+       struct dirent *de;
+       int oldlen = name->len;
+
+       if (!d)
+               return name->len ? errno : 0;
+
+       while ((de = readdir(d)) != NULL) {
+               struct stat st;
+
+               if (de->d_name[0] == '.')
+                       continue;
+               if (ends_with(de->d_name, ".lock"))
+                       continue;
+               strbuf_addstr(name, de->d_name);
+               if (stat(git_path("logs/%s", name->buf), &st) < 0) {
+                       ; /* silently ignore */
+               } else {
+                       if (S_ISDIR(st.st_mode)) {
+                               strbuf_addch(name, '/');
+                               retval = do_for_each_reflog(name, fn, cb_data);
+                       } else {
+                               struct object_id oid;
+
+                               if (read_ref_full(name->buf, 0, oid.hash, NULL))
+                                       retval = error("bad ref for %s", name->buf);
+                               else
+                                       retval = fn(name->buf, &oid, 0, cb_data);
+                       }
+                       if (retval)
+                               break;
+               }
+               strbuf_setlen(name, oldlen);
+       }
+       closedir(d);
+       return retval;
+}
+
+int for_each_reflog(each_ref_fn fn, void *cb_data)
+{
+       int retval;
+       struct strbuf name;
+       strbuf_init(&name, PATH_MAX);
+       retval = do_for_each_reflog(&name, fn, cb_data);
+       strbuf_release(&name);
+       return retval;
+}
+
+static int ref_update_reject_duplicates(struct string_list *refnames,
+                                       struct strbuf *err)
+{
+       int i, n = refnames->nr;
+
+       assert(err);
+
+       for (i = 1; i < n; i++)
+               if (!strcmp(refnames->items[i - 1].string, refnames->items[i].string)) {
+                       strbuf_addf(err,
+                                   "Multiple updates for ref '%s' not allowed.",
+                                   refnames->items[i].string);
+                       return 1;
+               }
+       return 0;
+}
+
+int ref_transaction_commit(struct ref_transaction *transaction,
+                          struct strbuf *err)
+{
+       int ret = 0, i;
+       int n = transaction->nr;
+       struct ref_update **updates = transaction->updates;
+       struct string_list refs_to_delete = STRING_LIST_INIT_NODUP;
+       struct string_list_item *ref_to_delete;
+       struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+
+       assert(err);
+
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: commit called for transaction that is not open");
+
+       if (!n) {
+               transaction->state = REF_TRANSACTION_CLOSED;
+               return 0;
+       }
+
+       /* Fail if a refname appears more than once in the transaction: */
+       for (i = 0; i < n; i++)
+               string_list_append(&affected_refnames, updates[i]->refname);
+       string_list_sort(&affected_refnames);
+       if (ref_update_reject_duplicates(&affected_refnames, err)) {
+               ret = TRANSACTION_GENERIC_ERROR;
+               goto cleanup;
+       }
+
+       /*
+        * Acquire all locks, verify old values if provided, check
+        * that new values are valid, and write new values to the
+        * lockfiles, ready to be activated. Only keep one lockfile
+        * open at a time to avoid running out of file descriptors.
+        */
+       for (i = 0; i < n; i++) {
+               struct ref_update *update = updates[i];
+
+               if ((update->flags & REF_HAVE_NEW) &&
+                   is_null_sha1(update->new_sha1))
+                       update->flags |= REF_DELETING;
+               update->lock = lock_ref_sha1_basic(
+                               update->refname,
+                               ((update->flags & REF_HAVE_OLD) ?
+                                update->old_sha1 : NULL),
+                               &affected_refnames, NULL,
+                               update->flags,
+                               &update->type,
+                               err);
+               if (!update->lock) {
+                       char *reason;
+
+                       ret = (errno == ENOTDIR)
+                               ? TRANSACTION_NAME_CONFLICT
+                               : TRANSACTION_GENERIC_ERROR;
+                       reason = strbuf_detach(err, NULL);
+                       strbuf_addf(err, "cannot lock ref '%s': %s",
+                                   update->refname, reason);
+                       free(reason);
+                       goto cleanup;
+               }
+               if ((update->flags & REF_HAVE_NEW) &&
+                   !(update->flags & REF_DELETING)) {
+                       int overwriting_symref = ((update->type & REF_ISSYMREF) &&
+                                                 (update->flags & REF_NODEREF));
+
+                       if (!overwriting_symref &&
+                           !hashcmp(update->lock->old_oid.hash, update->new_sha1)) {
+                               /*
+                                * The reference already has the desired
+                                * value, so we don't need to write it.
+                                */
+                       } else if (write_ref_to_lockfile(update->lock,
+                                                        update->new_sha1,
+                                                        err)) {
+                               char *write_err = strbuf_detach(err, NULL);
+
+                               /*
+                                * The lock was freed upon failure of
+                                * write_ref_to_lockfile():
+                                */
+                               update->lock = NULL;
+                               strbuf_addf(err,
+                                           "cannot update the ref '%s': %s",
+                                           update->refname, write_err);
+                               free(write_err);
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto cleanup;
+                       } else {
+                               update->flags |= REF_NEEDS_COMMIT;
+                       }
+               }
+               if (!(update->flags & REF_NEEDS_COMMIT)) {
+                       /*
+                        * We didn't have to write anything to the lockfile.
+                        * Close it to free up the file descriptor:
+                        */
+                       if (close_ref(update->lock)) {
+                               strbuf_addf(err, "Couldn't close %s.lock",
+                                           update->refname);
+                               goto cleanup;
+                       }
+               }
+       }
+
+       /* Perform updates first so live commits remain referenced */
+       for (i = 0; i < n; i++) {
+               struct ref_update *update = updates[i];
+
+               if (update->flags & REF_NEEDS_COMMIT) {
+                       if (commit_ref_update(update->lock,
+                                             update->new_sha1, update->msg,
+                                             update->flags, err)) {
+                               /* freed by commit_ref_update(): */
+                               update->lock = NULL;
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto cleanup;
+                       } else {
+                               /* freed by commit_ref_update(): */
+                               update->lock = NULL;
+                       }
+               }
+       }
+
+       /* Perform deletes now that updates are safely completed */
+       for (i = 0; i < n; i++) {
+               struct ref_update *update = updates[i];
+
+               if (update->flags & REF_DELETING) {
+                       if (delete_ref_loose(update->lock, update->type, err)) {
+                               ret = TRANSACTION_GENERIC_ERROR;
+                               goto cleanup;
+                       }
+
+                       if (!(update->flags & REF_ISPRUNING))
+                               string_list_append(&refs_to_delete,
+                                                  update->lock->ref_name);
+               }
+       }
+
+       if (repack_without_refs(&refs_to_delete, err)) {
+               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));
+       clear_loose_ref_cache(&ref_cache);
+
+cleanup:
+       transaction->state = REF_TRANSACTION_CLOSED;
+
+       for (i = 0; i < n; i++)
+               if (updates[i]->lock)
+                       unlock_ref(updates[i]->lock);
+       string_list_clear(&refs_to_delete, 0);
+       string_list_clear(&affected_refnames, 0);
+       return ret;
+}
+
+static int ref_present(const char *refname,
+                      const struct object_id *oid, int flags, void *cb_data)
+{
+       struct string_list *affected_refnames = cb_data;
+
+       return string_list_has_string(affected_refnames, refname);
+}
+
+int initial_ref_transaction_commit(struct ref_transaction *transaction,
+                                  struct strbuf *err)
+{
+       int ret = 0, i;
+       int n = transaction->nr;
+       struct ref_update **updates = transaction->updates;
+       struct string_list affected_refnames = STRING_LIST_INIT_NODUP;
+
+       assert(err);
+
+       if (transaction->state != REF_TRANSACTION_OPEN)
+               die("BUG: commit called for transaction that is not open");
+
+       /* Fail if a refname appears more than once in the transaction: */
+       for (i = 0; i < n; i++)
+               string_list_append(&affected_refnames, updates[i]->refname);
+       string_list_sort(&affected_refnames);
+       if (ref_update_reject_duplicates(&affected_refnames, err)) {
+               ret = TRANSACTION_GENERIC_ERROR;
+               goto cleanup;
+       }
+
+       /*
+        * It's really undefined to call this function in an active
+        * repository or when there are existing references: we are
+        * only locking and changing packed-refs, so (1) any
+        * simultaneous processes might try to change a reference at
+        * the same time we do, and (2) any existing loose versions of
+        * the references that we are setting would have precedence
+        * over our values. But some remote helpers create the remote
+        * "HEAD" and "master" branches before calling this function,
+        * 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))
+               die("BUG: initial ref transaction called with existing refs");
+
+       for (i = 0; i < n; i++) {
+               struct ref_update *update = updates[i];
+
+               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)) {
+                       ret = TRANSACTION_NAME_CONFLICT;
+                       goto cleanup;
+               }
+       }
+
+       if (lock_packed_refs(0)) {
+               strbuf_addf(err, "unable to lock packed-refs file: %s",
+                           strerror(errno));
+               ret = TRANSACTION_GENERIC_ERROR;
+               goto cleanup;
+       }
+
+       for (i = 0; i < n; i++) {
+               struct ref_update *update = updates[i];
+
+               if ((update->flags & REF_HAVE_NEW) &&
+                   !is_null_sha1(update->new_sha1))
+                       add_packed_ref(update->refname, update->new_sha1);
+       }
+
+       if (commit_packed_refs()) {
+               strbuf_addf(err, "unable to commit packed-refs file: %s",
+                           strerror(errno));
+               ret = TRANSACTION_GENERIC_ERROR;
+               goto cleanup;
+       }
+
+cleanup:
+       transaction->state = REF_TRANSACTION_CLOSED;
+       string_list_clear(&affected_refnames, 0);
+       return ret;
+}
+
+struct expire_reflog_cb {
+       unsigned int flags;
+       reflog_expiry_should_prune_fn *should_prune_fn;
+       void *policy_cb;
+       FILE *newlog;
+       unsigned char last_kept_sha1[20];
+};
+
+static int expire_reflog_ent(unsigned char *osha1, unsigned char *nsha1,
+                            const char *email, unsigned long timestamp, int tz,
+                            const char *message, void *cb_data)
+{
+       struct expire_reflog_cb *cb = cb_data;
+       struct expire_reflog_policy_cb *policy_cb = cb->policy_cb;
+
+       if (cb->flags & EXPIRE_REFLOGS_REWRITE)
+               osha1 = cb->last_kept_sha1;
+
+       if ((*cb->should_prune_fn)(osha1, nsha1, email, timestamp, tz,
+                                  message, policy_cb)) {
+               if (!cb->newlog)
+                       printf("would prune %s", message);
+               else if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
+                       printf("prune %s", message);
+       } else {
+               if (cb->newlog) {
+                       fprintf(cb->newlog, "%s %s %s %lu %+05d\t%s",
+                               sha1_to_hex(osha1), sha1_to_hex(nsha1),
+                               email, timestamp, tz, message);
+                       hashcpy(cb->last_kept_sha1, nsha1);
+               }
+               if (cb->flags & EXPIRE_REFLOGS_VERBOSE)
+                       printf("keep %s", message);
+       }
+       return 0;
+}
+
+int reflog_expire(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)
+{
+       static struct lock_file reflog_lock;
+       struct expire_reflog_cb cb;
+       struct ref_lock *lock;
+       char *log_file;
+       int status = 0;
+       int type;
+       struct strbuf err = STRBUF_INIT;
+
+       memset(&cb, 0, sizeof(cb));
+       cb.flags = flags;
+       cb.policy_cb = policy_cb_data;
+       cb.should_prune_fn = should_prune_fn;
+
+       /*
+        * The reflog file is locked by holding the lock on the
+        * reference itself, plus we might need to update the
+        * reference if --updateref was specified:
+        */
+       lock = lock_ref_sha1_basic(refname, sha1, NULL, NULL, 0, &type, &err);
+       if (!lock) {
+               error("cannot lock ref '%s': %s", refname, err.buf);
+               strbuf_release(&err);
+               return -1;
+       }
+       if (!reflog_exists(refname)) {
+               unlock_ref(lock);
+               return 0;
+       }
+
+       log_file = git_pathdup("logs/%s", refname);
+       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+               /*
+                * Even though holding $GIT_DIR/logs/$reflog.lock has
+                * no locking implications, we use the lock_file
+                * machinery here anyway because it does a lot of the
+                * work we need, including cleaning up if the program
+                * exits unexpectedly.
+                */
+               if (hold_lock_file_for_update(&reflog_lock, log_file, 0) < 0) {
+                       struct strbuf err = STRBUF_INIT;
+                       unable_to_lock_message(log_file, errno, &err);
+                       error("%s", err.buf);
+                       strbuf_release(&err);
+                       goto failure;
+               }
+               cb.newlog = fdopen_lock_file(&reflog_lock, "w");
+               if (!cb.newlog) {
+                       error("cannot fdopen %s (%s)",
+                             get_lock_file_path(&reflog_lock), strerror(errno));
+                       goto failure;
+               }
+       }
+
+       (*prepare_fn)(refname, sha1, cb.policy_cb);
+       for_each_reflog_ent(refname, expire_reflog_ent, &cb);
+       (*cleanup_fn)(cb.policy_cb);
+
+       if (!(flags & EXPIRE_REFLOGS_DRY_RUN)) {
+               /*
+                * It doesn't make sense to adjust a reference pointed
+                * to by a symbolic ref based on expiring entries in
+                * the symbolic reference's reflog. Nor can we update
+                * a reference if there are no remaining reflog
+                * entries.
+                */
+               int update = (flags & EXPIRE_REFLOGS_UPDATE_REF) &&
+                       !(type & REF_ISSYMREF) &&
+                       !is_null_sha1(cb.last_kept_sha1);
+
+               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 ||
+                           write_str_in_full(get_lock_file_fd(lock->lk), "\n") != 1 ||
+                           close_ref(lock) < 0)) {
+                       status |= error("couldn't write %s",
+                                       get_lock_file_path(lock->lk));
+                       rollback_lock_file(&reflog_lock);
+               } else if (commit_lock_file(&reflog_lock)) {
+                       status |= error("unable to write reflog '%s' (%s)",
+                                       log_file, strerror(errno));
+               } else if (update && commit_ref(lock)) {
+                       status |= error("couldn't set %s", lock->ref_name);
+               }
+       }
+       free(log_file);
+       unlock_ref(lock);
+       return status;
+
+ failure:
+       rollback_lock_file(&reflog_lock);
+       free(log_file);
+       unlock_ref(lock);
+       return -1;
+}
diff --git a/refs/refs-internal.h b/refs/refs-internal.h
new file mode 100644 (file)
index 0000000..c7dded3
--- /dev/null
@@ -0,0 +1,200 @@
+#ifndef REFS_REFS_INTERNAL_H
+#define REFS_REFS_INTERNAL_H
+
+/*
+ * Data structures and functions for the internal use of the refs
+ * module. Code outside of the refs module should use only the public
+ * functions defined in "refs.h", and should *not* include this file.
+ */
+
+/*
+ * Flag passed to lock_ref_sha1_basic() telling it to tolerate broken
+ * refs (i.e., because the reference is about to be deleted anyway).
+ */
+#define REF_DELETING   0x02
+
+/*
+ * Used as a flag in ref_update::flags when a loose ref is being
+ * pruned.
+ */
+#define REF_ISPRUNING  0x04
+
+/*
+ * Used as a flag in ref_update::flags when the reference should be
+ * updated to new_sha1.
+ */
+#define REF_HAVE_NEW   0x08
+
+/*
+ * Used as a flag in ref_update::flags when old_sha1 should be
+ * checked.
+ */
+#define REF_HAVE_OLD   0x10
+
+/*
+ * Used as a flag in ref_update::flags when the lockfile needs to be
+ * committed.
+ */
+#define REF_NEEDS_COMMIT 0x20
+
+/*
+ * 0x40 is REF_FORCE_CREATE_REFLOG, so skip it if you're adding a
+ * value to ref_update::flags
+ */
+
+/*
+ * Return true iff refname is minimally safe. "Safe" here means that
+ * deleting a loose reference by this name will not do any damage, for
+ * example by causing a file that is not a reference to be deleted.
+ * 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").
+ */
+int refname_is_safe(const char *refname);
+
+enum peel_status {
+       /* object was peeled successfully: */
+       PEEL_PEELED = 0,
+
+       /*
+        * object cannot be peeled because the named object (or an
+        * object referred to by a tag in the peel chain), does not
+        * exist.
+        */
+       PEEL_INVALID = -1,
+
+       /* object cannot be peeled because it is not a tag: */
+       PEEL_NON_TAG = -2,
+
+       /* ref_entry contains no peeled value because it is a symref: */
+       PEEL_IS_SYMREF = -3,
+
+       /*
+        * ref_entry cannot be peeled because it is broken (i.e., the
+        * symbolic reference cannot even be resolved to an object
+        * name):
+        */
+       PEEL_BROKEN = -4
+};
+
+/*
+ * Peel the named object; i.e., if the object is a tag, resolve the
+ * tag recursively until a non-tag is found.  If successful, store the
+ * result to sha1 and return PEEL_PEELED.  If the object is not a tag
+ * or is not valid, return PEEL_NON_TAG or PEEL_INVALID, respectively,
+ * and leave sha1 unchanged.
+ */
+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,
+                            struct string_list *extras,
+                            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,
+ * because reflog file is one line per entry.
+ */
+int copy_reflog_msg(char *buf, const char *msg);
+
+int should_autocreate_reflog(const char *refname);
+
+/**
+ * Information needed for a single ref update. Set new_sha1 to the new
+ * value or to null_sha1 to delete the ref. To check the old value
+ * while the ref is locked, set (flags & REF_HAVE_OLD) and set
+ * old_sha1 to the old value, or to null_sha1 to ensure the ref does
+ * not exist before update.
+ */
+struct ref_update {
+       /*
+        * If (flags & REF_HAVE_NEW), set the reference to this value:
+        */
+       unsigned char new_sha1[20];
+       /*
+        * If (flags & REF_HAVE_OLD), check that the reference
+        * previously had this value:
+        */
+       unsigned char old_sha1[20];
+       /*
+        * One or more of REF_HAVE_NEW, REF_HAVE_OLD, REF_NODEREF,
+        * REF_DELETING, and REF_ISPRUNING:
+        */
+       unsigned int flags;
+       struct ref_lock *lock;
+       int type;
+       char *msg;
+       const char refname[FLEX_ARRAY];
+};
+
+/*
+ * Transaction states.
+ * OPEN:   The transaction is in a valid state and can accept new updates.
+ *         An OPEN transaction can be committed.
+ * CLOSED: A closed transaction is no longer active and no other operations
+ *         than free can be used on it in this state.
+ *         A transaction can either become closed by successfully committing
+ *         an active transaction or if there is a failure while building
+ *         the transaction thus rendering it failed/inactive.
+ */
+enum ref_transaction_state {
+       REF_TRANSACTION_OPEN   = 0,
+       REF_TRANSACTION_CLOSED = 1
+};
+
+/*
+ * Data structure for holding a reference transaction, which can
+ * consist of checks and updates to multiple references, carried out
+ * as atomically as possible.  This structure is opaque to callers.
+ */
+struct ref_transaction {
+       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
+ * the trailing slash (e.g., "refs/heads/foo/"). Ignore any
+ * conflicting references that are found in skip. If there is a
+ * conflicting reference, return its name.
+ *
+ * extras and skip must be sorted lists of reference names. Either one
+ * can be NULL, signifying the empty list.
+ */
+const char *find_descendant_ref(const char *dirname,
+                               const struct string_list *extras,
+                               const struct string_list *skip);
+
+int rename_ref_available(const char *oldname, const char *newname);
+
+#endif /* REFS_REFS_INTERNAL_H */
index cc7a8a66faa96ba9bc8ae82e72d0202c0a802ba1..f404faf0f4b9ec298ccbae34fae0c79283884d26 100644 (file)
@@ -169,7 +169,7 @@ static struct ref *parse_info_refs(struct discovery *heads)
                        data[i] = 0;
                        ref_name = mid + 1;
                        ref = alloc_ref(ref_name);
-                       get_sha1_hex(start, ref->old_sha1);
+                       get_oid_hex(start, &ref->old_oid);
                        if (!refs)
                                refs = ref;
                        if (last_ref)
@@ -348,7 +348,7 @@ static void output_refs(struct ref *refs)
                if (posn->symref)
                        printf("@%s %s\n", posn->symref, posn->name);
                else
-                       printf("%s %s\n", sha1_to_hex(posn->old_sha1), posn->name);
+                       printf("%s %s\n", oid_to_hex(&posn->old_oid), posn->name);
        }
        printf("\n");
        fflush(stdout);
@@ -702,7 +702,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
        if (options.depth)
                die("dumb http transport does not support --depth");
        for (i = 0; i < nr_heads; i++)
-               targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
+               targets[i] = xstrdup(oid_to_hex(&to_fetch[i]->old_oid));
 
        walker = get_http_walker(url.buf);
        walker->get_all = 1;
@@ -763,7 +763,7 @@ static int fetch_git(struct discovery *heads,
                if (!*ref->name)
                        die("cannot fetch by sha1 over smart http");
                packet_buf_write(&preamble, "%s %s\n",
-                                sha1_to_hex(ref->old_sha1), ref->name);
+                                oid_to_hex(&ref->old_oid), ref->name);
        }
        packet_buf_flush(&preamble);
 
@@ -803,19 +803,19 @@ static void parse_fetch(struct strbuf *buf)
                if (skip_prefix(buf->buf, "fetch ", &p)) {
                        const char *name;
                        struct ref *ref;
-                       unsigned char old_sha1[20];
+                       struct object_id old_oid;
 
-                       if (strlen(p) < 40 || get_sha1_hex(p, old_sha1))
+                       if (get_oid_hex(p, &old_oid))
                                die("protocol error: expected sha/ref, got %s'", p);
-                       if (p[40] == ' ')
-                               name = p + 41;
-                       else if (!p[40])
+                       if (p[GIT_SHA1_HEXSZ] == ' ')
+                               name = p + GIT_SHA1_HEXSZ + 1;
+                       else if (!p[GIT_SHA1_HEXSZ])
                                name = "";
                        else
                                die("protocol error: expected sha/ref, got %s'", p);
 
                        ref = alloc_ref(name);
-                       hashcpy(ref->old_sha1, old_sha1);
+                       oidcpy(&ref->old_oid, &old_oid);
 
                        *list = ref;
                        list = &ref->next;
index 10f1ffce0b00a7bef0b156678ab7c04431827f0e..9d34b5a5dafa217e2ce9cb1b7e9a4ab74908f7af 100644 (file)
--- a/remote.c
+++ b/remote.c
@@ -456,7 +456,7 @@ static void alias_all_urls(void)
 static void read_config(void)
 {
        static int loaded;
-       unsigned char sha1[20];
+       struct object_id oid;
        const char *head_ref;
        int flag;
 
@@ -465,7 +465,7 @@ static void read_config(void)
        loaded = 1;
 
        current_branch = NULL;
-       head_ref = resolve_ref_unsafe("HEAD", 0, sha1, &flag);
+       head_ref = resolve_ref_unsafe("HEAD", 0, oid.hash, &flag);
        if (head_ref && (flag & REF_ISSYMREF) &&
            skip_prefix(head_ref, "refs/heads/", &head_ref)) {
                current_branch = make_branch(head_ref, 0);
@@ -544,12 +544,12 @@ static struct refspec *parse_refspec_internal(int nr_refspec, const char **refsp
                flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
 
                if (fetch) {
-                       unsigned char unused[40];
+                       struct object_id unused;
 
                        /* LHS */
                        if (!*rs[i].src)
                                ; /* empty is ok; it means "HEAD" */
-                       else if (llen == 40 && !get_sha1_hex(rs[i].src, unused))
+                       else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
                                rs[i].exact_sha1 = 1; /* ok */
                        else if (!check_refname_format(rs[i].src, flags))
                                ; /* valid looking ref is ok */
@@ -1075,14 +1075,14 @@ static void tail_link_ref(struct ref *ref, struct ref ***tail)
 static struct ref *alloc_delete_ref(void)
 {
        struct ref *ref = alloc_ref("(delete)");
-       hashclr(ref->new_sha1);
+       oidclr(&ref->new_oid);
        return ref;
 }
 
 static int try_explicit_object_name(const char *name,
                                    struct ref **match)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
 
        if (!*name) {
                if (match)
@@ -1090,12 +1090,12 @@ static int try_explicit_object_name(const char *name,
                return 0;
        }
 
-       if (get_sha1(name, sha1))
+       if (get_sha1(name, oid.hash))
                return -1;
 
        if (match) {
                *match = alloc_ref(name);
-               hashcpy((*match)->new_sha1, sha1);
+               oidcpy(&(*match)->new_oid, &oid);
        }
        return 0;
 }
@@ -1110,10 +1110,10 @@ static struct ref *make_linked_ref(const char *name, struct ref ***tail)
 static char *guess_ref(const char *name, struct ref *peer)
 {
        struct strbuf buf = STRBUF_INIT;
-       unsigned char sha1[20];
+       struct object_id oid;
 
        const char *r = resolve_ref_unsafe(peer->name, RESOLVE_REF_READING,
-                                          sha1, NULL);
+                                          oid.hash, NULL);
        if (!r)
                return NULL;
 
@@ -1171,12 +1171,12 @@ static int match_explicit(struct ref *src, struct ref *dst,
                return -1;
 
        if (!dst_value) {
-               unsigned char sha1[20];
+               struct object_id oid;
                int flag;
 
                dst_value = resolve_ref_unsafe(matched_src->name,
                                               RESOLVE_REF_READING,
-                                              sha1, &flag);
+                                              oid.hash, &flag);
                if (!dst_value ||
                    ((flag & REF_ISSYMREF) &&
                     !starts_with(dst_value, "refs/heads/")))
@@ -1190,7 +1190,7 @@ static int match_explicit(struct ref *src, struct ref *dst,
        case 0:
                if (starts_with(dst_value, "refs/"))
                        matched_dst = make_linked_ref(dst_value, dst_tail);
-               else if (is_null_sha1(matched_src->new_sha1))
+               else if (is_null_oid(&matched_src->new_oid))
                        error("unable to delete '%s': remote ref does not exist",
                              dst_value);
                else if ((dst_guess = guess_ref(dst_value, matched_src)))
@@ -1292,13 +1292,13 @@ struct tips {
        int nr, alloc;
 };
 
-static void add_to_tips(struct tips *tips, const unsigned char *sha1)
+static void add_to_tips(struct tips *tips, const struct object_id *oid)
 {
        struct commit *commit;
 
-       if (is_null_sha1(sha1))
+       if (is_null_oid(oid))
                return;
-       commit = lookup_commit_reference_gently(sha1, 1);
+       commit = lookup_commit_reference_gently(oid->hash, 1);
        if (!commit || (commit->object.flags & TMP_MARK))
                return;
        commit->object.flags |= TMP_MARK;
@@ -1321,10 +1321,10 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
        memset(&sent_tips, 0, sizeof(sent_tips));
        for (ref = *dst; ref; ref = ref->next) {
                if (ref->peer_ref &&
-                   !is_null_sha1(ref->peer_ref->new_sha1))
-                       add_to_tips(&sent_tips, ref->peer_ref->new_sha1);
+                   !is_null_oid(&ref->peer_ref->new_oid))
+                       add_to_tips(&sent_tips, &ref->peer_ref->new_oid);
                else
-                       add_to_tips(&sent_tips, ref->old_sha1);
+                       add_to_tips(&sent_tips, &ref->old_oid);
                if (starts_with(ref->name, "refs/tags/"))
                        string_list_append(&dst_tag, ref->name);
        }
@@ -1338,7 +1338,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
                        continue; /* not a tag */
                if (string_list_has_string(&dst_tag, ref->name))
                        continue; /* they already have it */
-               if (sha1_object_info(ref->new_sha1, NULL) != OBJ_TAG)
+               if (sha1_object_info(ref->new_oid.hash, NULL) != OBJ_TAG)
                        continue; /* be conservative */
                item = string_list_append(&src_tag, ref->name);
                item->util = ref;
@@ -1358,9 +1358,9 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
                        struct ref *dst_ref;
                        struct commit *commit;
 
-                       if (is_null_sha1(ref->new_sha1))
+                       if (is_null_oid(&ref->new_oid))
                                continue;
-                       commit = lookup_commit_reference_gently(ref->new_sha1, 1);
+                       commit = lookup_commit_reference_gently(ref->new_oid.hash, 1);
                        if (!commit)
                                /* not pushing a commit, which is not an error */
                                continue;
@@ -1374,7 +1374,7 @@ static void add_missing_tags(struct ref *src, struct ref **dst, struct ref ***ds
 
                        /* Add it in */
                        dst_ref = make_linked_ref(ref->name, dst_tail);
-                       hashcpy(dst_ref->new_sha1, ref->new_sha1);
+                       oidcpy(&dst_ref->new_oid, &ref->new_oid);
                        dst_ref->peer_ref = copy_ref(ref);
                }
        }
@@ -1481,7 +1481,7 @@ int match_push_refs(struct ref *src, struct ref **dst,
 
                        /* Create a new one and link it */
                        dst_peer = make_linked_ref(dst_name, &dst_tail);
-                       hashcpy(dst_peer->new_sha1, ref->new_sha1);
+                       oidcpy(&dst_peer->new_oid, &ref->new_oid);
                        string_list_insert(&dst_ref_index,
                                dst_peer->name)->util = dst_peer;
                }
@@ -1533,13 +1533,13 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                int reject_reason = 0;
 
                if (ref->peer_ref)
-                       hashcpy(ref->new_sha1, ref->peer_ref->new_sha1);
+                       oidcpy(&ref->new_oid, &ref->peer_ref->new_oid);
                else if (!send_mirror)
                        continue;
 
-               ref->deletion = is_null_sha1(ref->new_sha1);
+               ref->deletion = is_null_oid(&ref->new_oid);
                if (!ref->deletion &&
-                       !hashcmp(ref->old_sha1, ref->new_sha1)) {
+                       !oidcmp(&ref->old_oid, &ref->new_oid)) {
                        ref->status = REF_STATUS_UPTODATE;
                        continue;
                }
@@ -1558,7 +1558,7 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 */
                if (ref->expect_old_sha1) {
                        if (ref->expect_old_no_trackback ||
-                           hashcmp(ref->old_sha1, ref->old_sha1_expect))
+                           oidcmp(&ref->old_oid, &ref->old_oid_expect))
                                reject_reason = REF_STATUS_REJECT_STALE;
                }
 
@@ -1582,15 +1582,15 @@ void set_ref_status_for_push(struct ref *remote_refs, int send_mirror,
                 *     passing the --force argument
                 */
 
-               else if (!ref->deletion && !is_null_sha1(ref->old_sha1)) {
+               else if (!ref->deletion && !is_null_oid(&ref->old_oid)) {
                        if (starts_with(ref->name, "refs/tags/"))
                                reject_reason = REF_STATUS_REJECT_ALREADY_EXISTS;
-                       else if (!has_sha1_file(ref->old_sha1))
+                       else if (!has_object_file(&ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_FETCH_FIRST;
-                       else if (!lookup_commit_reference_gently(ref->old_sha1, 1) ||
-                                !lookup_commit_reference_gently(ref->new_sha1, 1))
+                       else if (!lookup_commit_reference_gently(ref->old_oid.hash, 1) ||
+                                !lookup_commit_reference_gently(ref->new_oid.hash, 1))
                                reject_reason = REF_STATUS_REJECT_NEEDS_FORCE;
-                       else if (!ref_newer(ref->new_sha1, ref->old_sha1))
+                       else if (!ref_newer(&ref->new_oid, &ref->old_oid))
                                reject_reason = REF_STATUS_REJECT_NONFASTFORWARD;
                }
 
@@ -1609,7 +1609,7 @@ static void set_merge(struct branch *ret)
 {
        struct remote *remote;
        char *ref;
-       unsigned char sha1[20];
+       struct object_id oid;
        int i;
 
        if (!ret)
@@ -1635,7 +1635,7 @@ static void set_merge(struct branch *ret)
                    strcmp(ret->remote_name, "."))
                        continue;
                if (dwim_ref(ret->merge_name[i], strlen(ret->merge_name[i]),
-                            sha1, &ref) == 1)
+                            oid.hash, &ref) == 1)
                        ret->merge[i]->dst = ref;
                else
                        ret->merge[i]->dst = xstrdup(ret->merge_name[i]);
@@ -1795,10 +1795,10 @@ const char *branch_get_push(struct branch *branch, struct strbuf *err)
 
 static int ignore_symref_update(const char *refname)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        int flag;
 
-       if (!resolve_ref_unsafe(refname, 0, sha1, &flag))
+       if (!resolve_ref_unsafe(refname, 0, oid.hash, &flag))
                return 0; /* non-existing refs are OK */
        return (flag & REF_ISSYMREF);
 }
@@ -1889,7 +1889,7 @@ int get_fetch_map(const struct ref *remote_refs,
 
                if (refspec->exact_sha1) {
                        ref_map = alloc_ref(name);
-                       get_sha1_hex(name, ref_map->old_sha1);
+                       get_oid_hex(name, &ref_map->old_oid);
                } else {
                        ref_map = get_remote_ref(remote_refs, name);
                }
@@ -1930,7 +1930,7 @@ int resolve_remote_symref(struct ref *ref, struct ref *list)
                return 0;
        for (; list; list = list->next)
                if (!strcmp(ref->symref, list->name)) {
-                       hashcpy(ref->old_sha1, list->old_sha1);
+                       oidcpy(&ref->old_oid, &list->old_oid);
                        return 0;
                }
        return 1;
@@ -1944,7 +1944,7 @@ static void unmark_and_free(struct commit_list *list, unsigned int mark)
        }
 }
 
-int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
+int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid)
 {
        struct object *o;
        struct commit *old, *new;
@@ -1955,12 +1955,12 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
         * Both new and old must be commit-ish and new is descendant of
         * old.  Otherwise we require --force.
         */
-       o = deref_tag(parse_object(old_sha1), NULL, 0);
+       o = deref_tag(parse_object(old_oid->hash), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        old = (struct commit *) o;
 
-       o = deref_tag(parse_object(new_sha1), NULL, 0);
+       o = deref_tag(parse_object(new_oid->hash), NULL, 0);
        if (!o || o->type != OBJ_COMMIT)
                return 0;
        new = (struct commit *) o;
@@ -1995,7 +1995,7 @@ int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1)
 int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                       const char **upstream_name)
 {
-       unsigned char sha1[20];
+       struct object_id oid;
        struct commit *ours, *theirs;
        struct rev_info revs;
        const char *base;
@@ -2009,15 +2009,15 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
                return -1;
 
        /* Cannot stat if what we used to build on no longer exists */
-       if (read_ref(base, sha1))
+       if (read_ref(base, oid.hash))
                return -1;
-       theirs = lookup_commit_reference(sha1);
+       theirs = lookup_commit_reference(oid.hash);
        if (!theirs)
                return -1;
 
-       if (read_ref(branch->refname, sha1))
+       if (read_ref(branch->refname, oid.hash))
                return -1;
-       ours = lookup_commit_reference(sha1);
+       ours = lookup_commit_reference(oid.hash);
        if (!ours)
                return -1;
 
@@ -2031,8 +2031,8 @@ int stat_tracking_info(struct branch *branch, int *num_ours, int *num_theirs,
        argv_array_push(&argv, ""); /* ignored */
        argv_array_push(&argv, "--left-right");
        argv_array_pushf(&argv, "%s...%s",
-                        sha1_to_hex(ours->object.sha1),
-                        sha1_to_hex(theirs->object.sha1));
+                        oid_to_hex(&ours->object.oid),
+                        oid_to_hex(&theirs->object.oid));
        argv_array_push(&argv, "--");
 
        init_revisions(&revs, NULL);
@@ -2140,7 +2140,7 @@ static int one_local_ref(const char *refname, const struct object_id *oid,
 
        len = strlen(refname) + 1;
        ref = xcalloc(1, sizeof(*ref) + len);
-       hashcpy(ref->new_sha1, oid->hash);
+       oidcpy(&ref->new_oid, oid);
        memcpy(ref->name, refname, len);
        **local_tail = ref;
        *local_tail = &ref->next;
@@ -2177,7 +2177,7 @@ struct ref *guess_remote_head(const struct ref *head,
        /* If refs/heads/master could be right, it is. */
        if (!all) {
                r = find_ref_by_name(refs, "refs/heads/master");
-               if (r && !hashcmp(r->old_sha1, head->old_sha1))
+               if (r && !oidcmp(&r->old_oid, &head->old_oid))
                        return copy_ref(r);
        }
 
@@ -2185,7 +2185,7 @@ struct ref *guess_remote_head(const struct ref *head,
        for (r = refs; r; r = r->next) {
                if (r != head &&
                    starts_with(r->name, "refs/heads/") &&
-                   !hashcmp(r->old_sha1, head->old_sha1)) {
+                   !oidcmp(&r->old_oid, &head->old_oid)) {
                        *tail = copy_ref(r);
                        tail = &((*tail)->next);
                        if (!all)
@@ -2233,7 +2233,7 @@ static int get_stale_heads_cb(const char *refname, const struct object_id *oid,
 
        if (stale) {
                struct ref *ref = make_linked_ref(refname, &info->stale_refs_tail);
-               hashcpy(ref->new_sha1, oid->hash);
+               oidcpy(&ref->new_oid, oid);
        }
 
 clean_exit:
@@ -2328,14 +2328,14 @@ int is_empty_cas(const struct push_cas_option *cas)
  * If we cannot do so, return negative to signal an error.
  */
 static int remote_tracking(struct remote *remote, const char *refname,
-                          unsigned char sha1[20])
+                          struct object_id *oid)
 {
        char *dst;
 
        dst = apply_refspecs(remote->fetch, remote->fetch_refspec_nr, refname);
        if (!dst)
                return -1; /* no tracking ref for refname at remote */
-       if (read_ref(dst, sha1))
+       if (read_ref(dst, oid->hash))
                return -1; /* we know what the tracking ref is but we cannot read it */
        return 0;
 }
@@ -2353,8 +2353,8 @@ static void apply_cas(struct push_cas_option *cas,
                        continue;
                ref->expect_old_sha1 = 1;
                if (!entry->use_tracking)
-                       hashcpy(ref->old_sha1_expect, cas->entry[i].expect);
-               else if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
+                       hashcpy(ref->old_oid_expect.hash, cas->entry[i].expect);
+               else if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
                        ref->expect_old_no_trackback = 1;
                return;
        }
@@ -2364,7 +2364,7 @@ static void apply_cas(struct push_cas_option *cas,
                return;
 
        ref->expect_old_sha1 = 1;
-       if (remote_tracking(remote, ref->name, ref->old_sha1_expect))
+       if (remote_tracking(remote, ref->name, &ref->old_oid_expect))
                ref->expect_old_no_trackback = 1;
 }
 
index 312b7ca131c459faeae32ff5cf3021c6516eb2af..4a039bae0b15d6d28aace0c62287c3f3bb11befa 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -79,9 +79,9 @@ extern const struct refspec *tag_refspec;
 
 struct ref {
        struct ref *next;
-       unsigned char old_sha1[20];
-       unsigned char new_sha1[20];
-       unsigned char old_sha1_expect[20]; /* used by expect-old */
+       struct object_id old_oid;
+       struct object_id new_oid;
+       struct object_id old_oid_expect; /* used by expect-old */
        char *symref;
        unsigned int
                force:1,
@@ -150,7 +150,7 @@ extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                                     struct sha1_array *shallow);
 
 int resolve_remote_symref(struct ref *ref, struct ref *list);
-int ref_newer(const unsigned char *new_sha1, const unsigned char *old_sha1);
+int ref_newer(const struct object_id *new_oid, const struct object_id *old_oid);
 
 /*
  * Remove and free all but the first of any entries in the input list
index 0fbb6841bf14389148474bff7bd702f5ec02e558..9404a05eeb4adf7e8c1efeed7e16af2c2504aefa 100644 (file)
@@ -86,7 +86,7 @@ void show_object_with_name(FILE *out, struct object *obj,
        leaf.elem = component;
        leaf.elem_len = strlen(component);
 
-       fprintf(out, "%s ", sha1_to_hex(obj->sha1));
+       fprintf(out, "%s ", oid_to_hex(&obj->oid));
        show_path_truncated(out, &leaf);
        fputc('\n', out);
 }
@@ -106,10 +106,10 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
        struct name_entry entry;
        struct object *obj = &tree->object;
 
-       if (!has_sha1_file(obj->sha1))
+       if (!has_object_file(&obj->oid))
                return;
        if (parse_tree(tree) < 0)
-               die("bad tree %s", sha1_to_hex(obj->sha1));
+               die("bad tree %s", oid_to_hex(&obj->oid));
 
        init_tree_desc(&desc, tree->buffer, tree->size);
        while (tree_entry(&desc, &entry)) {
@@ -135,10 +135,12 @@ static void mark_tree_contents_uninteresting(struct tree *tree)
 
 void mark_tree_uninteresting(struct tree *tree)
 {
-       struct object *obj = &tree->object;
+       struct object *obj;
 
        if (!tree)
                return;
+
+       obj = &tree->object;
        if (obj->flags & UNINTERESTING)
                return;
        obj->flags |= UNINTERESTING;
@@ -164,7 +166,7 @@ void mark_parents_uninteresting(struct commit *commit)
                         * it is popped next time around, we won't be trying
                         * to parse it and get an error.
                         */
-                       if (!has_sha1_file(commit->object.sha1))
+                       if (!has_object_file(&commit->object.oid))
                                commit->object.parsed = 1;
 
                        if (commit->object.flags & UNINTERESTING)
@@ -282,11 +284,11 @@ static struct commit *handle_commit(struct rev_info *revs,
                        add_pending_object(revs, object, tag->tag);
                if (!tag->tagged)
                        die("bad tag");
-               object = parse_object(tag->tagged->sha1);
+               object = parse_object(tag->tagged->oid.hash);
                if (!object) {
                        if (flags & UNINTERESTING)
                                return NULL;
-                       die("bad object %s", sha1_to_hex(tag->tagged->sha1));
+                       die("bad object %s", oid_to_hex(&tag->tagged->oid));
                }
                object->flags |= flags;
                /*
@@ -510,7 +512,7 @@ static int rev_compare_tree(struct rev_info *revs,
 
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       if (diff_tree_sha1(t1->object.sha1, t2->object.sha1, "",
+       if (diff_tree_sha1(t1->object.oid.hash, t2->object.oid.hash, "",
                           &revs->pruning) < 0)
                return REV_TREE_DIFFERENT;
        return tree_difference;
@@ -526,7 +528,7 @@ static int rev_same_tree_as_empty(struct rev_info *revs, struct commit *commit)
 
        tree_difference = REV_TREE_SAME;
        DIFF_OPT_CLR(&revs->pruning, HAS_CHANGES);
-       retval = diff_tree_sha1(NULL, t1->object.sha1, "", &revs->pruning);
+       retval = diff_tree_sha1(NULL, t1->object.oid.hash, "", &revs->pruning);
 
        return retval >= 0 && (tree_difference == REV_TREE_SAME);
 }
@@ -610,7 +612,7 @@ static unsigned update_treesame(struct rev_info *revs, struct commit *commit)
 
                st = lookup_decoration(&revs->treesame, &commit->object);
                if (!st)
-                       die("update_treesame %s", sha1_to_hex(commit->object.sha1));
+                       die("update_treesame %s", oid_to_hex(&commit->object.oid));
                relevant_parents = 0;
                relevant_change = irrelevant_change = 0;
                for (p = commit->parents, n = 0; p; n++, p = p->next) {
@@ -708,8 +710,8 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                }
                if (parse_commit(p) < 0)
                        die("cannot simplify commit %s (because of %s)",
-                           sha1_to_hex(commit->object.sha1),
-                           sha1_to_hex(p->object.sha1));
+                           oid_to_hex(&commit->object.oid),
+                           oid_to_hex(&p->object.oid));
                switch (rev_compare_tree(revs, p, commit)) {
                case REV_TREE_SAME:
                        if (!revs->simplify_history || !relevant_commit(p)) {
@@ -741,8 +743,8 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                                 */
                                if (parse_commit(p) < 0)
                                        die("cannot simplify commit %s (invalid %s)",
-                                           sha1_to_hex(commit->object.sha1),
-                                           sha1_to_hex(p->object.sha1));
+                                           oid_to_hex(&commit->object.oid),
+                                           oid_to_hex(&p->object.oid));
                                p->parents = NULL;
                        }
                /* fallthrough */
@@ -754,7 +756,7 @@ static void try_to_simplify_commit(struct rev_info *revs, struct commit *commit)
                                irrelevant_change = 1;
                        continue;
                }
-               die("bad tree compare for commit %s", sha1_to_hex(commit->object.sha1));
+               die("bad tree compare for commit %s", oid_to_hex(&commit->object.oid));
        }
 
        /*
@@ -1189,7 +1191,7 @@ static void add_rev_cmdline_list(struct rev_info *revs,
 {
        while (commit_list) {
                struct object *object = &commit_list->item->object;
-               add_rev_cmdline(revs, object, sha1_to_hex(object->sha1),
+               add_rev_cmdline(revs, object, oid_to_hex(&object->oid),
                                whence, flags);
                commit_list = commit_list->next;
        }
@@ -1378,7 +1380,7 @@ static int add_parents_only(struct rev_info *revs, const char *arg_, int flags)
                        break;
                if (!((struct tag*)it)->tagged)
                        return 0;
-               hashcpy(sha1, ((struct tag*)it)->tagged->sha1);
+               hashcpy(sha1, ((struct tag*)it)->tagged->oid.hash);
        }
        if (it->type != OBJ_COMMIT)
                return 0;
@@ -1435,7 +1437,7 @@ static void add_pending_commit_list(struct rev_info *revs,
        while (commit_list) {
                struct object *object = &commit_list->item->object;
                object->flags |= flags;
-               add_pending_object(revs, object, sha1_to_hex(object->sha1));
+               add_pending_object(revs, object, oid_to_hex(&object->oid));
                commit_list = commit_list->next;
        }
 }
@@ -1555,10 +1557,10 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
 
                                a = (a_obj->type == OBJ_COMMIT
                                     ? (struct commit *)a_obj
-                                    : lookup_commit_reference(a_obj->sha1));
+                                    : lookup_commit_reference(a_obj->oid.hash));
                                b = (b_obj->type == OBJ_COMMIT
                                     ? (struct commit *)b_obj
-                                    : lookup_commit_reference(b_obj->sha1));
+                                    : lookup_commit_reference(b_obj->oid.hash));
                                if (!a || !b)
                                        goto missing;
                                exclude = get_merge_bases(a, b);
@@ -2938,7 +2940,7 @@ static int commit_match(struct commit *commit, struct rev_info *opt)
        if (opt->show_notes) {
                if (!buf.len)
                        strbuf_addstr(&buf, message);
-               format_display_notes(commit->object.sha1, &buf, encoding, 1);
+               format_display_notes(commit->object.oid.hash, &buf, encoding, 1);
        }
 
        /*
@@ -2968,7 +2970,7 @@ enum commit_action get_commit_action(struct rev_info *revs, struct commit *commi
 {
        if (commit->object.flags & SHOWN)
                return commit_ignore;
-       if (revs->unpacked && has_sha1_pack(commit->object.sha1))
+       if (revs->unpacked && has_sha1_pack(commit->object.oid.hash))
                return commit_ignore;
        if (revs->show_all)
                return commit_show;
@@ -3094,7 +3096,7 @@ static void track_linear(struct rev_info *revs, struct commit *commit)
                struct commit_list *p;
                for (p = revs->previous_parents; p; p = p->next)
                        if (p->item == NULL || /* first commit */
-                           !hashcmp(p->item->object.sha1, commit->object.sha1))
+                           !oidcmp(&p->item->object.oid, &commit->object.oid))
                                break;
                revs->linear = p != NULL;
        }
@@ -3132,7 +3134,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                        if (add_parents_to_list(revs, commit, &revs->commits, NULL) < 0) {
                                if (!revs->ignore_missing_links)
                                        die("Failed to traverse parents of commit %s",
-                                               sha1_to_hex(commit->object.sha1));
+                                               oid_to_hex(&commit->object.oid));
                        }
                }
 
@@ -3141,7 +3143,7 @@ static struct commit *get_revision_1(struct rev_info *revs)
                        continue;
                case commit_error:
                        die("Failed to simplify parents of commit %s",
-                           sha1_to_hex(commit->object.sha1));
+                           oid_to_hex(&commit->object.oid));
                default:
                        if (revs->track_linear)
                                track_linear(revs, commit);
index c6a40307381257c070714c778ea9d96f4c676387..047bd18fde3d09921902ebb656ab78bfbcf940a8 100644 (file)
@@ -102,11 +102,11 @@ static int pack_objects(int fd, struct ref *refs, struct sha1_array *extra, stru
                        break;
 
        while (refs) {
-               if (!is_null_sha1(refs->old_sha1) &&
-                   !feed_object(refs->old_sha1, po.in, 1))
+               if (!is_null_oid(&refs->old_oid) &&
+                   !feed_object(refs->old_oid.hash, po.in, 1))
                        break;
-               if (!is_null_sha1(refs->new_sha1) &&
-                   !feed_object(refs->new_sha1, po.in, 0))
+               if (!is_null_oid(&refs->new_oid) &&
+                   !feed_object(refs->new_oid.hash, po.in, 0))
                        break;
                refs = refs->next;
        }
@@ -284,8 +284,8 @@ static int generate_push_cert(struct strbuf *req_buf,
                        continue;
                update_seen = 1;
                strbuf_addf(&cert, "%s %s %s\n",
-                           sha1_to_hex(ref->old_sha1),
-                           sha1_to_hex(ref->new_sha1),
+                           oid_to_hex(&ref->old_oid),
+                           oid_to_hex(&ref->new_oid),
                            ref->name);
        }
        if (!update_seen)
@@ -487,8 +487,8 @@ int send_pack(struct send_pack_args *args,
                if (check_to_send_update(ref, args) < 0)
                        continue;
 
-               old_hex = sha1_to_hex(ref->old_sha1);
-               new_hex = sha1_to_hex(ref->new_sha1);
+               old_hex = oid_to_hex(&ref->old_oid);
+               new_hex = oid_to_hex(&ref->new_oid);
                if (!cmds_sent) {
                        packet_buf_write(&req_buf,
                                         "%s %s %s%c%s",
index a0600aebcadb81e67921d953e8ed626d08529a9b..8c58fa2f4ddaaadb09a63ef5a1937b4282ed988a 100644 (file)
@@ -139,7 +139,7 @@ static int get_message(struct commit *commit, struct commit_message *out)
                git_commit_encoding = "UTF-8";
 
        out->message = logmsg_reencode(commit, NULL, git_commit_encoding);
-       abbrev = find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV);
+       abbrev = find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV);
        abbrev_len = strlen(abbrev);
 
        subject_len = find_commit_subject(out->message, &subject);
@@ -346,7 +346,7 @@ static int is_index_unchanged(void)
                if (cache_tree_update(&the_index, 0))
                        return error(_("Unable to update cache tree\n"));
 
-       return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.sha1);
+       return !hashcmp(active_cache_tree->sha1, head_commit->tree->object.oid.hash);
 }
 
 /*
@@ -397,18 +397,18 @@ static int is_original_commit_empty(struct commit *commit)
 
        if (parse_commit(commit))
                return error(_("Could not parse commit %s\n"),
-                            sha1_to_hex(commit->object.sha1));
+                            oid_to_hex(&commit->object.oid));
        if (commit->parents) {
                struct commit *parent = commit->parents->item;
                if (parse_commit(parent))
                        return error(_("Could not parse parent commit %s\n"),
-                               sha1_to_hex(parent->object.sha1));
-               ptree_sha1 = parent->tree->object.sha1;
+                               oid_to_hex(&parent->object.oid));
+               ptree_sha1 = parent->tree->object.oid.hash;
        } else {
                ptree_sha1 = EMPTY_TREE_SHA1_BIN; /* commit is root */
        }
 
-       return !hashcmp(ptree_sha1, commit->tree->object.sha1);
+       return !hashcmp(ptree_sha1, commit->tree->object.oid.hash);
 }
 
 /*
@@ -486,7 +486,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
 
                if (!opts->mainline)
                        return error(_("Commit %s is a merge but no -m option was given."),
-                               sha1_to_hex(commit->object.sha1));
+                               oid_to_hex(&commit->object.oid));
 
                for (cnt = 1, p = commit->parents;
                     cnt != opts->mainline && p;
@@ -494,28 +494,28 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                        p = p->next;
                if (cnt != opts->mainline || !p)
                        return error(_("Commit %s does not have parent %d"),
-                               sha1_to_hex(commit->object.sha1), opts->mainline);
+                               oid_to_hex(&commit->object.oid), opts->mainline);
                parent = p->item;
        } else if (0 < opts->mainline)
                return error(_("Mainline was specified but commit %s is not a merge."),
-                       sha1_to_hex(commit->object.sha1));
+                       oid_to_hex(&commit->object.oid));
        else
                parent = commit->parents->item;
 
        if (opts->allow_ff &&
-           ((parent && !hashcmp(parent->object.sha1, head)) ||
+           ((parent && !hashcmp(parent->object.oid.hash, head)) ||
             (!parent && unborn)))
-               return fast_forward_to(commit->object.sha1, head, unborn, opts);
+               return fast_forward_to(commit->object.oid.hash, head, unborn, opts);
 
        if (parent && parse_commit(parent) < 0)
                /* TRANSLATORS: The first %s will be "revert" or
                   "cherry-pick", the second %s a SHA1 */
                return error(_("%s: cannot parse parent commit %s"),
-                       action_name(opts), sha1_to_hex(parent->object.sha1));
+                       action_name(opts), oid_to_hex(&parent->object.oid));
 
        if (get_message(commit, &msg) != 0)
                return error(_("Cannot get commit message for %s"),
-                       sha1_to_hex(commit->object.sha1));
+                       oid_to_hex(&commit->object.oid));
 
        /*
         * "commit" is an existing commit.  We would want to apply
@@ -532,11 +532,11 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                strbuf_addstr(&msgbuf, "Revert \"");
                strbuf_addstr(&msgbuf, msg.subject);
                strbuf_addstr(&msgbuf, "\"\n\nThis reverts commit ");
-               strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+               strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
 
                if (commit->parents && commit->parents->next) {
                        strbuf_addstr(&msgbuf, ", reversing\nchanges made to ");
-                       strbuf_addstr(&msgbuf, sha1_to_hex(parent->object.sha1));
+                       strbuf_addstr(&msgbuf, oid_to_hex(&parent->object.oid));
                }
                strbuf_addstr(&msgbuf, ".\n");
        } else {
@@ -562,7 +562,7 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
                        if (!has_conforming_footer(&msgbuf, NULL, 0))
                                strbuf_addch(&msgbuf, '\n');
                        strbuf_addstr(&msgbuf, cherry_picked_prefix);
-                       strbuf_addstr(&msgbuf, sha1_to_hex(commit->object.sha1));
+                       strbuf_addstr(&msgbuf, oid_to_hex(&commit->object.oid));
                        strbuf_addstr(&msgbuf, ")\n");
                }
        }
@@ -592,17 +592,17 @@ static int do_pick_commit(struct commit *commit, struct replay_opts *opts)
         * write it at all.
         */
        if (opts->action == REPLAY_PICK && !opts->no_commit && (res == 0 || res == 1))
-               update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.sha1, NULL,
+               update_ref(NULL, "CHERRY_PICK_HEAD", commit->object.oid.hash, NULL,
                           REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
        if (opts->action == REPLAY_REVERT && ((opts->no_commit && res == 0) || res == 1))
-               update_ref(NULL, "REVERT_HEAD", commit->object.sha1, NULL,
+               update_ref(NULL, "REVERT_HEAD", commit->object.oid.hash, NULL,
                           REF_NODEREF, UPDATE_REFS_DIE_ON_ERR);
 
        if (res) {
                error(opts->action == REPLAY_REVERT
                      ? _("could not revert %s... %s")
                      : _("could not apply %s... %s"),
-                     find_unique_abbrev(commit->object.sha1, DEFAULT_ABBREV),
+                     find_unique_abbrev(commit->object.oid.hash, DEFAULT_ABBREV),
                      msg.subject);
                print_advice(res == 1, opts);
                rerere(opts->allow_rerere_auto);
@@ -664,7 +664,7 @@ static int format_todo(struct strbuf *buf, struct commit_list *todo_list,
 
        for (cur = todo_list; cur; cur = cur->next) {
                const char *commit_buffer = get_commit_buffer(cur->item, NULL);
-               sha1_abbrev = find_unique_abbrev(cur->item->object.sha1, DEFAULT_ABBREV);
+               sha1_abbrev = find_unique_abbrev(cur->item->object.oid.hash, DEFAULT_ABBREV);
                subject_len = find_commit_subject(commit_buffer, &subject);
                strbuf_addf(buf, "%s %s %.*s\n", action_str, sha1_abbrev,
                        subject_len, subject);
index c82e9ee3964a17b23809c28d7cd14961e3e61d87..5a86e297b51261b5f1ae9fa6dad6fa7dc86cd4ba 100644 (file)
@@ -62,7 +62,7 @@ static int add_info_ref(const char *path, const struct object_id *oid,
                o = deref_tag(o, path, 0);
                if (o)
                        if (fprintf(fp, "%s     %s^{}\n",
-                               sha1_to_hex(o->sha1), path) < 0)
+                               oid_to_hex(&o->oid), path) < 0)
                                return -1;
        }
        return 0;
index 3d56746a9be12e78c2b5262948bd411acf3230a2..2c68766ebf0cab5f13f684ac3e26061b93bda44a 100644 (file)
@@ -3216,6 +3216,11 @@ int has_sha1_file_with_flags(const unsigned char *sha1, int flags)
        return find_pack_entry(sha1, &e);
 }
 
+int has_object_file(const struct object_id *oid)
+{
+       return has_sha1_file(oid->hash);
+}
+
 static void check_tree(const void *buf, size_t size)
 {
        struct tree_desc desc;
@@ -3499,12 +3504,12 @@ static int for_each_file_in_obj_subdir(int subdir_nr,
                                break;
                }
        }
-       strbuf_setlen(path, baselen);
+       closedir(dir);
 
+       strbuf_setlen(path, baselen);
        if (!r && subdir_cb)
                r = subdir_cb(subdir_nr, path->buf, data);
 
-       closedir(dir);
        return r;
 }
 
index 3242c5ea462ce246bbc1b90c4aeb2183b2bcbecb..892db21813bc484628e940274e742d103629b0ed 100644 (file)
@@ -616,13 +616,13 @@ static int get_parent(const char *name, int len,
        if (parse_commit(commit))
                return -1;
        if (!idx) {
-               hashcpy(result, commit->object.sha1);
+               hashcpy(result, commit->object.oid.hash);
                return 0;
        }
        p = commit->parents;
        while (p) {
                if (!--idx) {
-                       hashcpy(result, p->item->object.sha1);
+                       hashcpy(result, p->item->object.oid.hash);
                        return 0;
                }
                p = p->next;
@@ -649,7 +649,7 @@ static int get_nth_ancestor(const char *name, int len,
                        return -1;
                commit = commit->parents->item;
        }
-       hashcpy(result, commit->object.sha1);
+       hashcpy(result, commit->object.oid.hash);
        return 0;
 }
 
@@ -659,7 +659,7 @@ struct object *peel_to_type(const char *name, int namelen,
        if (name && !namelen)
                namelen = strlen(name);
        while (1) {
-               if (!o || (!o->parsed && !parse_object(o->sha1)))
+               if (!o || (!o->parsed && !parse_object(o->oid.hash)))
                        return NULL;
                if (expected_type == OBJ_ANY || o->type == expected_type)
                        return o;
@@ -736,9 +736,9 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
                return -1;
        if (!expected_type) {
                o = deref_tag(o, name, sp - name - 2);
-               if (!o || (!o->parsed && !parse_object(o->sha1)))
+               if (!o || (!o->parsed && !parse_object(o->oid.hash)))
                        return -1;
-               hashcpy(sha1, o->sha1);
+               hashcpy(sha1, o->oid.hash);
                return 0;
        }
 
@@ -751,7 +751,7 @@ static int peel_onion(const char *name, int len, unsigned char *sha1)
        if (!o)
                return -1;
 
-       hashcpy(sha1, o->sha1);
+       hashcpy(sha1, o->oid.hash);
        if (sp[0] == '/') {
                /* "$commit^{/foo}" */
                char *prefix;
@@ -899,7 +899,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
                int matches;
 
                commit = pop_most_recent_commit(&list, ONELINE_SEEN);
-               if (!parse_object(commit->object.sha1))
+               if (!parse_object(commit->object.oid.hash))
                        continue;
                buf = get_commit_buffer(commit, NULL);
                p = strstr(buf, "\n\n");
@@ -907,7 +907,7 @@ static int get_sha1_oneline(const char *prefix, unsigned char *sha1,
                unuse_commit_buffer(commit, buf);
 
                if (matches) {
-                       hashcpy(sha1, commit->object.sha1);
+                       hashcpy(sha1, commit->object.oid.hash);
                        found = 1;
                        break;
                }
@@ -1022,7 +1022,7 @@ int get_sha1_mb(const char *name, unsigned char *sha1)
                st = -1;
        else {
                st = 0;
-               hashcpy(sha1, mbs->item->object.sha1);
+               hashcpy(sha1, mbs->item->object.oid.hash);
        }
        free_commit_list(mbs);
        return st;
index 46be7897794ab8e350d64f00d3c11390fe993ad8..60f1505d9791bc92a283d02183da35e7542ad2dc 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -105,7 +105,7 @@ struct commit_list *get_shallow_commits(struct object_array *heads, int depth,
                cur_depth++;
                if ((depth != INFINITE_DEPTH && cur_depth >= depth) ||
                    (is_repository_shallow() && !commit->parents &&
-                    (graft = lookup_commit_graft(commit->object.sha1)) != NULL &&
+                    (graft = lookup_commit_graft(commit->object.oid.hash)) != NULL &&
                     graft->nr_parent < 0)) {
                        commit_list_insert(commit, &result);
                        commit->object.flags |= shallow_flag;
@@ -167,7 +167,7 @@ static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
                if (!c || !(c->object.flags & SEEN)) {
                        if (data->flags & VERBOSE)
                                printf("Removing %s from .git/shallow\n",
-                                      sha1_to_hex(c->object.sha1));
+                                      oid_to_hex(&c->object.oid));
                        return 0;
                }
        }
@@ -426,7 +426,7 @@ static void paint_down(struct paint_info *info, const unsigned char *sha1,
 
                if (parse_commit(c))
                        die("unable to parse commit %s",
-                           sha1_to_hex(c->object.sha1));
+                           oid_to_hex(&c->object.oid));
 
                for (p = c->parents; p; p = p->next) {
                        uint32_t **p_refs = ref_bitmap_at(&info->ref_bitmap,
index 88af54c633851d440f46e21d65b1362b6360d2bb..14e76247bf107da916cf100b8078cf10238e6b76 100644 (file)
@@ -249,7 +249,7 @@ static int prepare_submodule_summary(struct rev_info *rev, const char *path,
        for (list = merge_bases; list; list = list->next) {
                list->item->object.flags |= UNINTERESTING;
                add_pending_object(rev, &list->item->object,
-                       sha1_to_hex(list->item->object.sha1));
+                       oid_to_hex(&list->item->object.oid));
        }
        return prepare_revision_walk(rev);
 }
@@ -597,7 +597,7 @@ static void calculate_changed_submodule_paths(void)
                        diff_opts.output_format |= DIFF_FORMAT_CALLBACK;
                        diff_opts.format_callback = submodule_collect_changed_cb;
                        diff_setup_done(&diff_opts);
-                       diff_tree_sha1(parent->item->object.sha1, commit->object.sha1, "", &diff_opts);
+                       diff_tree_sha1(parent->item->object.oid.hash, commit->object.oid.hash, "", &diff_opts);
                        diffcore_std(&diff_opts);
                        diff_flush(&diff_opts);
                        parent = parent->next;
@@ -875,7 +875,7 @@ static int find_first_merges(struct object_array *result, const char *path,
 
        /* get all revisions that merge commit a */
        snprintf(merged_revision, sizeof(merged_revision), "^%s",
-                       sha1_to_hex(a->object.sha1));
+                       oid_to_hex(&a->object.oid));
        init_revisions(&revs, NULL);
        rev_opts.submodule = path;
        setup_revisions(ARRAY_SIZE(rev_args)-1, rev_args, &revs, &rev_opts);
@@ -1006,7 +1006,7 @@ int merge_submodule(unsigned char result[20], const char *path,
                        "by using:\n\n"
                        "  git update-index --cacheinfo 160000 %s \"%s\"\n\n"
                        "which will accept this suggestion.\n",
-                       sha1_to_hex(merges.objects[0].item->sha1), path);
+                       oid_to_hex(&merges.objects[0].item->oid), path);
                break;
 
        default:
index b1673b3e8f38d9961b8b2783e1f5c47a865638fb..093832fef1541906b735d162a76c8ffa773d236c 100644 (file)
@@ -68,6 +68,13 @@ test_expect_success 'blame 1 author' '
        check_count A 2
 '
 
+test_expect_success 'blame by tag objects' '
+       git tag -m "test tag" testTag &&
+       git tag -m "test tag #2" testTag2 testTag &&
+       check_count -h testTag A 2 &&
+       check_count -h testTag2 A 2
+'
+
 test_expect_success 'setup B lines' '
        echo "2A quick brown fox jumps over the" >>file &&
        echo "lazy dog" >>file &&
index 75482254a3e72a65640c6805abd028e2b7bd0e2f..f9ae1d780dceb526fcfbce2684e3e7dc9b06f5eb 100644 (file)
@@ -6,6 +6,14 @@
 # a subdirectory called "$git"
 TEST_NO_CREATE_REPO=NoThanks
 
+# Some operations require multiple attempts to be successful. Define
+# here the maximal retry timeout in seconds.
+RETRY_TIMEOUT=60
+
+# Sometimes p4d seems to hang. Terminate the p4d process automatically after
+# the defined timeout in seconds.
+P4D_TIMEOUT=300
+
 . ./test-lib.sh
 
 if ! test_have_prereq PYTHON
@@ -36,6 +44,15 @@ native_path() {
        echo "$path"
 }
 
+# On Solaris the 'date +%s' function is not supported and therefore we
+# need this replacement.
+# Attention: This function is not safe again against time offset updates
+# at runtime (e.g. via NTP). The 'clock_gettime(CLOCK_MONOTONIC)'
+# function could fix that but it is not in Python until 3.3.
+time_in_seconds() {
+       python -c 'import time; print int(time.time())'
+}
+
 # Try to pick a unique port: guess a large number, then hope
 # no more than one of each test is running.
 #
@@ -57,6 +74,15 @@ cli="$TRASH_DIRECTORY/cli"
 git="$TRASH_DIRECTORY/git"
 pidfile="$TRASH_DIRECTORY/p4d.pid"
 
+# Sometimes "prove" seems to hang on exit because p4d is still running
+cleanup() {
+       if test -f "$pidfile"
+       then
+               kill -9 $(cat "$pidfile") 2>/dev/null && exit 255
+       fi
+}
+trap cleanup EXIT
+
 # git p4 submit generates a temp file, which will
 # not get cleaned up if the submission fails.  Don't
 # clutter up /tmp on the test machine.
@@ -81,6 +107,19 @@ start_p4d() {
        # will be caught with the "kill -0" check below.
        i=${P4D_START_PATIENCE:-300}
        pid=$(cat "$pidfile")
+
+       timeout=$(($(time_in_seconds) + $P4D_TIMEOUT))
+       while true
+       do
+               if test $(time_in_seconds) -gt $timeout
+               then
+                       kill -9 $pid
+                       exit 1
+               fi
+               sleep 1
+       done &
+       watchdog_pid=$!
+
        ready=
        while test $i -gt 0
        do
@@ -121,22 +160,36 @@ p4_add_user() {
        EOF
 }
 
+retry_until_success() {
+       timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
+       until "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+       do
+               sleep 1
+       done
+}
+
+retry_until_fail() {
+       timeout=$(($(time_in_seconds) + $RETRY_TIMEOUT))
+       until ! "$@" 2>/dev/null || test $(time_in_seconds) -gt $timeout
+       do
+               sleep 1
+       done
+}
+
 kill_p4d() {
        pid=$(cat "$pidfile")
-       # it had better exist for the first kill
-       kill $pid &&
-       for i in 1 2 3 4 5 ; do
-               kill $pid >/dev/null 2>&1 || break
-               sleep 1
-       done &&
+       retry_until_fail kill $pid
+       retry_until_fail kill -9 $pid
        # complain if it would not die
        test_must_fail kill $pid >/dev/null 2>&1 &&
-       rm -rf "$db" "$cli" "$pidfile"
+       rm -rf "$db" "$cli" "$pidfile" &&
+       retry_until_fail kill -9 $watchdog_pid
 }
 
 cleanup_git() {
-       rm -rf "$git" &&
-       mkdir "$git"
+       retry_until_success rm -r "$git"
+       test_must_fail test -d "$git" &&
+       retry_until_success mkdir "$git"
 }
 
 marshal_dump() {
diff --git a/t/perf/p7000-filter-branch.sh b/t/perf/p7000-filter-branch.sh
new file mode 100755 (executable)
index 0000000..15ee5d1
--- /dev/null
@@ -0,0 +1,19 @@
+#!/bin/sh
+
+test_description='performance of filter-branch'
+. ./perf-lib.sh
+
+test_perf_default_repo
+test_checkout_worktree
+
+test_expect_success 'mark bases for tests' '
+       git tag -f tip &&
+       git tag -f base HEAD~100
+'
+
+test_perf 'noop filter' '
+       git checkout --detach tip &&
+       git filter-branch -f base..HEAD
+'
+
+test_done
index dc09797021a6a5fc8eed6da4412164673ab484a1..e66b7cb697bc8a4ef7268e5d8d29f0b69a8ac213 100755 (executable)
@@ -176,6 +176,18 @@ test_expect_success 'integer overflow in timestamps is reported' '
        grep "error in commit $new.*integer overflow" out
 '
 
+test_expect_success 'commit with NUL in header' '
+       git cat-file commit HEAD >basis &&
+       sed "s/author ./author Q/" <basis | q_to_nul >commit-NUL-header &&
+       new=$(git hash-object -t commit -w --stdin <commit-NUL-header) &&
+       test_when_finished "remove_object $new" &&
+       git update-ref refs/heads/bogus "$new" &&
+       test_when_finished "git update-ref -d refs/heads/bogus" &&
+       test_must_fail git fsck 2>out &&
+       cat out &&
+       grep "error in commit $new.*unterminated header: NUL at offset" out
+'
+
 test_expect_success 'malformatted tree object' '
        test_when_finished "git update-ref -d refs/tags/wrong" &&
        test_when_finished "remove_object \$T" &&
@@ -276,6 +288,26 @@ test_expect_success 'tag with bad tagger' '
        grep "error in tag .*: invalid author/committer" out
 '
 
+test_expect_success 'tag with NUL in header' '
+       sha=$(git rev-parse HEAD) &&
+       q_to_nul >tag-NUL-header <<-EOF &&
+       object $sha
+       type commit
+       tag contains-Q-in-header
+       tagger T A Gger <tagger@example.com> 1234567890 -0000
+
+       This is an invalid tag.
+       EOF
+
+       tag=$(git hash-object --literally -t tag -w --stdin <tag-NUL-header) &&
+       test_when_finished "remove_object $tag" &&
+       echo $tag >.git/refs/tags/wrong &&
+       test_when_finished "git update-ref -d refs/tags/wrong" &&
+       test_must_fail git fsck --tags 2>out &&
+       cat out &&
+       grep "error in tag $tag.*unterminated header: NUL at offset" out
+'
+
 test_expect_success 'cleaned up' '
        git fsck >actual 2>&1 &&
        test_cmp empty actual
index 98eb49ac236e9e9d08d5da5167f7e257db173c41..9067e0206f12d472d6940842635759b4447010f1 100755 (executable)
@@ -1234,7 +1234,7 @@ test_expect_success 'tabs and spaces are accepted in the todolist' '
                # Turn single spaces into space/tab mix
                sed "1s/ /      /g; 2s/ /  /g; 3s/ /    /g" "$1"
                printf "\n\t# comment\n #more\n\t # comment\n"
-       ) >$1.new
+       ) >"$1.new"
        mv "$1.new" "$1"
        EOF
        test_set_editor "$(pwd)/add-indent.sh" &&
index 44f3d5fb284e9848180df9ed9cfbc44a91762277..89224edcc582e6610fdefd63a8d40048bfe10da0 100755 (executable)
@@ -100,7 +100,7 @@ test_expect_success 'push with receive.fsckobjects' '
                git config receive.fsckobjects true &&
                git config transfer.fsckobjects false
        ) &&
-       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
+       test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act &&
        test_cmp exp act
 '
 
@@ -111,8 +111,7 @@ test_expect_success 'push with transfer.fsckobjects' '
                cd dst &&
                git config transfer.fsckobjects true
        ) &&
-       test_must_fail git push --porcelain dst master:refs/heads/test >act &&
-       test_cmp exp act
+       test_must_fail ok=sigpipe git push --porcelain dst master:refs/heads/test >act
 '
 
 cat >bogus-commit <<\EOF
index ec22c984450bda4110fe165917aece2ecbe4ceb2..0a87e195eac3a70ecca9e9a5750655774a686b1d 100755 (executable)
@@ -1162,15 +1162,15 @@ do
                mk_empty shallow &&
                (
                        cd shallow &&
-                       test_must_fail git fetch ../testrepo/.git $SHA1_3 &&
-                       test_must_fail git fetch ../testrepo/.git $SHA1_1 &&
+                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3 &&
+                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_1 &&
                        git --git-dir=../testrepo/.git config uploadpack.allowreachablesha1inwant true &&
                        git fetch ../testrepo/.git $SHA1_1 &&
                        git cat-file commit $SHA1_1 &&
                        test_must_fail git cat-file commit $SHA1_2 &&
                        git fetch ../testrepo/.git $SHA1_2 &&
                        git cat-file commit $SHA1_2 &&
-                       test_must_fail git fetch ../testrepo/.git $SHA1_3
+                       test_must_fail ok=sigpipe git fetch ../testrepo/.git $SHA1_3
                )
        '
 done
index 6f9916a3901caa71b6a1994b6091b629a4397473..ba975bb3557b5e243deb2c40217dbe04ff4953ae 100755 (executable)
@@ -109,23 +109,20 @@ test_expect_success 'push to URL' '
        diff expected actual
 '
 
-# Test that filling pipe buffers doesn't cause failure
-# Too slow to leave enabled for general use
-if false
-then
-       printf 'parent1\nrepo1\n' >expected
-       nr=1000
-       while test $nr -lt 2000
-       do
-               nr=$(( $nr + 1 ))
-               git branch b/$nr $COMMIT3
-               echo "refs/heads/b/$nr $COMMIT3 refs/heads/b/$nr $_z40" >>expected
-       done
-
-       test_expect_success 'push many refs' '
-               git push parent1 "refs/heads/b/*:refs/heads/b/*" &&
-               diff expected actual
-       '
-fi
+test_expect_success 'set up many-ref tests' '
+       {
+               nr=1000
+               while test $nr -lt 2000
+               do
+                       nr=$(( $nr + 1 ))
+                       echo "create refs/heads/b/$nr $COMMIT3"
+               done
+       } | git update-ref --stdin
+'
+
+test_expect_success 'sigpipe does not cause pre-push hook failure' '
+       echo "exit 0" | write_script "$HOOK" &&
+       git push parent1 "refs/heads/b/*:refs/heads/b/*"
+'
 
 test_done
index ad877d774aad308bc81ecfae6eead262719694b9..a954ead8af882002d3e8bbb6dad8c52190f794c7 100755 (executable)
@@ -14,7 +14,7 @@ test_expect_success 'setup repository to clone' '
 '
 
 test_proto "host:path" ssh "remote:repo.git"
-test_proto "ssh://" ssh "ssh://remote/$PWD/remote/repo.git"
-test_proto "git+ssh://" ssh "git+ssh://remote/$PWD/remote/repo.git"
+test_proto "ssh://" ssh "ssh://remote$PWD/remote/repo.git"
+test_proto "git+ssh://" ssh "git+ssh://remote$PWD/remote/repo.git"
 
 test_done
index 377c648e04f55359eefa87cdf2ad0c06be0df171..869e0bf0735033f66c5cba44d107f799fc07b112 100755 (executable)
@@ -418,4 +418,11 @@ test_expect_success 'filter commit message without trailing newline' '
        test_cmp expect actual
 '
 
+test_expect_success 'tree-filter deals with object name vs pathname ambiguity' '
+       test_when_finished "git reset --hard original" &&
+       ambiguous=$(git rev-list -1 HEAD) &&
+       git filter-branch --tree-filter "mv file.t $ambiguous" HEAD^.. &&
+       git show HEAD:$ambiguous
+'
+
 test_done
index 9984c48b5a75a2f6442616602e79f4689e7066fb..14a938402e099744d6c334b7b5bb2c9ee835be3c 100755 (executable)
@@ -47,1077 +47,1075 @@ file5_data='an inline file.
 file6_data='#!/bin/sh
 echo "$@"'
 
->empty
-
 ###
 ### series A
 ###
 
-test_tick
-
 test_expect_success 'empty stream succeeds' '
        git fast-import </dev/null
 '
 
-cat >input <<INPUT_END
-blob
-mark :2
-data <<EOF
-$file2_data
-EOF
-
-blob
-mark :3
-data <<END
-$file3_data
-END
-
-blob
-mark :4
-data $file4_len
-$file4_data
-commit refs/heads/master
-mark :5
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-initial
-COMMIT
-
-M 644 :2 file2
-M 644 :3 file3
-M 755 :4 file4
-
-tag series-A
-from :5
-data <<EOF
-An annotated tag without a tagger
-EOF
-
-tag series-A-blob
-from :3
-data <<EOF
-An annotated tag that annotates a blob.
-EOF
-
-INPUT_END
-test_expect_success \
-    'A: create pack from stdin' \
-    'git fast-import --export-marks=marks.out <input &&
-        git whatchanged master'
+test_expect_success 'A: create pack from stdin' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       blob
+       mark :2
+       data <<EOF
+       $file2_data
+       EOF
+
+       blob
+       mark :3
+       data <<END
+       $file3_data
+       END
+
+       blob
+       mark :4
+       data $file4_len
+       $file4_data
+       commit refs/heads/master
+       mark :5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       initial
+       COMMIT
+
+       M 644 :2 file2
+       M 644 :3 file3
+       M 755 :4 file4
+
+       tag series-A
+       from :5
+       data <<EOF
+       An annotated tag without a tagger
+       EOF
+
+       tag series-A-blob
+       from :3
+       data <<EOF
+       An annotated tag that annotates a blob.
+       EOF
+
+       INPUT_END
+       git fast-import --export-marks=marks.out <input &&
+       git whatchanged master
+'
 
 test_expect_success 'A: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-initial
-EOF
-test_expect_success \
-       'A: verify commit' \
-       'git cat-file commit master | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect <<EOF
-100644 blob file2
-100644 blob file3
-100755 blob file4
-EOF
-test_expect_success \
-       'A: verify tree' \
-       'git cat-file -p master^{tree} | sed "s/ [0-9a-f]*      / /" >actual &&
-        test_cmp expect actual'
-
-echo "$file2_data" >expect
-test_expect_success \
-       'A: verify file2' \
-       'git cat-file blob master:file2 >actual && test_cmp expect actual'
-
-echo "$file3_data" >expect
-test_expect_success \
-       'A: verify file3' \
-       'git cat-file blob master:file3 >actual && test_cmp expect actual'
-
-printf "$file4_data" >expect
-test_expect_success \
-       'A: verify file4' \
-       'git cat-file blob master:file4 >actual && test_cmp expect actual'
-
-cat >expect <<EOF
-object $(git rev-parse refs/heads/master)
-type commit
-tag series-A
-
-An annotated tag without a tagger
-EOF
+test_expect_success 'A: verify commit' '
+       cat >expect <<-EOF &&
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+       initial
+       EOF
+       git cat-file commit master | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'A: verify tree' '
+       cat >expect <<-EOF &&
+       100644 blob file2
+       100644 blob file3
+       100755 blob file4
+       EOF
+       git cat-file -p master^{tree} | sed "s/ [0-9a-f]*       / /" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'A: verify file2' '
+       echo "$file2_data" >expect &&
+       git cat-file blob master:file2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'A: verify file3' '
+       echo "$file3_data" >expect &&
+       git cat-file blob master:file3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'A: verify file4' '
+       printf "$file4_data" >expect &&
+       git cat-file blob master:file4 >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'A: verify tag/series-A' '
+       cat >expect <<-EOF &&
+       object $(git rev-parse refs/heads/master)
+       type commit
+       tag series-A
+
+       An annotated tag without a tagger
+       EOF
        git cat-file tag tags/series-A >actual &&
        test_cmp expect actual
 '
 
-cat >expect <<EOF
-object $(git rev-parse refs/heads/master:file3)
-type blob
-tag series-A-blob
-
-An annotated tag that annotates a blob.
-EOF
 test_expect_success 'A: verify tag/series-A-blob' '
+       cat >expect <<-EOF &&
+       object $(git rev-parse refs/heads/master:file3)
+       type blob
+       tag series-A-blob
+
+       An annotated tag that annotates a blob.
+       EOF
        git cat-file tag tags/series-A-blob >actual &&
        test_cmp expect actual
 '
 
-cat >expect <<EOF
-:2 `git rev-parse --verify master:file2`
-:3 `git rev-parse --verify master:file3`
-:4 `git rev-parse --verify master:file4`
-:5 `git rev-parse --verify master^0`
-EOF
-test_expect_success \
-       'A: verify marks output' \
-       'test_cmp expect marks.out'
+test_expect_success 'A: verify marks output' '
+       cat >expect <<-EOF &&
+       :2 `git rev-parse --verify master:file2`
+       :3 `git rev-parse --verify master:file3`
+       :4 `git rev-parse --verify master:file4`
+       :5 `git rev-parse --verify master^0`
+       EOF
+       test_cmp expect marks.out
+'
 
-test_expect_success \
-       'A: verify marks import' \
-       'git fast-import \
+test_expect_success 'A: verify marks import' '
+       git fast-import \
                --import-marks=marks.out \
                --export-marks=marks.new \
                </dev/null &&
-       test_cmp expect marks.new'
-
-test_tick
-new_blob=$(echo testing | git hash-object --stdin)
-cat >input <<INPUT_END
-tag series-A-blob-2
-from $(git rev-parse refs/heads/master:file3)
-data <<EOF
-Tag blob by sha1.
-EOF
-
-blob
-mark :6
-data <<EOF
-testing
-EOF
-
-commit refs/heads/new_blob
-committer  <> 0 +0000
-data 0
-M 644 :6 new_blob
-#pretend we got sha1 from fast-import
-ls "new_blob"
-
-tag series-A-blob-3
-from $new_blob
-data <<EOF
-Tag new_blob.
-EOF
-INPUT_END
-
-cat >expect <<EOF
-object $(git rev-parse refs/heads/master:file3)
-type blob
-tag series-A-blob-2
-
-Tag blob by sha1.
-object $new_blob
-type blob
-tag series-A-blob-3
-
-Tag new_blob.
-EOF
-
-test_expect_success \
-       'A: tag blob by sha1' \
-       'git fast-import <input &&
+       test_cmp expect marks.new
+'
+
+test_expect_success 'A: tag blob by sha1' '
+       test_tick &&
+       new_blob=$(echo testing | git hash-object --stdin) &&
+       cat >input <<-INPUT_END &&
+       tag series-A-blob-2
+       from $(git rev-parse refs/heads/master:file3)
+       data <<EOF
+       Tag blob by sha1.
+       EOF
+
+       blob
+       mark :6
+       data <<EOF
+       testing
+       EOF
+
+       commit refs/heads/new_blob
+       committer  <> 0 +0000
+       data 0
+       M 644 :6 new_blob
+       #pretend we got sha1 from fast-import
+       ls "new_blob"
+
+       tag series-A-blob-3
+       from $new_blob
+       data <<EOF
+       Tag new_blob.
+       EOF
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       object $(git rev-parse refs/heads/master:file3)
+       type blob
+       tag series-A-blob-2
+
+       Tag blob by sha1.
+       object $new_blob
+       type blob
+       tag series-A-blob-3
+
+       Tag new_blob.
+       EOF
+
+       git fast-import <input &&
        git cat-file tag tags/series-A-blob-2 >actual &&
        git cat-file tag tags/series-A-blob-3 >>actual &&
-       test_cmp expect actual'
+       test_cmp expect actual
+'
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/verify--import-marks
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-recreate from :5
-COMMIT
+test_expect_success 'A: verify marks import does not crash' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/verify--import-marks
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       recreate from :5
+       COMMIT
 
-from :5
-M 755 :2 copy-of-file2
+       from :5
+       M 755 :2 copy-of-file2
 
-INPUT_END
-test_expect_success \
-       'A: verify marks import does not crash' \
-       'git fast-import --import-marks=marks.out <input &&
-        git whatchanged verify--import-marks'
+       INPUT_END
+
+       git fast-import --import-marks=marks.out <input &&
+       git whatchanged verify--import-marks
+'
 
 test_expect_success 'A: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-:000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A     copy-of-file2
-EOF
-git diff-tree -M -r master verify--import-marks >actual
-test_expect_success \
-       'A: verify diff' \
-       'compare_diff_raw expect actual &&
-        test `git rev-parse --verify master:file2` \
-           = `git rev-parse --verify verify--import-marks:copy-of-file2`'
-
-test_tick
-mt=$(git hash-object --stdin < /dev/null)
-: >input.blob
-: >marks.exp
-: >tree.exp
-
-cat >input.commit <<EOF
-commit refs/heads/verify--dump-marks
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-test the sparse array dumping routines with exponentially growing marks
-COMMIT
-EOF
-
-i=0
-l=4
-m=6
-n=7
-while test "$i" -lt 27; do
-    cat >>input.blob <<EOF
-blob
-mark :$l
-data 0
-blob
-mark :$m
-data 0
-blob
-mark :$n
-data 0
-EOF
-    echo "M 100644 :$l l$i" >>input.commit
-    echo "M 100644 :$m m$i" >>input.commit
-    echo "M 100644 :$n n$i" >>input.commit
-
-    echo ":$l $mt" >>marks.exp
-    echo ":$m $mt" >>marks.exp
-    echo ":$n $mt" >>marks.exp
-
-    printf "100644 blob $mt\tl$i\n" >>tree.exp
-    printf "100644 blob $mt\tm$i\n" >>tree.exp
-    printf "100644 blob $mt\tn$i\n" >>tree.exp
-
-    l=$(($l + $l))
-    m=$(($m + $m))
-    n=$(($l + $n))
-
-    i=$((1 + $i))
-done
-
-sort tree.exp > tree.exp_s
+test_expect_success 'A: verify diff' '
+       cat >expect <<-EOF &&
+       :000000 100755 0000000000000000000000000000000000000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 A      copy-of-file2
+       EOF
+       git diff-tree -M -r master verify--import-marks >actual &&
+       compare_diff_raw expect actual &&
+       test `git rev-parse --verify master:file2` \
+           = `git rev-parse --verify verify--import-marks:copy-of-file2`
+'
 
 test_expect_success 'A: export marks with large values' '
+       test_tick &&
+       mt=$(git hash-object --stdin < /dev/null) &&
+       >input.blob &&
+       >marks.exp &&
+       >tree.exp &&
+
+       cat >input.commit <<-EOF &&
+       commit refs/heads/verify--dump-marks
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       test the sparse array dumping routines with exponentially growing marks
+       COMMIT
+       EOF
+
+       i=0 l=4 m=6 n=7 &&
+       while test "$i" -lt 27
+       do
+               cat >>input.blob <<-EOF &&
+               blob
+               mark :$l
+               data 0
+               blob
+               mark :$m
+               data 0
+               blob
+               mark :$n
+               data 0
+               EOF
+               echo "M 100644 :$l l$i" >>input.commit &&
+               echo "M 100644 :$m m$i" >>input.commit &&
+               echo "M 100644 :$n n$i" >>input.commit &&
+
+               echo ":$l $mt" >>marks.exp &&
+               echo ":$m $mt" >>marks.exp &&
+               echo ":$n $mt" >>marks.exp &&
+
+               printf "100644 blob $mt\tl$i\n" >>tree.exp &&
+               printf "100644 blob $mt\tm$i\n" >>tree.exp &&
+               printf "100644 blob $mt\tn$i\n" >>tree.exp &&
+
+               l=$(($l + $l)) &&
+               m=$(($m + $m)) &&
+               n=$(($l + $n)) &&
+
+               i=$((1 + $i)) || return 1
+       done &&
+
+       sort tree.exp > tree.exp_s &&
+
        cat input.blob input.commit | git fast-import --export-marks=marks.large &&
        git ls-tree refs/heads/verify--dump-marks >tree.out &&
        test_cmp tree.exp_s tree.out &&
-       test_cmp marks.exp marks.large'
+       test_cmp marks.exp marks.large
+'
 
 ###
 ### series B
 ###
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/branch
-mark :1
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
+test_expect_success 'B: fail on invalid blob sha1' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       mark :1
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       corrupt
+       COMMIT
+
+       from refs/heads/master
+       M 755 0000000000000000000000000000000000000001 zero1
 
-from refs/heads/master
-M 755 0000000000000000000000000000000000000001 zero1
+       INPUT_END
+
+       test_when_finished "rm -f .git/objects/pack_* .git/objects/index_*" &&
+       test_must_fail git fast-import <input
+'
+
+test_expect_success 'B: accept branch name "TEMP_TAG"' '
+       cat >input <<-INPUT_END &&
+       commit TEMP_TAG
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       tag base
+       COMMIT
+
+       from refs/heads/master
+
+       INPUT_END
+
+       test_when_finished "rm -f .git/TEMP_TAG
+               git gc
+               git prune" &&
+       git fast-import <input &&
+       test -f .git/TEMP_TAG &&
+       test `git rev-parse master` = `git rev-parse TEMP_TAG^`
+'
 
-INPUT_END
-test_expect_success 'B: fail on invalid blob sha1' '
-    test_must_fail git fast-import <input
-'
-rm -f .git/objects/pack_* .git/objects/index_*
-
-cat >input <<INPUT_END
-commit TEMP_TAG
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-tag base
-COMMIT
-
-from refs/heads/master
-
-INPUT_END
-test_expect_success \
-    'B: accept branch name "TEMP_TAG"' \
-    'git fast-import <input &&
-        test -f .git/TEMP_TAG &&
-        test `git rev-parse master` = `git rev-parse TEMP_TAG^`'
-rm -f .git/TEMP_TAG
-
-git gc 2>/dev/null >/dev/null
-git prune 2>/dev/null >/dev/null
-
-cat >input <<INPUT_END
-commit refs/heads/empty-committer-1
-committer  <> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: accept empty committer' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/empty-committer-1
+       committer  <> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/empty-committer-1
+               git gc
+               git prune" &&
        git fast-import <input &&
        out=$(git fsck) &&
        echo "$out" &&
        test -z "$out"
 '
-git update-ref -d refs/heads/empty-committer-1 || true
 
-git gc 2>/dev/null >/dev/null
-git prune 2>/dev/null >/dev/null
-
-cat >input <<INPUT_END
-commit refs/heads/empty-committer-2
-committer <a@b.com> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: accept and fixup committer with no name' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/empty-committer-2
+       committer <a@b.com> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/empty-committer-2
+               git gc
+               git prune" &&
        git fast-import <input &&
        out=$(git fsck) &&
        echo "$out" &&
        test -z "$out"
 '
-git update-ref -d refs/heads/empty-committer-2 || true
 
-git gc 2>/dev/null >/dev/null
-git prune 2>/dev/null >/dev/null
-
-cat >input <<INPUT_END
-commit refs/heads/invalid-committer
-committer Name email> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: fail on invalid committer (1)' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/invalid-committer
+       committer Name email> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
        test_must_fail git fast-import <input
 '
-git update-ref -d refs/heads/invalid-committer || true
 
-cat >input <<INPUT_END
-commit refs/heads/invalid-committer
-committer Name <e<mail> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: fail on invalid committer (2)' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/invalid-committer
+       committer Name <e<mail> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
        test_must_fail git fast-import <input
 '
-git update-ref -d refs/heads/invalid-committer || true
 
-cat >input <<INPUT_END
-commit refs/heads/invalid-committer
-committer Name <email>> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: fail on invalid committer (3)' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/invalid-committer
+       committer Name <email>> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
        test_must_fail git fast-import <input
 '
-git update-ref -d refs/heads/invalid-committer || true
 
-cat >input <<INPUT_END
-commit refs/heads/invalid-committer
-committer Name <email $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: fail on invalid committer (4)' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/invalid-committer
+       committer Name <email $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
        test_must_fail git fast-import <input
 '
-git update-ref -d refs/heads/invalid-committer || true
 
-cat >input <<INPUT_END
-commit refs/heads/invalid-committer
-committer Name<email> $GIT_COMMITTER_DATE
-data <<COMMIT
-empty commit
-COMMIT
-INPUT_END
 test_expect_success 'B: fail on invalid committer (5)' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/invalid-committer
+       committer Name<email> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       empty commit
+       COMMIT
+       INPUT_END
+
+       test_when_finished "git update-ref -d refs/heads/invalid-committer" &&
        test_must_fail git fast-import <input
 '
-git update-ref -d refs/heads/invalid-committer || true
 
 ###
 ### series C
 ###
 
-newf=`echo hi newf | git hash-object -w --stdin`
-oldf=`git rev-parse --verify master:file2`
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/branch
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-second
-COMMIT
-
-from refs/heads/master
-M 644 $oldf file2/oldf
-M 755 $newf file2/newf
-D file3
-
-INPUT_END
-test_expect_success \
-    'C: incremental import create pack from stdin' \
-    'git fast-import <input &&
-        git whatchanged branch'
+test_expect_success 'C: incremental import create pack from stdin' '
+       newf=`echo hi newf | git hash-object -w --stdin` &&
+       oldf=`git rev-parse --verify master:file2` &&
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       second
+       COMMIT
+
+       from refs/heads/master
+       M 644 $oldf file2/oldf
+       M 755 $newf file2/newf
+       D file3
+
+       INPUT_END
+
+       git fast-import <input &&
+       git whatchanged branch
+'
 
 test_expect_success 'C: verify pack' '
        verify_packs
 '
 
-test_expect_success \
-       'C: validate reuse existing blob' \
-       'test $newf = `git rev-parse --verify branch:file2/newf` &&
-        test $oldf = `git rev-parse --verify branch:file2/oldf`'
-
-cat >expect <<EOF
-parent `git rev-parse --verify master^0`
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-second
-EOF
-test_expect_success \
-       'C: verify commit' \
-       'git cat-file commit branch | sed 1d >actual &&
-        test_cmp expect actual'
-
-cat >expect <<EOF
-:000000 100755 0000000000000000000000000000000000000000 f1fb5da718392694d0076d677d6d0e364c79b0bc A     file2/newf
-:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100  file2   file2/oldf
-:100644 000000 0d92e9f3374ae2947c23aa477cbc68ce598135f1 0000000000000000000000000000000000000000 D     file3
-EOF
-git diff-tree -M -r master branch >actual
-test_expect_success \
-       'C: validate rename result' \
-       'compare_diff_raw expect actual'
+test_expect_success 'C: validate reuse existing blob' '
+       test $newf = `git rev-parse --verify branch:file2/newf` &&
+       test $oldf = `git rev-parse --verify branch:file2/oldf`
+'
+
+test_expect_success 'C: verify commit' '
+       cat >expect <<-EOF &&
+       parent `git rev-parse --verify master^0`
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+       second
+       EOF
+
+       git cat-file commit branch | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'C: validate rename result' '
+       cat >expect <<-EOF &&
+       :000000 100755 0000000000000000000000000000000000000000 f1fb5da718392694d0076d677d6d0e364c79b0bc A      file2/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100   file2   file2/oldf
+       :100644 000000 0d92e9f3374ae2947c23aa477cbc68ce598135f1 0000000000000000000000000000000000000000 D      file3
+       EOF
+       git diff-tree -M -r master branch >actual &&
+       compare_diff_raw expect actual
+'
 
 ###
 ### series D
 ###
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/branch
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-third
-COMMIT
-
-from refs/heads/branch^0
-M 644 inline newdir/interesting
-data <<EOF
-$file5_data
-EOF
-
-M 755 inline newdir/exec.sh
-data <<EOF
-$file6_data
-EOF
-
-INPUT_END
-test_expect_success \
-    'D: inline data in commit' \
-    'git fast-import <input &&
-        git whatchanged branch'
+test_expect_success 'D: inline data in commit' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       third
+       COMMIT
+
+       from refs/heads/branch^0
+       M 644 inline newdir/interesting
+       data <<EOF
+       $file5_data
+       EOF
+
+       M 755 inline newdir/exec.sh
+       data <<EOF
+       $file6_data
+       EOF
+
+       INPUT_END
+
+       git fast-import <input &&
+       git whatchanged branch
+'
 
 test_expect_success 'D: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-:000000 100755 0000000000000000000000000000000000000000 e74b7d465e52746be2b4bae983670711e6e66657 A     newdir/exec.sh
-:000000 100644 0000000000000000000000000000000000000000 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 A     newdir/interesting
-EOF
-git diff-tree -M -r branch^ branch >actual
-test_expect_success \
-       'D: validate new files added' \
-       'compare_diff_raw expect actual'
+test_expect_success 'D: validate new files added' '
+       cat >expect <<-EOF &&
+       :000000 100755 0000000000000000000000000000000000000000 e74b7d465e52746be2b4bae983670711e6e66657 A      newdir/exec.sh
+       :000000 100644 0000000000000000000000000000000000000000 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 A      newdir/interesting
+       EOF
+       git diff-tree -M -r branch^ branch >actual &&
+       compare_diff_raw expect actual
+'
 
-echo "$file5_data" >expect
-test_expect_success \
-       'D: verify file5' \
-       'git cat-file blob branch:newdir/interesting >actual &&
-        test_cmp expect actual'
+test_expect_success 'D: verify file5' '
+       echo "$file5_data" >expect &&
+       git cat-file blob branch:newdir/interesting >actual &&
+       test_cmp expect actual
+'
 
-echo "$file6_data" >expect
-test_expect_success \
-       'D: verify file6' \
-       'git cat-file blob branch:newdir/exec.sh >actual &&
-        test_cmp expect actual'
+test_expect_success 'D: verify file6' '
+       echo "$file6_data" >expect &&
+       git cat-file blob branch:newdir/exec.sh >actual &&
+       test_cmp expect actual
+'
 
 ###
 ### series E
 ###
 
-cat >input <<INPUT_END
-commit refs/heads/branch
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> Tue Feb 6 11:22:18 2007 -0500
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:02 2007 -0500
-data <<COMMIT
-RFC 2822 type date
-COMMIT
+test_expect_success 'E: rfc2822 date, --date-format=raw' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> Tue Feb 6 11:22:18 2007 -0500
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> Tue Feb 6 12:35:02 2007 -0500
+       data <<COMMIT
+       RFC 2822 type date
+       COMMIT
+
+       from refs/heads/branch^0
 
-from refs/heads/branch^0
+       INPUT_END
 
-INPUT_END
-test_expect_success 'E: rfc2822 date, --date-format=raw' '
-    test_must_fail git fast-import --date-format=raw <input
+       test_must_fail git fast-import --date-format=raw <input
+'
+test_expect_success 'E: rfc2822 date, --date-format=rfc2822' '
+       git fast-import --date-format=rfc2822 <input
 '
-test_expect_success \
-    'E: rfc2822 date, --date-format=rfc2822' \
-    'git fast-import --date-format=rfc2822 <input'
 
 test_expect_success 'E: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1170783302 -0500
+test_expect_success 'E: verify commit' '
+       cat >expect <<-EOF &&
+       author $GIT_AUTHOR_NAME <$GIT_AUTHOR_EMAIL> 1170778938 -0500
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1170783302 -0500
 
-RFC 2822 type date
-EOF
-test_expect_success \
-       'E: verify commit' \
-       'git cat-file commit branch | sed 1,2d >actual &&
-       test_cmp expect actual'
+       RFC 2822 type date
+       EOF
+       git cat-file commit branch | sed 1,2d >actual &&
+       test_cmp expect actual
+'
 
 ###
 ### series F
 ###
 
-old_branch=`git rev-parse --verify branch^0`
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/branch
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-losing things already?
-COMMIT
-
-from refs/heads/branch~1
-
-reset refs/heads/other
-from refs/heads/branch
-
-INPUT_END
-test_expect_success \
-    'F: non-fast-forward update skips' \
-    'if git fast-import <input
-        then
-               echo BAD gfi did not fail
-               return 1
-        else
-               if test $old_branch = `git rev-parse --verify branch^0`
-               then
-                       : branch unaffected and failure returned
-                       return 0
-               else
-                       echo BAD gfi changed branch $old_branch
-                       return 1
-               fi
-        fi
-       '
+test_expect_success 'F: non-fast-forward update skips' '
+       old_branch=`git rev-parse --verify branch^0` &&
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       losing things already?
+       COMMIT
+
+       from refs/heads/branch~1
+
+       reset refs/heads/other
+       from refs/heads/branch
+
+       INPUT_END
+
+       test_must_fail git fast-import <input &&
+       # branch must remain unaffected
+       test $old_branch = `git rev-parse --verify branch^0`
+'
 
 test_expect_success 'F: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-tree `git rev-parse branch~1^{tree}`
-parent `git rev-parse branch~1`
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+test_expect_success 'F: verify other commit' '
+       cat >expect <<-EOF &&
+       tree `git rev-parse branch~1^{tree}`
+       parent `git rev-parse branch~1`
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-losing things already?
-EOF
-test_expect_success \
-       'F: verify other commit' \
-       'git cat-file commit other >actual &&
-       test_cmp expect actual'
+       losing things already?
+       EOF
+       git cat-file commit other >actual &&
+       test_cmp expect actual
+'
 
 ###
 ### series G
 ###
 
-old_branch=`git rev-parse --verify branch^0`
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/branch
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-losing things already?
-COMMIT
+test_expect_success 'G: non-fast-forward update forced' '
+       old_branch=`git rev-parse --verify branch^0` &&
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/branch
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       losing things already?
+       COMMIT
 
-from refs/heads/branch~1
+       from refs/heads/branch~1
 
-INPUT_END
-test_expect_success \
-    'G: non-fast-forward update forced' \
-    'git fast-import --force <input'
+       INPUT_END
+       git fast-import --force <input
+'
 
 test_expect_success 'G: verify pack' '
        verify_packs
 '
 
-test_expect_success \
-       'G: branch changed, but logged' \
-       'test $old_branch != `git rev-parse --verify branch^0` &&
-        test $old_branch = `git rev-parse --verify branch@{1}`'
+test_expect_success 'G: branch changed, but logged' '
+       test $old_branch != `git rev-parse --verify branch^0` &&
+       test $old_branch = `git rev-parse --verify branch@{1}`
+'
 
 ###
 ### series H
 ###
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/H
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-third
-COMMIT
-
-from refs/heads/branch^0
-M 644 inline i-will-die
-data <<EOF
-this file will never exist.
-EOF
-
-deleteall
-M 644 inline h/e/l/lo
-data <<EOF
-$file5_data
-EOF
-
-INPUT_END
-test_expect_success \
-    'H: deletall, add 1' \
-    'git fast-import <input &&
-        git whatchanged H'
+test_expect_success 'H: deletall, add 1' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/H
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       third
+       COMMIT
+
+       from refs/heads/branch^0
+       M 644 inline i-will-die
+       data <<EOF
+       this file will never exist.
+       EOF
+
+       deleteall
+       M 644 inline h/e/l/lo
+       data <<EOF
+       $file5_data
+       EOF
+
+       INPUT_END
+       git fast-import <input &&
+       git whatchanged H
+'
 
 test_expect_success 'H: verify pack' '
        verify_packs
 '
 
-cat >expect <<EOF
-:100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D     file2/newf
-:100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D     file2/oldf
-:100755 000000 85df50785d62d3b05ab03d9cbf7e4a0b49449730 0000000000000000000000000000000000000000 D     file4
-:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100  newdir/interesting      h/e/l/lo
-:100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D     newdir/exec.sh
-EOF
-git diff-tree -M -r H^ H >actual
-test_expect_success \
-       'H: validate old files removed, new files added' \
-       'compare_diff_raw expect actual'
-
-echo "$file5_data" >expect
-test_expect_success \
-       'H: verify file' \
-       'git cat-file blob H:h/e/l/lo >actual &&
-        test_cmp expect actual'
+test_expect_success 'H: validate old files removed, new files added' '
+       cat >expect <<-EOF &&
+       :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D      file2/newf
+       :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D      file2/oldf
+       :100755 000000 85df50785d62d3b05ab03d9cbf7e4a0b49449730 0000000000000000000000000000000000000000 D      file4
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100   newdir/interesting      h/e/l/lo
+       :100755 000000 e74b7d465e52746be2b4bae983670711e6e66657 0000000000000000000000000000000000000000 D      newdir/exec.sh
+       EOF
+       git diff-tree -M -r H^ H >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'H: verify file' '
+       echo "$file5_data" >expect &&
+       git cat-file blob H:h/e/l/lo >actual &&
+       test_cmp expect actual
+'
 
 ###
 ### series I
 ###
 
-cat >input <<INPUT_END
-commit refs/heads/export-boundary
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-we have a border.  its only 40 characters wide.
-COMMIT
+test_expect_success 'I: export-pack-edges' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/export-boundary
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       we have a border.  its only 40 characters wide.
+       COMMIT
 
-from refs/heads/branch
+       from refs/heads/branch
 
-INPUT_END
-test_expect_success \
-    'I: export-pack-edges' \
-    'git fast-import --export-pack-edges=edges.list <input'
+       INPUT_END
+       git fast-import --export-pack-edges=edges.list <input
+'
 
-cat >expect <<EOF
-.git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
-EOF
-test_expect_success \
-       'I: verify edge list' \
-       'sed -e s/pack-.*pack/pack-.pack/ edges.list >actual &&
-        test_cmp expect actual'
+test_expect_success 'I: verify edge list' '
+       cat >expect <<-EOF &&
+       .git/objects/pack/pack-.pack: `git rev-parse --verify export-boundary`
+       EOF
+       sed -e s/pack-.*pack/pack-.pack/ edges.list >actual &&
+       test_cmp expect actual
+'
 
 ###
 ### series J
 ###
 
-cat >input <<INPUT_END
-commit refs/heads/J
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-create J
-COMMIT
-
-from refs/heads/branch
-
-reset refs/heads/J
-
-commit refs/heads/J
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-initialize J
-COMMIT
-
-INPUT_END
-test_expect_success \
-    'J: reset existing branch creates empty commit' \
-    'git fast-import <input'
-test_expect_success \
-       'J: branch has 1 commit, empty tree' \
-       'test 1 = `git rev-list J | wc -l` &&
-        test 0 = `git ls-tree J | wc -l`'
-
-cat >input <<INPUT_END
-reset refs/heads/J2
-
-tag wrong_tag
-from refs/heads/J2
-data <<EOF
-Tag branch that was reset.
-EOF
-INPUT_END
-test_expect_success \
-       'J: tag must fail on empty branch' \
-       'test_must_fail git fast-import <input'
+test_expect_success 'J: reset existing branch creates empty commit' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/J
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       create J
+       COMMIT
+
+       from refs/heads/branch
+
+       reset refs/heads/J
+
+       commit refs/heads/J
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       initialize J
+       COMMIT
+
+       INPUT_END
+       git fast-import <input
+'
+test_expect_success 'J: branch has 1 commit, empty tree' '
+       test 1 = `git rev-list J | wc -l` &&
+       test 0 = `git ls-tree J | wc -l`
+'
+
+test_expect_success 'J: tag must fail on empty branch' '
+       cat >input <<-INPUT_END &&
+       reset refs/heads/J2
+
+       tag wrong_tag
+       from refs/heads/J2
+       data <<EOF
+       Tag branch that was reset.
+       EOF
+       INPUT_END
+       test_must_fail git fast-import <input
+'
+
 ###
 ### series K
 ###
 
-cat >input <<INPUT_END
-commit refs/heads/K
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-create K
-COMMIT
+test_expect_success 'K: reinit branch with from' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/K
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       create K
+       COMMIT
 
-from refs/heads/branch
+       from refs/heads/branch
 
-commit refs/heads/K
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-redo K
-COMMIT
+       commit refs/heads/K
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       redo K
+       COMMIT
 
-from refs/heads/branch^1
+       from refs/heads/branch^1
 
-INPUT_END
-test_expect_success \
-    'K: reinit branch with from' \
-    'git fast-import <input'
-test_expect_success \
-    'K: verify K^1 = branch^1' \
-    'test `git rev-parse --verify branch^1` \
-               = `git rev-parse --verify K^1`'
+       INPUT_END
+       git fast-import <input
+'
+test_expect_success 'K: verify K^1 = branch^1' '
+       test `git rev-parse --verify branch^1` \
+               = `git rev-parse --verify K^1`
+'
 
 ###
 ### series L
 ###
 
-cat >input <<INPUT_END
-blob
-mark :1
-data <<EOF
-some data
-EOF
-
-blob
-mark :2
-data <<EOF
-other data
-EOF
-
-commit refs/heads/L
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-create L
-COMMIT
-
-M 644 :1 b.
-M 644 :1 b/other
-M 644 :1 ba
-
-commit refs/heads/L
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-update L
-COMMIT
-
-M 644 :2 b.
-M 644 :2 b/other
-M 644 :2 ba
-INPUT_END
-
-cat >expect <<EXPECT_END
-:100644 100644 4268632... 55d3a52... M b.
-:040000 040000 0ae5cac... 443c768... M b
-:100644 100644 4268632... 55d3a52... M ba
-EXPECT_END
-
-test_expect_success \
-    'L: verify internal tree sorting' \
-       'git fast-import <input &&
-        git diff-tree --abbrev --raw L^ L >output &&
-        test_cmp expect output'
-
-cat >input <<INPUT_END
-blob
-mark :1
-data <<EOF
-the data
-EOF
-
-commit refs/heads/L2
-committer C O Mitter <committer@example.com> 1112912473 -0700
-data <<COMMIT
-init L2
-COMMIT
-M 644 :1 a/b/c
-M 644 :1 a/b/d
-M 644 :1 a/e/f
-
-commit refs/heads/L2
-committer C O Mitter <committer@example.com> 1112912473 -0700
-data <<COMMIT
-update L2
-COMMIT
-C a g
-C a/e g/b
-M 644 :1 g/b/h
-INPUT_END
-
-cat <<EOF >expect
-g/b/f
-g/b/h
-EOF
-
-test_expect_success \
-    'L: nested tree copy does not corrupt deltas' \
-       'git fast-import <input &&
+test_expect_success 'L: verify internal tree sorting' '
+       cat >input <<-INPUT_END &&
+       blob
+       mark :1
+       data <<EOF
+       some data
+       EOF
+
+       blob
+       mark :2
+       data <<EOF
+       other data
+       EOF
+
+       commit refs/heads/L
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       create L
+       COMMIT
+
+       M 644 :1 b.
+       M 644 :1 b/other
+       M 644 :1 ba
+
+       commit refs/heads/L
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       update L
+       COMMIT
+
+       M 644 :2 b.
+       M 644 :2 b/other
+       M 644 :2 ba
+       INPUT_END
+
+       cat >expect <<-EXPECT_END &&
+       :100644 100644 4268632... 55d3a52... M  b.
+       :040000 040000 0ae5cac... 443c768... M  b
+       :100644 100644 4268632... 55d3a52... M  ba
+       EXPECT_END
+
+       git fast-import <input &&
+       git diff-tree --abbrev --raw L^ L >output &&
+       test_cmp expect output
+'
+
+test_expect_success 'L: nested tree copy does not corrupt deltas' '
+       cat >input <<-INPUT_END &&
+       blob
+       mark :1
+       data <<EOF
+       the data
+       EOF
+
+       commit refs/heads/L2
+       committer C O Mitter <committer@example.com> 1112912473 -0700
+       data <<COMMIT
+       init L2
+       COMMIT
+       M 644 :1 a/b/c
+       M 644 :1 a/b/d
+       M 644 :1 a/e/f
+
+       commit refs/heads/L2
+       committer C O Mitter <committer@example.com> 1112912473 -0700
+       data <<COMMIT
+       update L2
+       COMMIT
+       C a g
+       C a/e g/b
+       M 644 :1 g/b/h
+       INPUT_END
+
+       cat >expect <<-\EOF &&
+       g/b/f
+       g/b/h
+       EOF
+
+       test_when_finished "git update-ref -d refs/heads/L2" &&
+       git fast-import <input &&
        git ls-tree L2 g/b/ >tmp &&
        cat tmp | cut -f 2 >actual &&
        test_cmp expect actual &&
-       git fsck `git rev-parse L2`'
-
-git update-ref -d refs/heads/L2
+       git fsck `git rev-parse L2`
+'
 
 ###
 ### series M
 ###
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/M1
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-file rename
-COMMIT
-
-from refs/heads/branch^0
-R file2/newf file2/n.e.w.f
-
-INPUT_END
-
-cat >expect <<EOF
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100  file2/newf      file2/n.e.w.f
-EOF
-test_expect_success \
-       'M: rename file in same subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -M -r M1^ M1 >actual &&
-        compare_diff_raw expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/M2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-file rename
-COMMIT
-
-from refs/heads/branch^0
-R file2/newf i/am/new/to/you
-
-INPUT_END
-
-cat >expect <<EOF
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100  file2/newf      i/am/new/to/you
-EOF
-test_expect_success \
-       'M: rename file to new subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -M -r M2^ M2 >actual &&
-        compare_diff_raw expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/M3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-file rename
-COMMIT
-
-from refs/heads/M2^0
-R i other/sub
-
-INPUT_END
-
-cat >expect <<EOF
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100  i/am/new/to/you other/sub/am/new/to/you
-EOF
-test_expect_success \
-       'M: rename subdirectory to new subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -M -r M3^ M3 >actual &&
-        compare_diff_raw expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/M4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-rename root
-COMMIT
-
-from refs/heads/M2^0
-R "" sub
-
-INPUT_END
-
-cat >expect <<EOF
-:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100  file2/oldf      sub/file2/oldf
-:100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100  file4   sub/file4
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100  i/am/new/to/you sub/i/am/new/to/you
-:100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100  newdir/exec.sh  sub/newdir/exec.sh
-:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100  newdir/interesting      sub/newdir/interesting
-EOF
-test_expect_success \
-       'M: rename root to subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -M -r M4^ M4 >actual &&
-        cat actual &&
-        compare_diff_raw expect actual'
+test_expect_success 'M: rename file in same subdirectory' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/M1
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       file rename
+       COMMIT
+
+       from refs/heads/branch^0
+       R file2/newf file2/n.e.w.f
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100   file2/newf      file2/n.e.w.f
+       EOF
+       git fast-import <input &&
+       git diff-tree -M -r M1^ M1 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename file to new subdirectory' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/M2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       file rename
+       COMMIT
+
+       from refs/heads/branch^0
+       R file2/newf i/am/new/to/you
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100   file2/newf      i/am/new/to/you
+       EOF
+       git fast-import <input &&
+       git diff-tree -M -r M2^ M2 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename subdirectory to new subdirectory' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/M3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       file rename
+       COMMIT
+
+       from refs/heads/M2^0
+       R i other/sub
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100   i/am/new/to/you other/sub/am/new/to/you
+       EOF
+       git fast-import <input &&
+       git diff-tree -M -r M3^ M3 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'M: rename root to subdirectory' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/M4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       rename root
+       COMMIT
+
+       from refs/heads/M2^0
+       R "" sub
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 R100   file2/oldf      sub/file2/oldf
+       :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 R100   file4   sub/file4
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc R100   i/am/new/to/you sub/i/am/new/to/you
+       :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 R100   newdir/exec.sh  sub/newdir/exec.sh
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 R100   newdir/interesting      sub/newdir/interesting
+       EOF
+       git fast-import <input &&
+       git diff-tree -M -r M4^ M4 >actual &&
+       cat actual &&
+       compare_diff_raw expect actual
+'
 
 ###
 ### series N
 ###
 
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/N1
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-file copy
-COMMIT
-
-from refs/heads/branch^0
-C file2/newf file2/n.e.w.f
-
-INPUT_END
-
-cat >expect <<EOF
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100  file2/newf      file2/n.e.w.f
-EOF
-test_expect_success \
-       'N: copy file in same subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
-        compare_diff_raw expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/N2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-clean directory copy
-COMMIT
-
-from refs/heads/branch^0
-C file2 file3
-
-commit refs/heads/N2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-modify directory copy
-COMMIT
-
-M 644 inline file3/file5
-data <<EOF
-$file5_data
-EOF
-
-INPUT_END
-
-cat >expect <<EOF
-:100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100  newdir/interesting      file3/file5
-:100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100  file2/newf      file3/newf
-:100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100  file2/oldf      file3/oldf
-EOF
-test_expect_success \
-       'N: copy then modify subdirectory' \
-       'git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
-        compare_diff_raw expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/N3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-dirty directory copy
-COMMIT
-
-from refs/heads/branch^0
-M 644 inline file2/file5
-data <<EOF
-$file5_data
-EOF
-
-C file2 file3
-D file2/file5
-
-INPUT_END
-
-test_expect_success \
-       'N: copy dirty subdirectory' \
-       'git fast-import <input &&
-        test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`'
-
-test_expect_success \
-       'N: copy directory by id' \
-       'cat >expect <<-\EOF &&
+test_expect_success 'N: copy file in same subdirectory' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/N1
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       file copy
+       COMMIT
+
+       from refs/heads/branch^0
+       C file2/newf file2/n.e.w.f
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file2/n.e.w.f
+       EOF
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r N1^ N1 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'N: copy then modify subdirectory' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/N2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       clean directory copy
+       COMMIT
+
+       from refs/heads/branch^0
+       C file2 file3
+
+       commit refs/heads/N2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       modify directory copy
+       COMMIT
+
+       M 644 inline file3/file5
+       data <<EOF
+       $file5_data
+       EOF
+
+       INPUT_END
+
+       cat >expect <<-EOF &&
+       :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      file3/file5
+       :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
+       :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
+       EOF
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r N2^^ N2 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'N: copy dirty subdirectory' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/N3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       dirty directory copy
+       COMMIT
+
+       from refs/heads/branch^0
+       M 644 inline file2/file5
+       data <<EOF
+       $file5_data
+       EOF
+
+       C file2 file3
+       D file2/file5
+
+       INPUT_END
+
+       git fast-import <input &&
+       test `git rev-parse N2^{tree}` = `git rev-parse N3^{tree}`
+'
+
+test_expect_success 'N: copy directory by id' '
+       cat >expect <<-\EOF &&
        :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
        :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
        EOF
-        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
-        cat >input <<-INPUT_END &&
+       subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N4
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1127,9 +1125,10 @@ test_expect_success \
        from refs/heads/branch^0
        M 040000 $subdir file3
        INPUT_END
-        git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
-        compare_diff_raw expect actual'
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r N4^ N4 >actual &&
+       compare_diff_raw expect actual
+'
 
 test_expect_success PIPE 'N: read and copy directory' '
        cat >expect <<-\EOF &&
@@ -1202,14 +1201,13 @@ test_expect_success PIPE 'N: empty directory reads as missing' '
        test_cmp expect actual
 '
 
-test_expect_success \
-       'N: copy root directory by tree hash' \
-       'cat >expect <<-\EOF &&
+test_expect_success 'N: copy root directory by tree hash' '
+       cat >expect <<-\EOF &&
        :100755 000000 f1fb5da718392694d0076d677d6d0e364c79b0bc 0000000000000000000000000000000000000000 D      file3/newf
        :100644 000000 7123f7f44e39be127c5eb701e5968176ee9d78b1 0000000000000000000000000000000000000000 D      file3/oldf
        EOF
-        root=$(git rev-parse refs/heads/branch^0^{tree}) &&
-        cat >input <<-INPUT_END &&
+       root=$(git rev-parse refs/heads/branch^0^{tree}) &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N6
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1219,20 +1217,20 @@ test_expect_success \
        from refs/heads/branch^0
        M 040000 $root ""
        INPUT_END
-        git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
-        compare_diff_raw expect actual'
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r N4 N6 >actual &&
+       compare_diff_raw expect actual
+'
 
-test_expect_success \
-       'N: copy root by path' \
-       'cat >expect <<-\EOF &&
+test_expect_success 'N: copy root by path' '
+       cat >expect <<-\EOF &&
        :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      oldroot/file2/newf
        :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      oldroot/file2/oldf
        :100755 100755 85df50785d62d3b05ab03d9cbf7e4a0b49449730 85df50785d62d3b05ab03d9cbf7e4a0b49449730 C100   file4   oldroot/file4
        :100755 100755 e74b7d465e52746be2b4bae983670711e6e66657 e74b7d465e52746be2b4bae983670711e6e66657 C100   newdir/exec.sh  oldroot/newdir/exec.sh
        :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      oldroot/newdir/interesting
        EOF
-        cat >input <<-INPUT_END &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N-copy-root-path
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1242,21 +1240,21 @@ test_expect_success \
        from refs/heads/branch^0
        C "" oldroot
        INPUT_END
-        git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
-        compare_diff_raw expect actual'
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r branch N-copy-root-path >actual &&
+       compare_diff_raw expect actual
+'
 
-test_expect_success \
-       'N: delete directory by copying' \
-       'cat >expect <<-\EOF &&
+test_expect_success 'N: delete directory by copying' '
+       cat >expect <<-\EOF &&
        OBJID
        :100644 000000 OBJID OBJID D    foo/bar/qux
        OBJID
        :000000 100644 OBJID OBJID A    foo/bar/baz
        :000000 100644 OBJID OBJID A    foo/bar/qux
        EOF
-        empty_tree=$(git mktree </dev/null) &&
-        cat >input <<-INPUT_END &&
+       empty_tree=$(git mktree </dev/null) &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N-delete
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1282,21 +1280,21 @@ test_expect_success \
 
        M 040000 $empty_tree foo/bar/qux
        INPUT_END
-        git fast-import <input &&
-        git rev-list N-delete |
+       git fast-import <input &&
+       git rev-list N-delete |
                git diff-tree -r --stdin --root --always |
                sed -e "s/$_x40/OBJID/g" >actual &&
-        test_cmp expect actual'
+       test_cmp expect actual
+'
 
-test_expect_success \
-       'N: modify copied tree' \
-       'cat >expect <<-\EOF &&
+test_expect_success 'N: modify copied tree' '
+       cat >expect <<-\EOF &&
        :100644 100644 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 fcf778cda181eaa1cbc9e9ce3a2e15ee9f9fe791 C100   newdir/interesting      file3/file5
        :100755 100755 f1fb5da718392694d0076d677d6d0e364c79b0bc f1fb5da718392694d0076d677d6d0e364c79b0bc C100   file2/newf      file3/newf
        :100644 100644 7123f7f44e39be127c5eb701e5968176ee9d78b1 7123f7f44e39be127c5eb701e5968176ee9d78b1 C100   file2/oldf      file3/oldf
        EOF
-        subdir=$(git rev-parse refs/heads/branch^0:file2) &&
-        cat >input <<-INPUT_END &&
+       subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N5
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1317,14 +1315,14 @@ test_expect_success \
        $file5_data
        EOF
        INPUT_END
-        git fast-import <input &&
-        git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
-        compare_diff_raw expect actual'
-
-test_expect_success \
-       'N: reject foo/ syntax' \
-       'subdir=$(git rev-parse refs/heads/branch^0:file2) &&
-        test_must_fail git fast-import <<-INPUT_END
+       git fast-import <input &&
+       git diff-tree -C --find-copies-harder -r N5^^ N5 >actual &&
+       compare_diff_raw expect actual
+'
+
+test_expect_success 'N: reject foo/ syntax' '
+       subdir=$(git rev-parse refs/heads/branch^0:file2) &&
+       test_must_fail git fast-import <<-INPUT_END
        commit refs/heads/N5B
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1333,11 +1331,11 @@ test_expect_success \
 
        from refs/heads/branch^0
        M 040000 $subdir file3/
-       INPUT_END'
+       INPUT_END
+'
 
-test_expect_success \
-       'N: reject foo/ syntax in copy source' \
-       'test_must_fail git fast-import <<-INPUT_END
+test_expect_success 'N: reject foo/ syntax in copy source' '
+       test_must_fail git fast-import <<-INPUT_END
        commit refs/heads/N5C
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1346,11 +1344,11 @@ test_expect_success \
 
        from refs/heads/branch^0
        C file2/ file3
-       INPUT_END'
+       INPUT_END
+'
 
-test_expect_success \
-       'N: reject foo/ syntax in rename source' \
-       'test_must_fail git fast-import <<-INPUT_END
+test_expect_success 'N: reject foo/ syntax in rename source' '
+       test_must_fail git fast-import <<-INPUT_END
        commit refs/heads/N5D
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1359,11 +1357,11 @@ test_expect_success \
 
        from refs/heads/branch^0
        R file2/ file3
-       INPUT_END'
+       INPUT_END
+'
 
-test_expect_success \
-       'N: reject foo/ syntax in ls argument' \
-       'test_must_fail git fast-import <<-INPUT_END
+test_expect_success 'N: reject foo/ syntax in ls argument' '
+       test_must_fail git fast-import <<-INPUT_END
        commit refs/heads/N5E
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1372,13 +1370,13 @@ test_expect_success \
 
        from refs/heads/branch^0
        ls "file2/"
-       INPUT_END'
+       INPUT_END
+'
 
-test_expect_success \
-       'N: copy to root by id and modify' \
-       'echo "hello, world" >expect.foo &&
-        echo hello >expect.bar &&
-        git fast-import <<-SETUP_END &&
+test_expect_success 'N: copy to root by id and modify' '
+       echo "hello, world" >expect.foo &&
+       echo hello >expect.bar &&
+       git fast-import <<-SETUP_END &&
        commit refs/heads/N7
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1392,8 +1390,8 @@ test_expect_success \
        EOF
        SETUP_END
 
-        tree=$(git rev-parse --verify N7:) &&
-        git fast-import <<-INPUT_END &&
+       tree=$(git rev-parse --verify N7:) &&
+       git fast-import <<-INPUT_END &&
        commit refs/heads/N8
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1406,15 +1404,15 @@ test_expect_success \
        hello, world
        EOF
        INPUT_END
-        git show N8:foo/foo >actual.foo &&
-        git show N8:foo/bar >actual.bar &&
-        test_cmp expect.foo actual.foo &&
-        test_cmp expect.bar actual.bar'
-
-test_expect_success \
-       'N: extract subtree' \
-       'branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
-        cat >input <<-INPUT_END &&
+       git show N8:foo/foo >actual.foo &&
+       git show N8:foo/bar >actual.bar &&
+       test_cmp expect.foo actual.foo &&
+       test_cmp expect.bar actual.bar
+'
+
+test_expect_success 'N: extract subtree' '
+       branch=$(git rev-parse --verify refs/heads/branch^{tree}) &&
+       cat >input <<-INPUT_END &&
        commit refs/heads/N9
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
@@ -1424,713 +1422,729 @@ test_expect_success \
        M 040000 $branch ""
        C "newdir" ""
        INPUT_END
-        git fast-import <input &&
-        git diff --exit-code branch:newdir N9'
-
-test_expect_success \
-       'N: modify subtree, extract it, and modify again' \
-       'echo hello >expect.baz &&
-        echo hello, world >expect.qux &&
-        git fast-import <<-SETUP_END &&
+       git fast-import <input &&
+       git diff --exit-code branch:newdir N9
+'
+
+test_expect_success 'N: modify subtree, extract it, and modify again' '
+       echo hello >expect.baz &&
+       echo hello, world >expect.qux &&
+       git fast-import <<-SETUP_END &&
        commit refs/heads/N10
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
-       hello, tree
+       hello, tree
+       COMMIT
+
+       deleteall
+       M 644 inline foo/bar/baz
+       data <<EOF
+       hello
+       EOF
+       SETUP_END
+
+       tree=$(git rev-parse --verify N10:) &&
+       git fast-import <<-INPUT_END &&
+       commit refs/heads/N11
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       copy to root by id and modify
+       COMMIT
+
+       M 040000 $tree ""
+       M 100644 inline foo/bar/qux
+       data <<EOF
+       hello, world
+       EOF
+       R "foo" ""
+       C "bar/qux" "bar/quux"
+       INPUT_END
+       git show N11:bar/baz >actual.baz &&
+       git show N11:bar/qux >actual.qux &&
+       git show N11:bar/quux >actual.quux &&
+       test_cmp expect.baz actual.baz &&
+       test_cmp expect.qux actual.qux &&
+       test_cmp expect.qux actual.quux'
+
+###
+### series O
+###
+
+test_expect_success 'O: comments are all skipped' '
+       cat >input <<-INPUT_END &&
+       #we will
+       commit refs/heads/O1
+       # -- ignore all of this text
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       # $GIT_COMMITTER_NAME has inserted here for his benefit.
+       data <<COMMIT
+       dirty directory copy
+       COMMIT
+
+       # do not forget the import blank line!
+       #
+       # yes, we started from our usual base of branch^0.
+       # i like branch^0.
+       from refs/heads/branch^0
+       # and we need to reuse file2/file5 from N3 above.
+       M 644 inline file2/file5
+       # otherwise the tree will be different
+       data <<EOF
+       $file5_data
+       EOF
+
+       # do not forget to copy file2 to file3
+       C file2 file3
+       #
+       # or to delete file5 from file2.
+       D file2/file5
+       # are we done yet?
+
+       INPUT_END
+
+       git fast-import <input &&
+       test `git rev-parse N3` = `git rev-parse O1`
+'
+
+test_expect_success 'O: blank lines not necessary after data commands' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/O2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       dirty directory copy
+       COMMIT
+       from refs/heads/branch^0
+       M 644 inline file2/file5
+       data <<EOF
+       $file5_data
+       EOF
+       C file2 file3
+       D file2/file5
+
+       INPUT_END
+
+       git fast-import <input &&
+       test `git rev-parse N3` = `git rev-parse O2`
+'
+
+test_expect_success 'O: repack before next test' '
+       git repack -a -d
+'
+
+test_expect_success 'O: blank lines not necessary after other commands' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/O3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zstring
+       COMMIT
+       commit refs/heads/O3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zof
+       COMMIT
+       checkpoint
+       commit refs/heads/O3
+       mark :5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zempty
+       COMMIT
+       checkpoint
+       commit refs/heads/O3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zcommits
+       COMMIT
+       reset refs/tags/O3-2nd
+       from :5
+       reset refs/tags/O3-3rd
+       from :5
+       INPUT_END
+
+       cat >expect <<-INPUT_END &&
+       string
+       of
+       empty
+       commits
+       INPUT_END
+
+       git fast-import <input &&
+       test 8 = `find .git/objects/pack -type f | wc -l` &&
+       test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` &&
+       git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'O: progress outputs as requested by input' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/O4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zstring
+       COMMIT
+       commit refs/heads/O4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zof
+       COMMIT
+       progress Two commits down, 2 to go!
+       commit refs/heads/O4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zempty
+       COMMIT
+       progress Three commits down, 1 to go!
+       commit refs/heads/O4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       zcommits
+       COMMIT
+       progress done!
+       INPUT_END
+       git fast-import <input >actual &&
+       grep "progress " <input >expect &&
+       test_cmp expect actual
+'
+
+###
+### series P (gitlinks)
+###
+
+test_expect_success 'P: superproject & submodule mix' '
+       cat >input <<-INPUT_END &&
+       blob
+       mark :1
+       data 10
+       test file
+
+       reset refs/heads/sub
+       commit refs/heads/sub
+       mark :2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 12
+       sub_initial
+       M 100644 :1 file
+
+       blob
+       mark :3
+       data <<DATAEND
+       [submodule "sub"]
+               path = sub
+               url = "`pwd`/sub"
+       DATAEND
+
+       commit refs/heads/subuse1
+       mark :4
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 8
+       initial
+       from refs/heads/master
+       M 100644 :3 .gitmodules
+       M 160000 :2 sub
+
+       blob
+       mark :5
+       data 20
+       test file
+       more data
+
+       commit refs/heads/sub
+       mark :6
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 11
+       sub_second
+       from :2
+       M 100644 :5 file
+
+       commit refs/heads/subuse1
+       mark :7
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 7
+       second
+       from :4
+       M 160000 :6 sub
+
+       INPUT_END
+
+       git fast-import <input &&
+       git checkout subuse1 &&
+       rm -rf sub &&
+       mkdir sub &&
+       (
+               cd sub &&
+               git init &&
+               git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
+               git checkout master
+       ) &&
+       git submodule init &&
+       git submodule update
+'
+
+test_expect_success 'P: verbatim SHA gitlinks' '
+       SUBLAST=$(git rev-parse --verify sub) &&
+       SUBPREV=$(git rev-parse --verify sub^) &&
+
+       cat >input <<-INPUT_END &&
+       blob
+       mark :1
+       data <<DATAEND
+       [submodule "sub"]
+               path = sub
+               url = "`pwd`/sub"
+       DATAEND
+
+       commit refs/heads/subuse2
+       mark :2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 8
+       initial
+       from refs/heads/master
+       M 100644 :1 .gitmodules
+       M 160000 $SUBPREV sub
+
+       commit refs/heads/subuse2
+       mark :3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data 7
+       second
+       from :2
+       M 160000 $SUBLAST sub
+
+       INPUT_END
+
+       git branch -D sub &&
+       git gc &&
+       git prune &&
+       git fast-import <input &&
+       test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)
+'
+
+test_expect_success 'P: fail on inline gitlink' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/subuse3
+       mark :1
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       corrupt
+       COMMIT
+
+       from refs/heads/subuse2
+       M 160000 inline sub
+       data <<DATA
+       $SUBPREV
+       DATA
+
+       INPUT_END
+
+       test_must_fail git fast-import <input
+'
+
+test_expect_success 'P: fail on blob mark in gitlink' '
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       blob
+       mark :1
+       data <<DATA
+       $SUBPREV
+       DATA
+
+       commit refs/heads/subuse3
+       mark :2
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       corrupt
+       COMMIT
+
+       from refs/heads/subuse2
+       M 160000 :1 sub
+
+       INPUT_END
+
+       test_must_fail git fast-import <input
+'
+
+###
+### series Q (notes)
+###
+
+test_expect_success 'Q: commit notes' '
+       note1_data="The first note for the first commit" &&
+       note2_data="The first note for the second commit" &&
+       note3_data="The first note for the third commit" &&
+       note1b_data="The second note for the first commit" &&
+       note1c_data="The third note for the first commit" &&
+       note2b_data="The second note for the second commit" &&
+
+       test_tick &&
+       cat >input <<-INPUT_END &&
+       blob
+       mark :2
+       data <<EOF
+       $file2_data
+       EOF
+
+       commit refs/heads/notes-test
+       mark :3
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       first (:3)
+       COMMIT
+
+       M 644 :2 file2
+
+       blob
+       mark :4
+       data $file4_len
+       $file4_data
+       commit refs/heads/notes-test
+       mark :5
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       second (:5)
+       COMMIT
+
+       M 644 :4 file4
+
+       commit refs/heads/notes-test
+       mark :6
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       third (:6)
+       COMMIT
+
+       M 644 inline file5
+       data <<EOF
+       $file5_data
+       EOF
+
+       M 755 inline file6
+       data <<EOF
+       $file6_data
+       EOF
+
+       blob
+       mark :7
+       data <<EOF
+       $note1_data
+       EOF
+
+       blob
+       mark :8
+       data <<EOF
+       $note2_data
+       EOF
+
+       commit refs/notes/foobar
+       mark :9
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       notes (:9)
+       COMMIT
+
+       N :7 :3
+       N :8 :5
+       N inline :6
+       data <<EOF
+       $note3_data
+       EOF
+
+       commit refs/notes/foobar
+       mark :10
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       notes (:10)
+       COMMIT
+
+       N inline :3
+       data <<EOF
+       $note1b_data
+       EOF
+
+       commit refs/notes/foobar2
+       mark :11
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       notes (:11)
        COMMIT
 
-       deleteall
-       M 644 inline foo/bar/baz
+       N inline :3
        data <<EOF
-       hello
+       $note1c_data
        EOF
-       SETUP_END
 
-        tree=$(git rev-parse --verify N10:) &&
-        git fast-import <<-INPUT_END &&
-       commit refs/heads/N11
+       commit refs/notes/foobar
+       mark :12
        committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
        data <<COMMIT
-       copy to root by id and modify
+       notes (:12)
        COMMIT
 
-       M 040000 $tree ""
-       M 100644 inline foo/bar/qux
+       deleteall
+       N inline :5
        data <<EOF
-       hello, world
+       $note2b_data
        EOF
-       R "foo" ""
-       C "bar/qux" "bar/quux"
+
        INPUT_END
-        git show N11:bar/baz >actual.baz &&
-        git show N11:bar/qux >actual.qux &&
-        git show N11:bar/quux >actual.quux &&
-        test_cmp expect.baz actual.baz &&
-        test_cmp expect.qux actual.qux &&
-        test_cmp expect.qux actual.quux'
 
-###
-### series O
-###
+       git fast-import <input &&
+       git whatchanged notes-test
+'
 
-cat >input <<INPUT_END
-#we will
-commit refs/heads/O1
-# -- ignore all of this text
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-# $GIT_COMMITTER_NAME has inserted here for his benefit.
-data <<COMMIT
-dirty directory copy
-COMMIT
-
-# don't forget the import blank line!
-#
-# yes, we started from our usual base of branch^0.
-# i like branch^0.
-from refs/heads/branch^0
-# and we need to reuse file2/file5 from N3 above.
-M 644 inline file2/file5
-# otherwise the tree will be different
-data <<EOF
-$file5_data
-EOF
-
-# don't forget to copy file2 to file3
-C file2 file3
-#
-# or to delete file5 from file2.
-D file2/file5
-# are we done yet?
-
-INPUT_END
-
-test_expect_success \
-       'O: comments are all skipped' \
-       'git fast-import <input &&
-        test `git rev-parse N3` = `git rev-parse O1`'
-
-cat >input <<INPUT_END
-commit refs/heads/O2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-dirty directory copy
-COMMIT
-from refs/heads/branch^0
-M 644 inline file2/file5
-data <<EOF
-$file5_data
-EOF
-C file2 file3
-D file2/file5
-
-INPUT_END
-
-test_expect_success \
-       'O: blank lines not necessary after data commands' \
-       'git fast-import <input &&
-        test `git rev-parse N3` = `git rev-parse O2`'
-
-test_expect_success \
-       'O: repack before next test' \
-       'git repack -a -d'
-
-cat >input <<INPUT_END
-commit refs/heads/O3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zstring
-COMMIT
-commit refs/heads/O3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zof
-COMMIT
-checkpoint
-commit refs/heads/O3
-mark :5
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zempty
-COMMIT
-checkpoint
-commit refs/heads/O3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zcommits
-COMMIT
-reset refs/tags/O3-2nd
-from :5
-reset refs/tags/O3-3rd
-from :5
-INPUT_END
-
-cat >expect <<INPUT_END
-string
-of
-empty
-commits
-INPUT_END
-test_expect_success \
-       'O: blank lines not necessary after other commands' \
-       'git fast-import <input &&
-        test 8 = `find .git/objects/pack -type f | wc -l` &&
-        test `git rev-parse refs/tags/O3-2nd` = `git rev-parse O3^` &&
-        git log --reverse --pretty=oneline O3 | sed s/^.*z// >actual &&
-        test_cmp expect actual'
-
-cat >input <<INPUT_END
-commit refs/heads/O4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zstring
-COMMIT
-commit refs/heads/O4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zof
-COMMIT
-progress Two commits down, 2 to go!
-commit refs/heads/O4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zempty
-COMMIT
-progress Three commits down, 1 to go!
-commit refs/heads/O4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-zcommits
-COMMIT
-progress I'm done!
-INPUT_END
-test_expect_success \
-       'O: progress outputs as requested by input' \
-       'git fast-import <input >actual &&
-        grep "progress " <input >expect &&
-        test_cmp expect actual'
+test_expect_success 'Q: verify pack' '
+       verify_packs
+'
 
-###
-### series P (gitlinks)
-###
+test_expect_success 'Q: verify first commit' '
+       commit1=$(git rev-parse notes-test~2) &&
+       commit2=$(git rev-parse notes-test^) &&
+       commit3=$(git rev-parse notes-test) &&
 
-cat >input <<INPUT_END
-blob
-mark :1
-data 10
-test file
-
-reset refs/heads/sub
-commit refs/heads/sub
-mark :2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 12
-sub_initial
-M 100644 :1 file
-
-blob
-mark :3
-data <<DATAEND
-[submodule "sub"]
-       path = sub
-       url = "`pwd`/sub"
-DATAEND
-
-commit refs/heads/subuse1
-mark :4
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 8
-initial
-from refs/heads/master
-M 100644 :3 .gitmodules
-M 160000 :2 sub
-
-blob
-mark :5
-data 20
-test file
-more data
-
-commit refs/heads/sub
-mark :6
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 11
-sub_second
-from :2
-M 100644 :5 file
-
-commit refs/heads/subuse1
-mark :7
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 7
-second
-from :4
-M 160000 :6 sub
-
-INPUT_END
-
-test_expect_success \
-       'P: superproject & submodule mix' \
-       'git fast-import <input &&
-        git checkout subuse1 &&
-        rm -rf sub && mkdir sub && (cd sub &&
-        git init &&
-        git fetch --update-head-ok .. refs/heads/sub:refs/heads/master &&
-        git checkout master) &&
-        git submodule init &&
-        git submodule update'
-
-SUBLAST=$(git rev-parse --verify sub)
-SUBPREV=$(git rev-parse --verify sub^)
-
-cat >input <<INPUT_END
-blob
-mark :1
-data <<DATAEND
-[submodule "sub"]
-       path = sub
-       url = "`pwd`/sub"
-DATAEND
-
-commit refs/heads/subuse2
-mark :2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 8
-initial
-from refs/heads/master
-M 100644 :1 .gitmodules
-M 160000 $SUBPREV sub
-
-commit refs/heads/subuse2
-mark :3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data 7
-second
-from :2
-M 160000 $SUBLAST sub
-
-INPUT_END
-
-test_expect_success \
-       'P: verbatim SHA gitlinks' \
-       'git branch -D sub &&
-        git gc && git prune &&
-        git fast-import <input &&
-        test $(git rev-parse --verify subuse2) = $(git rev-parse --verify subuse1)'
-
-test_tick
-cat >input <<INPUT_END
-commit refs/heads/subuse3
-mark :1
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
-
-from refs/heads/subuse2
-M 160000 inline sub
-data <<DATA
-$SUBPREV
-DATA
-
-INPUT_END
+       cat >expect <<-EOF &&
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-test_expect_success 'P: fail on inline gitlink' '
-    test_must_fail git fast-import <input'
+       first (:3)
+       EOF
+       git cat-file commit notes-test~2 | sed 1d >actual &&
+       test_cmp expect actual
+'
 
-test_tick
-cat >input <<INPUT_END
-blob
-mark :1
-data <<DATA
-$SUBPREV
-DATA
+test_expect_success 'Q: verify second commit' '
+       cat >expect <<-EOF &&
+       parent $commit1
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-commit refs/heads/subuse3
-mark :2
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-corrupt
-COMMIT
+       second (:5)
+       EOF
+       git cat-file commit notes-test^ | sed 1d >actual &&
+       test_cmp expect actual
+'
 
-from refs/heads/subuse2
-M 160000 :1 sub
+test_expect_success 'Q: verify third commit' '
+       cat >expect <<-EOF &&
+       parent $commit2
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-INPUT_END
+       third (:6)
+       EOF
+       git cat-file commit notes-test | sed 1d >actual &&
+       test_cmp expect actual
+'
 
-test_expect_success 'P: fail on blob mark in gitlink' '
-    test_must_fail git fast-import <input'
+test_expect_success 'Q: verify first notes commit' '
+       cat >expect <<-EOF &&
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
 
-###
-### series Q (notes)
-###
+       notes (:9)
+       EOF
+       git cat-file commit refs/notes/foobar~2 | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first notes tree' '
+       cat >expect.unsorted <<-EOF &&
+       100644 blob $commit1
+       100644 blob $commit2
+       100644 blob $commit3
+       EOF
+       cat expect.unsorted | sort >expect &&
+       git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]*  / /" >actual &&
+       test_cmp expect actual
+'
 
-note1_data="The first note for the first commit"
-note2_data="The first note for the second commit"
-note3_data="The first note for the third commit"
-note1b_data="The second note for the first commit"
-note1c_data="The third note for the first commit"
-note2b_data="The second note for the second commit"
-
-test_tick
-cat >input <<INPUT_END
-blob
-mark :2
-data <<EOF
-$file2_data
-EOF
-
-commit refs/heads/notes-test
-mark :3
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-first (:3)
-COMMIT
-
-M 644 :2 file2
-
-blob
-mark :4
-data $file4_len
-$file4_data
-commit refs/heads/notes-test
-mark :5
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-second (:5)
-COMMIT
-
-M 644 :4 file4
-
-commit refs/heads/notes-test
-mark :6
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-third (:6)
-COMMIT
-
-M 644 inline file5
-data <<EOF
-$file5_data
-EOF
-
-M 755 inline file6
-data <<EOF
-$file6_data
-EOF
-
-blob
-mark :7
-data <<EOF
-$note1_data
-EOF
-
-blob
-mark :8
-data <<EOF
-$note2_data
-EOF
-
-commit refs/notes/foobar
-mark :9
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-notes (:9)
-COMMIT
-
-N :7 :3
-N :8 :5
-N inline :6
-data <<EOF
-$note3_data
-EOF
-
-commit refs/notes/foobar
-mark :10
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-notes (:10)
-COMMIT
-
-N inline :3
-data <<EOF
-$note1b_data
-EOF
-
-commit refs/notes/foobar2
-mark :11
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-notes (:11)
-COMMIT
-
-N inline :3
-data <<EOF
-$note1c_data
-EOF
-
-commit refs/notes/foobar
-mark :12
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-notes (:12)
-COMMIT
-
-deleteall
-N inline :5
-data <<EOF
-$note2b_data
-EOF
-
-INPUT_END
-
-test_expect_success \
-       'Q: commit notes' \
-       'git fast-import <input &&
-        git whatchanged notes-test'
+test_expect_success 'Q: verify first note for first commit' '
+       echo "$note1_data" >expect &&
+       git cat-file blob refs/notes/foobar~2:$commit1 >actual &&
+       test_cmp expect actual
+'
 
-test_expect_success 'Q: verify pack' '
-       verify_packs
+test_expect_success 'Q: verify first note for second commit' '
+       echo "$note2_data" >expect &&
+       git cat-file blob refs/notes/foobar~2:$commit2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for third commit' '
+       echo "$note3_data" >expect &&
+       git cat-file blob refs/notes/foobar~2:$commit3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second notes commit' '
+       cat >expect <<-EOF &&
+       parent `git rev-parse --verify refs/notes/foobar~2`
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+       notes (:10)
+       EOF
+       git cat-file commit refs/notes/foobar^ | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second notes tree' '
+       cat >expect.unsorted <<-EOF &&
+       100644 blob $commit1
+       100644 blob $commit2
+       100644 blob $commit3
+       EOF
+       cat expect.unsorted | sort >expect &&
+       git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second note for first commit' '
+       echo "$note1b_data" >expect &&
+       git cat-file blob refs/notes/foobar^:$commit1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for second commit' '
+       echo "$note2_data" >expect &&
+       git cat-file blob refs/notes/foobar^:$commit2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify first note for third commit' '
+       echo "$note3_data" >expect &&
+       git cat-file blob refs/notes/foobar^:$commit3 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify third notes commit' '
+       cat >expect <<-EOF &&
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+       notes (:11)
+       EOF
+       git cat-file commit refs/notes/foobar2 | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify third notes tree' '
+       cat >expect.unsorted <<-EOF &&
+       100644 blob $commit1
+       EOF
+       cat expect.unsorted | sort >expect &&
+       git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
+       test_cmp expect actual
 '
 
-commit1=$(git rev-parse notes-test~2)
-commit2=$(git rev-parse notes-test^)
-commit3=$(git rev-parse notes-test)
-
-cat >expect <<EOF
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-first (:3)
-EOF
-test_expect_success \
-       'Q: verify first commit' \
-       'git cat-file commit notes-test~2 | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect <<EOF
-parent $commit1
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-second (:5)
-EOF
-test_expect_success \
-       'Q: verify second commit' \
-       'git cat-file commit notes-test^ | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect <<EOF
-parent $commit2
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-third (:6)
-EOF
-test_expect_success \
-       'Q: verify third commit' \
-       'git cat-file commit notes-test | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect <<EOF
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-notes (:9)
-EOF
-test_expect_success \
-       'Q: verify first notes commit' \
-       'git cat-file commit refs/notes/foobar~2 | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect.unsorted <<EOF
-100644 blob $commit1
-100644 blob $commit2
-100644 blob $commit3
-EOF
-cat expect.unsorted | sort >expect
-test_expect_success \
-       'Q: verify first notes tree' \
-       'git cat-file -p refs/notes/foobar~2^{tree} | sed "s/ [0-9a-f]* / /" >actual &&
-        test_cmp expect actual'
-
-echo "$note1_data" >expect
-test_expect_success \
-       'Q: verify first note for first commit' \
-       'git cat-file blob refs/notes/foobar~2:$commit1 >actual && test_cmp expect actual'
-
-echo "$note2_data" >expect
-test_expect_success \
-       'Q: verify first note for second commit' \
-       'git cat-file blob refs/notes/foobar~2:$commit2 >actual && test_cmp expect actual'
-
-echo "$note3_data" >expect
-test_expect_success \
-       'Q: verify first note for third commit' \
-       'git cat-file blob refs/notes/foobar~2:$commit3 >actual && test_cmp expect actual'
-
-cat >expect <<EOF
-parent `git rev-parse --verify refs/notes/foobar~2`
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-notes (:10)
-EOF
-test_expect_success \
-       'Q: verify second notes commit' \
-       'git cat-file commit refs/notes/foobar^ | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect.unsorted <<EOF
-100644 blob $commit1
-100644 blob $commit2
-100644 blob $commit3
-EOF
-cat expect.unsorted | sort >expect
-test_expect_success \
-       'Q: verify second notes tree' \
-       'git cat-file -p refs/notes/foobar^^{tree} | sed "s/ [0-9a-f]*  / /" >actual &&
-        test_cmp expect actual'
-
-echo "$note1b_data" >expect
-test_expect_success \
-       'Q: verify second note for first commit' \
-       'git cat-file blob refs/notes/foobar^:$commit1 >actual && test_cmp expect actual'
-
-echo "$note2_data" >expect
-test_expect_success \
-       'Q: verify first note for second commit' \
-       'git cat-file blob refs/notes/foobar^:$commit2 >actual && test_cmp expect actual'
-
-echo "$note3_data" >expect
-test_expect_success \
-       'Q: verify first note for third commit' \
-       'git cat-file blob refs/notes/foobar^:$commit3 >actual && test_cmp expect actual'
-
-cat >expect <<EOF
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-notes (:11)
-EOF
-test_expect_success \
-       'Q: verify third notes commit' \
-       'git cat-file commit refs/notes/foobar2 | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect.unsorted <<EOF
-100644 blob $commit1
-EOF
-cat expect.unsorted | sort >expect
-test_expect_success \
-       'Q: verify third notes tree' \
-       'git cat-file -p refs/notes/foobar2^{tree} | sed "s/ [0-9a-f]*  / /" >actual &&
-        test_cmp expect actual'
-
-echo "$note1c_data" >expect
-test_expect_success \
-       'Q: verify third note for first commit' \
-       'git cat-file blob refs/notes/foobar2:$commit1 >actual && test_cmp expect actual'
-
-cat >expect <<EOF
-parent `git rev-parse --verify refs/notes/foobar^`
-author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-
-notes (:12)
-EOF
-test_expect_success \
-       'Q: verify fourth notes commit' \
-       'git cat-file commit refs/notes/foobar | sed 1d >actual &&
-       test_cmp expect actual'
-
-cat >expect.unsorted <<EOF
-100644 blob $commit2
-EOF
-cat expect.unsorted | sort >expect
-test_expect_success \
-       'Q: verify fourth notes tree' \
-       'git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*   / /" >actual &&
-        test_cmp expect actual'
-
-echo "$note2b_data" >expect
-test_expect_success \
-       'Q: verify second note for second commit' \
-       'git cat-file blob refs/notes/foobar:$commit2 >actual && test_cmp expect actual'
-
-cat >input <<EOF
-reset refs/heads/Q0
-
-commit refs/heads/note-Q0
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-Note for an empty branch.
-COMMIT
-
-N inline refs/heads/Q0
-data <<NOTE
-some note
-NOTE
-EOF
-test_expect_success \
-       'Q: deny note on empty branch' \
-       'test_must_fail git fast-import <input'
+test_expect_success 'Q: verify third note for first commit' '
+       echo "$note1c_data" >expect &&
+       git cat-file blob refs/notes/foobar2:$commit1 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify fourth notes commit' '
+       cat >expect <<-EOF &&
+       parent `git rev-parse --verify refs/notes/foobar^`
+       author $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+
+       notes (:12)
+       EOF
+       git cat-file commit refs/notes/foobar | sed 1d >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify fourth notes tree' '
+       cat >expect.unsorted <<-EOF &&
+       100644 blob $commit2
+       EOF
+       cat expect.unsorted | sort >expect &&
+       git cat-file -p refs/notes/foobar^{tree} | sed "s/ [0-9a-f]*    / /" >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: verify second note for second commit' '
+       echo "$note2b_data" >expect &&
+       git cat-file blob refs/notes/foobar:$commit2 >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success 'Q: deny note on empty branch' '
+       cat >input <<-EOF &&
+       reset refs/heads/Q0
+
+       commit refs/heads/note-Q0
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       Note for an empty branch.
+       COMMIT
+
+       N inline refs/heads/Q0
+       data <<NOTE
+       some note
+       NOTE
+       EOF
+       test_must_fail git fast-import <input
+'
 ###
 ### series R (feature and option)
 ###
 
-cat >input <<EOF
-feature no-such-feature-exists
-EOF
-
 test_expect_success 'R: abort on unsupported feature' '
+       cat >input <<-EOF &&
+       feature no-such-feature-exists
+       EOF
+
        test_must_fail git fast-import <input
 '
 
-cat >input <<EOF
-feature date-format=now
-EOF
-
 test_expect_success 'R: supported feature is accepted' '
+       cat >input <<-EOF &&
+       feature date-format=now
+       EOF
+
        git fast-import <input
 '
 
-cat >input << EOF
-blob
-data 3
-hi
-feature date-format=now
-EOF
-
 test_expect_success 'R: abort on receiving feature after data command' '
+       cat >input <<-EOF &&
+       blob
+       data 3
+       hi
+       feature date-format=now
+       EOF
+
        test_must_fail git fast-import <input
 '
 
-cat >input << EOF
-feature import-marks=git.marks
-feature import-marks=git2.marks
-EOF
-
 test_expect_success 'R: only one import-marks feature allowed per stream' '
+       cat >input <<-EOF &&
+       feature import-marks=git.marks
+       feature import-marks=git2.marks
+       EOF
+
        test_must_fail git fast-import <input
 '
 
-cat >input << EOF
-feature export-marks=git.marks
-blob
-mark :1
-data 3
-hi
+test_expect_success 'R: export-marks feature results in a marks file being created' '
+       cat >input <<-EOF &&
+       feature export-marks=git.marks
+       blob
+       mark :1
+       data 3
+       hi
 
-EOF
+       EOF
 
-test_expect_success \
-    'R: export-marks feature results in a marks file being created' \
-    'cat input | git fast-import &&
-    grep :1 git.marks'
+       cat input | git fast-import &&
+       grep :1 git.marks
+'
 
-test_expect_success \
-    'R: export-marks options can be overridden by commandline options' \
-    'cat input | git fast-import --export-marks=other.marks &&
-    grep :1 other.marks'
+test_expect_success 'R: export-marks options can be overridden by commandline options' '
+       cat input | git fast-import --export-marks=other.marks &&
+       grep :1 other.marks
+'
 
 test_expect_success 'R: catch typo in marks file name' '
        test_must_fail git fast-import --import-marks=nonexistent.marks </dev/null &&
@@ -2234,62 +2248,62 @@ test_expect_success 'R: feature import-marks-if-exists' '
        test_cmp expect io.marks
 '
 
-cat >input << EOF
-feature import-marks=marks.out
-feature export-marks=marks.new
-EOF
-
-test_expect_success \
-    'R: import to output marks works without any content' \
-    'cat input | git fast-import &&
-    test_cmp marks.out marks.new'
+test_expect_success 'R: import to output marks works without any content' '
+       cat >input <<-EOF &&
+       feature import-marks=marks.out
+       feature export-marks=marks.new
+       EOF
 
-cat >input <<EOF
-feature import-marks=nonexistent.marks
-feature export-marks=marks.new
-EOF
+       cat input | git fast-import &&
+       test_cmp marks.out marks.new
+'
 
-test_expect_success \
-    'R: import marks prefers commandline marks file over the stream' \
-    'cat input | git fast-import --import-marks=marks.out &&
-    test_cmp marks.out marks.new'
+test_expect_success 'R: import marks prefers commandline marks file over the stream' '
+       cat >input <<-EOF &&
+       feature import-marks=nonexistent.marks
+       feature export-marks=marks.new
+       EOF
 
+       cat input | git fast-import --import-marks=marks.out &&
+       test_cmp marks.out marks.new
+'
 
-cat >input <<EOF
-feature import-marks=nonexistent.marks
-feature export-marks=combined.marks
-EOF
 
 test_expect_success 'R: multiple --import-marks= should be honoured' '
-    head -n2 marks.out > one.marks &&
-    tail -n +3 marks.out > two.marks &&
-    git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
-    test_cmp marks.out combined.marks
-'
+       cat >input <<-EOF &&
+       feature import-marks=nonexistent.marks
+       feature export-marks=combined.marks
+       EOF
 
-cat >input <<EOF
-feature relative-marks
-feature import-marks=relative.in
-feature export-marks=relative.out
-EOF
+       head -n2 marks.out > one.marks &&
+       tail -n +3 marks.out > two.marks &&
+       git fast-import --import-marks=one.marks --import-marks=two.marks <input &&
+       test_cmp marks.out combined.marks
+'
 
 test_expect_success 'R: feature relative-marks should be honoured' '
-    mkdir -p .git/info/fast-import/ &&
-    cp marks.new .git/info/fast-import/relative.in &&
-    git fast-import <input &&
-    test_cmp marks.new .git/info/fast-import/relative.out
-'
+       cat >input <<-EOF &&
+       feature relative-marks
+       feature import-marks=relative.in
+       feature export-marks=relative.out
+       EOF
 
-cat >input <<EOF
-feature relative-marks
-feature import-marks=relative.in
-feature no-relative-marks
-feature export-marks=non-relative.out
-EOF
+       mkdir -p .git/info/fast-import/ &&
+       cp marks.new .git/info/fast-import/relative.in &&
+       git fast-import <input &&
+       test_cmp marks.new .git/info/fast-import/relative.out
+'
 
 test_expect_success 'R: feature no-relative-marks should be honoured' '
-    git fast-import <input &&
-    test_cmp marks.new non-relative.out
+       cat >input <<-EOF &&
+       feature relative-marks
+       feature import-marks=relative.in
+       feature no-relative-marks
+       feature export-marks=non-relative.out
+       EOF
+
+       git fast-import <input &&
+       test_cmp marks.new non-relative.out
 '
 
 test_expect_success 'R: feature ls supported' '
@@ -2330,12 +2344,12 @@ test_expect_success !MINGW 'R: in-stream cat-blob-fd not respected' '
        cat-blob $blob
        EOF
        test_cmp expect actual.3 &&
-       test_cmp empty actual.1 &&
+       test_must_be_empty actual.1 &&
        git fast-import 3>actual.3 >actual.1 <<-EOF &&
        option cat-blob-fd=3
        cat-blob $blob
        EOF
-       test_cmp empty actual.3 &&
+       test_must_be_empty actual.3 &&
        test_cmp expect actual.1
 '
 
@@ -2549,17 +2563,17 @@ test_expect_success PIPE 'R: print staged blob within commit' '
        test_cmp expect actual
 '
 
-cat >input << EOF
-option git quiet
-blob
-data 3
-hi
+test_expect_success 'R: quiet option results in no stats being output' '
+       cat >input <<-EOF &&
+       option git quiet
+       blob
+       data 3
+       hi
 
-EOF
+       EOF
 
-test_expect_success 'R: quiet option results in no stats being output' '
-    cat input | git fast-import 2> output &&
-    test_cmp empty output
+       cat input | git fast-import 2> output &&
+       test_must_be_empty output
 '
 
 test_expect_success 'R: feature done means terminating "done" is mandatory' '
@@ -2604,16 +2618,16 @@ test_expect_success 'R: terminating "done" within commit' '
        test_cmp expect actual
 '
 
-cat >input <<EOF
-option git non-existing-option
-EOF
-
 test_expect_success 'R: die on unknown option' '
-    test_must_fail git fast-import <input
+       cat >input <<-EOF &&
+       option git non-existing-option
+       EOF
+
+       test_must_fail git fast-import <input
 '
 
 test_expect_success 'R: unknown commandline options are rejected' '\
-    test_must_fail git fast-import --non-existing-option < /dev/null
+       test_must_fail git fast-import --non-existing-option < /dev/null
 '
 
 test_expect_success 'R: die on invalid option argument' '
@@ -2624,41 +2638,41 @@ test_expect_success 'R: die on invalid option argument' '
        test_must_fail git fast-import --depth="5 elephants" </dev/null
 '
 
-cat >input <<EOF
-option non-existing-vcs non-existing-option
-EOF
-
 test_expect_success 'R: ignore non-git options' '
-    git fast-import <input
+       cat >input <<-EOF &&
+       option non-existing-vcs non-existing-option
+       EOF
+
+       git fast-import <input
 '
 
 ##
 ## R: very large blobs
 ##
-blobsize=$((2*1024*1024 + 53))
-test-genrandom bar $blobsize >expect
-cat >input <<INPUT_END
-commit refs/heads/big-file
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-R - big file
-COMMIT
-
-M 644 inline big1
-data $blobsize
-INPUT_END
-cat expect >>input
-cat >>input <<INPUT_END
-M 644 inline big2
-data $blobsize
-INPUT_END
-cat expect >>input
-echo >>input
-
-test_expect_success \
-       'R: blob bigger than threshold' \
-       'test_create_repo R &&
-        git --git-dir=R/.git fast-import --big-file-threshold=1 <input'
+test_expect_success 'R: blob bigger than threshold' '
+       blobsize=$((2*1024*1024 + 53)) &&
+       test-genrandom bar $blobsize >expect &&
+       cat >input <<-INPUT_END &&
+       commit refs/heads/big-file
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       R - big file
+       COMMIT
+
+       M 644 inline big1
+       data $blobsize
+       INPUT_END
+       cat expect >>input &&
+       cat >>input <<-INPUT_END &&
+       M 644 inline big2
+       data $blobsize
+       INPUT_END
+       cat expect >>input &&
+       echo >>input &&
+
+       test_create_repo R &&
+       git --git-dir=R/.git fast-import --big-file-threshold=1 <input
+'
 
 test_expect_success 'R: verify created pack' '
        (
@@ -2667,17 +2681,18 @@ test_expect_success 'R: verify created pack' '
        )
 '
 
-test_expect_success \
-       'R: verify written objects' \
-       'git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
-        test_cmp_bin expect actual &&
-        a=$(git --git-dir=R/.git rev-parse big-file:big1) &&
-        b=$(git --git-dir=R/.git rev-parse big-file:big2) &&
-        test $a = $b'
-test_expect_success \
-       'R: blob appears only once' \
-       'n=$(grep $a verify | wc -l) &&
-        test 1 = $n'
+test_expect_success 'R: verify written objects' '
+       git --git-dir=R/.git cat-file blob big-file:big1 >actual &&
+       test_cmp_bin expect actual &&
+       a=$(git --git-dir=R/.git rev-parse big-file:big1) &&
+       b=$(git --git-dir=R/.git rev-parse big-file:big2) &&
+       test $a = $b
+'
+
+test_expect_success 'R: blob appears only once' '
+       n=$(grep $a verify | wc -l) &&
+       test 1 = $n
+'
 
 ###
 ### series S
@@ -2710,46 +2725,46 @@ test_expect_success \
 #
 #   Invalid dataref ..
 #
-test_tick
-
-cat >input <<INPUT_END
-commit refs/heads/S
-mark :301
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-commit 1
-COMMIT
-M 100644 inline hello.c
-data <<BLOB
-blob 1
-BLOB
-
-commit refs/heads/S
-mark :302
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-commit 2
-COMMIT
-from :301
-M 100644 inline hello.c
-data <<BLOB
-blob 2
-BLOB
-
-blob
-mark :403
-data <<BLOB
-blob 3
-BLOB
-
-blob
-mark :202
-data <<BLOB
-note 2
-BLOB
-INPUT_END
-
 test_expect_success 'S: initialize for S tests' '
+       test_tick &&
+
+       cat >input <<-INPUT_END &&
+       commit refs/heads/S
+       mark :301
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit 1
+       COMMIT
+       M 100644 inline hello.c
+       data <<BLOB
+       blob 1
+       BLOB
+
+       commit refs/heads/S
+       mark :302
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       commit 2
+       COMMIT
+       from :301
+       M 100644 inline hello.c
+       data <<BLOB
+       blob 2
+       BLOB
+
+       blob
+       mark :403
+       data <<BLOB
+       blob 3
+       BLOB
+
+       blob
+       mark :202
+       data <<BLOB
+       note 2
+       BLOB
+       INPUT_END
+
        git fast-import --export-marks=marks <input
 '
 
@@ -3001,103 +3016,103 @@ test_expect_success 'T: empty reset doesnt delete branch' '
 ### series U (filedelete)
 ###
 
-cat >input <<INPUT_END
-commit refs/heads/U
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-test setup
-COMMIT
-M 100644 inline hello.c
-data <<BLOB
-blob 1
-BLOB
-M 100644 inline good/night.txt
-data <<BLOB
-sleep well
-BLOB
-M 100644 inline good/bye.txt
-data <<BLOB
-au revoir
-BLOB
-
-INPUT_END
-
 test_expect_success 'U: initialize for U tests' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/U
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       test setup
+       COMMIT
+       M 100644 inline hello.c
+       data <<BLOB
+       blob 1
+       BLOB
+       M 100644 inline good/night.txt
+       data <<BLOB
+       sleep well
+       BLOB
+       M 100644 inline good/bye.txt
+       data <<BLOB
+       au revoir
+       BLOB
+
+       INPUT_END
+
        git fast-import <input
 '
 
-cat >input <<INPUT_END
-commit refs/heads/U
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-delete good/night.txt
-COMMIT
-from refs/heads/U^0
-D good/night.txt
+test_expect_success 'U: filedelete file succeeds' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/U
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       delete good/night.txt
+       COMMIT
+       from refs/heads/U^0
+       D good/night.txt
 
-INPUT_END
+       INPUT_END
 
-test_expect_success 'U: filedelete file succeeds' '
        git fast-import <input
 '
 
-cat >expect <<EOF
-:100644 000000 2907ebb4bf85d91bf0716bb3bd8a68ef48d6da76 0000000000000000000000000000000000000000 D     good/night.txt
-EOF
+test_expect_success 'U: validate file delete result' '
+       cat >expect <<-EOF &&
+       :100644 000000 2907ebb4bf85d91bf0716bb3bd8a68ef48d6da76 0000000000000000000000000000000000000000 D      good/night.txt
+       EOF
 
-git diff-tree -M -r U^1 U >actual
+       git diff-tree -M -r U^1 U >actual &&
 
-test_expect_success 'U: validate file delete result' '
        compare_diff_raw expect actual
 '
 
-cat >input <<INPUT_END
-commit refs/heads/U
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-delete good dir
-COMMIT
-from refs/heads/U^0
-D good
+test_expect_success 'U: filedelete directory succeeds' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/U
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       delete good dir
+       COMMIT
+       from refs/heads/U^0
+       D good
 
-INPUT_END
+       INPUT_END
 
-test_expect_success 'U: filedelete directory succeeds' '
        git fast-import <input
 '
 
-cat >expect <<EOF
-:100644 000000 69cb75792f55123d8389c156b0b41c2ff00ed507 0000000000000000000000000000000000000000 D     good/bye.txt
-EOF
+test_expect_success 'U: validate directory delete result' '
+       cat >expect <<-EOF &&
+       :100644 000000 69cb75792f55123d8389c156b0b41c2ff00ed507 0000000000000000000000000000000000000000 D      good/bye.txt
+       EOF
 
-git diff-tree -M -r U^1 U >actual
+       git diff-tree -M -r U^1 U >actual &&
 
-test_expect_success 'U: validate directory delete result' '
        compare_diff_raw expect actual
 '
 
-cat >input <<INPUT_END
-commit refs/heads/U
-committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
-data <<COMMIT
-must succeed
-COMMIT
-from refs/heads/U^0
-D ""
+test_expect_success 'U: filedelete root succeeds' '
+       cat >input <<-INPUT_END &&
+       commit refs/heads/U
+       committer $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> $GIT_COMMITTER_DATE
+       data <<COMMIT
+       must succeed
+       COMMIT
+       from refs/heads/U^0
+       D ""
 
-INPUT_END
+       INPUT_END
 
-test_expect_success 'U: filedelete root succeeds' '
-    git fast-import <input
+       git fast-import <input
 '
 
-cat >expect <<EOF
-:100644 000000 c18147dc648481eeb65dc5e66628429a64843327 0000000000000000000000000000000000000000 D     hello.c
-EOF
+test_expect_success 'U: validate root delete result' '
+       cat >expect <<-EOF &&
+       :100644 000000 c18147dc648481eeb65dc5e66628429a64843327 0000000000000000000000000000000000000000 D      hello.c
+       EOF
 
-git diff-tree -M -r U^1 U >actual
+       git diff-tree -M -r U^1 U >actual &&
 
-test_expect_success 'U: validate root delete result' '
        compare_diff_raw expect actual
 '
 
index 90d41ed954c829bfb3735f3cea9db336f74adb81..0730f18d0f83f4145c5a0dbfde784d71bdc57a1d 100755 (executable)
@@ -241,6 +241,22 @@ test_expect_success 'unresolvable host in P4PORT should display error' '
        )
 '
 
+test_expect_success 'submit from detached head' '
+       test_when_finished cleanup_git &&
+       git p4 clone --dest="$git" //depot &&
+       (
+               cd "$git" &&
+               git checkout p4/master &&
+               >detached_head_test &&
+               git add detached_head_test &&
+               git commit -m "add detached_head" &&
+               git config git-p4.skipSubmitEdit true &&
+               git p4 submit &&
+               git p4 rebase &&
+               git log p4/master | grep detached_head
+       )
+'
+
 test_expect_success 'kill p4d' '
        kill_p4d
 '
index 1f74a88385e827f7162256fafc22db5de3d41bf8..593152817dadaf1804f4a851c51b8bf1cbf52db9 100755 (executable)
@@ -389,7 +389,7 @@ test_expect_success 'description with Jobs section and bogus following text' '
        (
                cd "$cli" &&
                p4 revert desc6 &&
-               rm desc6
+               rm -f desc6
        )
 '
 
diff --git a/t/t9826-git-p4-keep-empty-commits.sh b/t/t9826-git-p4-keep-empty-commits.sh
new file mode 100755 (executable)
index 0000000..be12960
--- /dev/null
@@ -0,0 +1,134 @@
+#!/bin/sh
+
+test_description='Clone repositories and keep empty commits'
+
+. ./lib-git-p4.sh
+
+test_expect_success 'start p4d' '
+       start_p4d
+'
+
+test_expect_success 'Create a repo' '
+       client_view "//depot/... //client/..." &&
+       (
+               cd "$cli" &&
+
+               mkdir -p subdir &&
+
+               >subdir/file1.txt &&
+               p4 add subdir/file1.txt &&
+               p4 submit -d "Add file 1" &&
+
+               >file2.txt &&
+               p4 add file2.txt &&
+               p4 submit -d "Add file 2" &&
+
+               >subdir/file3.txt &&
+               p4 add subdir/file3.txt &&
+               p4 submit -d "Add file 3" &&
+
+               >file4.txt &&
+               p4 add file4.txt &&
+               p4 submit -d "Add file 4" &&
+
+               p4 delete subdir/file3.txt &&
+               p4 submit -d "Remove file 3" &&
+
+               p4 delete file4.txt &&
+               p4 submit -d "Remove file 4"
+       )
+'
+
+test_expect_success 'Clone repo root path with all history' '
+       client_view "//depot/... //client/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 4
+[git-p4: depot-paths = "//depot/": change = 6]
+
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 4
+[git-p4: depot-paths = "//depot/": change = 4]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 2
+[git-p4: depot-paths = "//depot/": change = 2]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'Clone repo subdir with all history but keep empty commits' '
+       client_view "//depot/subdir/... //client/subdir/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git config git-p4.keepEmptyCommits true &&
+               git p4 clone --use-client-spec --destination="$git" //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 4
+[git-p4: depot-paths = "//depot/": change = 6]
+
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 4
+[git-p4: depot-paths = "//depot/": change = 4]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 2
+[git-p4: depot-paths = "//depot/": change = 2]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'Clone repo subdir with all history' '
+       client_view "//depot/subdir/... //client/subdir/..." &&
+       test_when_finished cleanup_git &&
+       (
+               cd "$git" &&
+               git init . &&
+               git p4 clone --use-client-spec --destination="$git" --verbose //depot@all &&
+               cat >expect <<-\EOF &&
+Remove file 3
+[git-p4: depot-paths = "//depot/": change = 5]
+
+Add file 3
+[git-p4: depot-paths = "//depot/": change = 3]
+
+Add file 1
+[git-p4: depot-paths = "//depot/": change = 1]
+
+               EOF
+               git log --format=%B >actual &&
+               test_cmp expect actual
+       )
+'
+
+test_expect_success 'kill p4d' '
+       kill_p4d
+'
+
+test_done
index 6b68777b9804a86a9d6e0bef63b5073e895efcfe..af82049f82e89c3275c0f1e6c5da9d7f9c7041f5 100755 (executable)
@@ -273,11 +273,36 @@ test_expect_success 'prompt - dirty status indicator - dirty index and worktree'
        test_cmp expected "$actual"
 '
 
-test_expect_success 'prompt - dirty status indicator - before root commit' '
-       printf " (master #)" >expected &&
+test_expect_success 'prompt - dirty status indicator - orphan branch - clean' '
+       printf " (orphan #)" >expected &&
+       test_when_finished "git checkout master" &&
+       git checkout --orphan orphan &&
+       git reset --hard &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               __git_ps1 >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index' '
+       printf " (orphan +)" >expected &&
+       test_when_finished "git checkout master" &&
+       git checkout --orphan orphan &&
+       (
+               GIT_PS1_SHOWDIRTYSTATE=y &&
+               __git_ps1 >"$actual"
+       ) &&
+       test_cmp expected "$actual"
+'
+
+test_expect_success 'prompt - dirty status indicator - orphan branch - dirty index and worktree' '
+       printf " (orphan *+)" >expected &&
+       test_when_finished "git checkout master" &&
+       git checkout --orphan orphan &&
+       >file &&
        (
                GIT_PS1_SHOWDIRTYSTATE=y &&
-               cd otherrepo &&
                __git_ps1 >"$actual"
        ) &&
        test_cmp expected "$actual"
index 73e37a1f6c23d00371dbd3f10aee1648e8b947bc..c64e5a50258d1b3d3655e2dfcf8f391977c5e758 100644 (file)
@@ -569,6 +569,21 @@ test_line_count () {
        fi
 }
 
+# Returns success if a comma separated string of keywords ($1) contains a
+# given keyword ($2).
+# Examples:
+# `list_contains "foo,bar" bar` returns 0
+# `list_contains "foo" bar` returns 1
+
+list_contains () {
+       case ",$1," in
+       *,$2,*)
+               return 0
+               ;;
+       esac
+       return 1
+}
+
 # This is not among top-level (test_expect_success | test_expect_failure)
 # but is a prefix that can be used in the test script, like:
 #
@@ -582,18 +597,34 @@ test_line_count () {
 # the failure could be due to a segv.  We want a controlled failure.
 
 test_must_fail () {
+       case "$1" in
+       ok=*)
+               _test_ok=${1#ok=}
+               shift
+               ;;
+       *)
+               _test_ok=
+               ;;
+       esac
        "$@"
        exit_code=$?
-       if test $exit_code = 0; then
+       if test $exit_code -eq 0 && ! list_contains "$_test_ok" success
+       then
                echo >&2 "test_must_fail: command succeeded: $*"
                return 1
-       elif test $exit_code -gt 129 && test $exit_code -le 192; then
+       elif test $exit_code -eq 141 && list_contains "$_test_ok" sigpipe
+       then
+               return 0
+       elif test $exit_code -gt 129 && test $exit_code -le 192
+       then
                echo >&2 "test_must_fail: died by signal: $*"
                return 1
-       elif test $exit_code = 127; then
+       elif test $exit_code -eq 127
+       then
                echo >&2 "test_must_fail: command not found: $*"
                return 1
-       elif test $exit_code = 126; then
+       elif test $exit_code -eq 126
+       then
                echo >&2 "test_must_fail: valgrind error: $*"
                return 1
        fi
@@ -612,16 +643,7 @@ test_must_fail () {
 # because we want to notice if it fails due to segv.
 
 test_might_fail () {
-       "$@"
-       exit_code=$?
-       if test $exit_code -gt 129 && test $exit_code -le 192; then
-               echo >&2 "test_might_fail: died by signal: $*"
-               return 1
-       elif test $exit_code = 127; then
-               echo >&2 "test_might_fail: command not found: $*"
-               return 1
-       fi
-       return 0
+       test_must_fail ok=success "$@"
 }
 
 # Similar to test_must_fail and test_might_fail, but check that a
diff --git a/tag.c b/tag.c
index 5b2a06d92b75c7aea3f8cfb1cadb33d9da27f3c6..d72f742af9a4a6a76c8b0f6fd314473dea244b2a 100644 (file)
--- a/tag.c
+++ b/tag.c
@@ -10,7 +10,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 {
        while (o && o->type == OBJ_TAG)
                if (((struct tag *)o)->tagged)
-                       o = parse_object(((struct tag *)o)->tagged->sha1);
+                       o = parse_object(((struct tag *)o)->tagged->oid.hash);
                else
                        o = NULL;
        if (!o && warn) {
@@ -24,7 +24,7 @@ struct object *deref_tag(struct object *o, const char *warn, int warnlen)
 struct object *deref_tag_noverify(struct object *o)
 {
        while (o && o->type == OBJ_TAG) {
-               o = parse_object(o->sha1);
+               o = parse_object(o->oid.hash);
                if (o && o->type == OBJ_TAG && ((struct tag *)o)->tagged)
                        o = ((struct tag *)o)->tagged;
                else
@@ -127,14 +127,14 @@ int parse_tag(struct tag *item)
 
        if (item->object.parsed)
                return 0;
-       data = read_sha1_file(item->object.sha1, &type, &size);
+       data = read_sha1_file(item->object.oid.hash, &type, &size);
        if (!data)
                return error("Could not read %s",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        if (type != OBJ_TAG) {
                free(data);
                return error("Object %s not a tag",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        }
        ret = parse_tag_buffer(item, data, size);
        free(data);
index 2ef725e5ff94c4db59fdc4b389ee2399854cb83b..109f03e711803251b5b5d06bc42579784a326ba8 100644 (file)
@@ -17,7 +17,7 @@ int main(int ac, char **av)
        if (!two)
                die("not a tree-ish %s", av[2]);
 
-       shift_tree(one->object.sha1, two->object.sha1, shifted, -1);
+       shift_tree(one->object.oid.hash, two->object.oid.hash, shifted, -1);
        printf("shifted: %s\n", sha1_to_hex(shifted));
 
        exit(0);
index 63d54271b09d34dc21076205e6cecf88a56ab422..0eb3cf01aa1da857c7340805971e63305d73a6ea 100644 (file)
@@ -346,7 +346,7 @@ static int fetch_with_fetch(struct transport *transport,
                        continue;
 
                strbuf_addf(&buf, "fetch %s %s\n",
-                           sha1_to_hex(posn->old_sha1),
+                           oid_to_hex(&posn->old_oid),
                            posn->symref ? posn->symref : posn->name);
        }
 
@@ -489,7 +489,7 @@ static int fetch_with_import(struct transport *transport,
                else
                        private = xstrdup(name);
                if (private) {
-                       if (read_ref(private, posn->old_sha1) < 0)
+                       if (read_ref(private, posn->old_oid.hash) < 0)
                                die("Could not read ref %s", private);
                        free(private);
                }
@@ -756,7 +756,7 @@ static int push_update_refs_status(struct helper_data *data,
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
                if (!private)
                        continue;
-               update_ref("update by helper", private, ref->new_sha1, NULL, 0, 0);
+               update_ref("update by helper", private, ref->new_oid.hash, NULL, 0, 0);
                free(private);
        }
        strbuf_release(&buf);
@@ -818,7 +818,7 @@ static int push_refs_with_push(struct transport *transport,
                        if (ref->peer_ref)
                                strbuf_addstr(&buf, ref->peer_ref->name);
                        else
-                               strbuf_addstr(&buf, sha1_to_hex(ref->new_sha1));
+                               strbuf_addstr(&buf, oid_to_hex(&ref->new_oid));
                }
                strbuf_addch(&buf, ':');
                strbuf_addstr(&buf, ref->name);
@@ -827,14 +827,14 @@ static int push_refs_with_push(struct transport *transport,
                /*
                 * The "--force-with-lease" options without explicit
                 * values to expect have already been expanded into
-                * the ref->old_sha1_expect[] field; we can ignore
+                * the ref->old_oid_expect[] field; we can ignore
                 * transport->smart_options->cas altogether and instead
                 * can enumerate them from the refs.
                 */
                if (ref->expect_old_sha1) {
                        struct strbuf cas = STRBUF_INIT;
                        strbuf_addf(&cas, "%s:%s",
-                                   ref->name, sha1_to_hex(ref->old_sha1_expect));
+                                   ref->name, oid_to_hex(&ref->old_oid_expect));
                        string_list_append(&cas_options, strbuf_detach(&cas, NULL));
                }
        }
@@ -878,13 +878,13 @@ static int push_refs_with_export(struct transport *transport,
 
        for (ref = remote_refs; ref; ref = ref->next) {
                char *private;
-               unsigned char sha1[20];
+               struct object_id oid;
 
                private = apply_refspecs(data->refspecs, data->refspec_nr, ref->name);
-               if (private && !get_sha1(private, sha1)) {
+               if (private && !get_sha1(private, oid.hash)) {
                        strbuf_addf(&buf, "^%s", private);
                        string_list_append(&revlist_args, strbuf_detach(&buf, NULL));
-                       hashcpy(ref->old_sha1, sha1);
+                       oidcpy(&ref->old_oid, &oid);
                }
                free(private);
 
@@ -898,7 +898,7 @@ static int push_refs_with_export(struct transport *transport,
                                        name = resolve_ref_unsafe(
                                                 ref->peer_ref->name,
                                                 RESOLVE_REF_READING,
-                                                sha1, &flag);
+                                                oid.hash, &flag);
                                        if (!name || !(flag & REF_ISSYMREF))
                                                name = ref->peer_ref->name;
 
@@ -1016,12 +1016,12 @@ static struct ref *get_refs_list(struct transport *transport, int for_push)
                if (buf.buf[0] == '@')
                        (*tail)->symref = xstrdup(buf.buf + 1);
                else if (buf.buf[0] != '?')
-                       get_sha1_hex(buf.buf, (*tail)->old_sha1);
+                       get_oid_hex(buf.buf, &(*tail)->old_oid);
                if (eon) {
                        if (has_attribute(eon + 1, "unchanged")) {
                                (*tail)->status |= REF_STATUS_UPTODATE;
                                if (read_ref((*tail)->name,
-                                            (*tail)->old_sha1) < 0)
+                                            (*tail)->old_oid.hash) < 0)
                                        die(N_("Could not read ref %s"),
                                            (*tail)->name);
                        }
index 23b2ed6f0cf6f521c4d5afa6c1cffe0786d29a5f..67f366687c7abf2f6bd9cf31faa6f4ed9e6dc69e 100644 (file)
@@ -15,6 +15,7 @@
 #include "submodule.h"
 #include "string-list.h"
 #include "sha1-array.h"
+#include "sigchain.h"
 
 /* rsync support */
 
@@ -79,7 +80,7 @@ static int read_loose_refs(struct strbuf *path, int name_offset,
                                continue;
                        next = alloc_ref(path->buf + name_offset);
                        if (read_in_full(fd, buffer, 40) != 40 ||
-                                       get_sha1_hex(buffer, next->old_sha1)) {
+                                       get_oid_hex(buffer, &next->old_oid)) {
                                close(fd);
                                free(next);
                                continue;
@@ -131,7 +132,7 @@ static void insert_packed_refs(const char *packed_refs, struct ref **list)
                if (!(*list)->next || cmp < 0) {
                        struct ref *next = alloc_ref(buffer + 41);
                        buffer[40] = '\0';
-                       if (get_sha1_hex(buffer, next->old_sha1)) {
+                       if (get_oid_hex(buffer, &next->old_oid)) {
                                warning ("invalid SHA-1: %s", buffer);
                                free(next);
                                continue;
@@ -162,7 +163,7 @@ static void set_upstreams(struct transport *transport, struct ref *refs,
                        continue;
                if (!ref->peer_ref)
                        continue;
-               if (is_null_sha1(ref->new_sha1))
+               if (is_null_oid(&ref->new_oid))
                        continue;
 
                /* Follow symbolic refs (mainly for HEAD). */
@@ -412,7 +413,7 @@ static struct ref *get_refs_from_bundle(struct transport *transport, int for_pus
        for (i = 0; i < data->header.references.nr; i++) {
                struct ref_list_entry *e = data->header.references.list + i;
                struct ref *ref = alloc_ref(e->name);
-               hashcpy(ref->old_sha1, e->sha1);
+               hashcpy(ref->old_oid.hash, e->sha1);
                ref->next = result;
                result = ref;
        }
@@ -608,7 +609,7 @@ void transport_update_tracking_ref(struct remote *remote, struct ref *ref, int v
                        delete_ref(rs.dst, NULL, 0);
                } else
                        update_ref("update by push", rs.dst,
-                                       ref->new_sha1, NULL, 0, 0);
+                                       ref->new_oid.hash, NULL, 0, 0);
                free(rs.dst);
        }
 }
@@ -648,7 +649,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
 {
        if (ref->deletion)
                print_ref_status('-', "[deleted]", ref, NULL, NULL, porcelain);
-       else if (is_null_sha1(ref->old_sha1))
+       else if (is_null_oid(&ref->old_oid))
                print_ref_status('*',
                        (starts_with(ref->name, "refs/tags/") ? "[new tag]" :
                        "[new branch]"),
@@ -658,7 +659,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
                char type;
                const char *msg;
 
-               strbuf_addstr(&quickref, status_abbrev(ref->old_sha1));
+               strbuf_addstr(&quickref, status_abbrev(ref->old_oid.hash));
                if (ref->forced_update) {
                        strbuf_addstr(&quickref, "...");
                        type = '+';
@@ -668,7 +669,7 @@ static void print_ok_ref_status(struct ref *ref, int porcelain)
                        type = ' ';
                        msg = NULL;
                }
-               strbuf_addstr(&quickref, status_abbrev(ref->new_sha1));
+               strbuf_addstr(&quickref, status_abbrev(ref->new_oid.hash));
 
                print_ref_status(type, quickref.buf, ref, ref->peer_ref, msg, porcelain);
                strbuf_release(&quickref);
@@ -1127,6 +1128,8 @@ static int run_pre_push_hook(struct transport *transport,
                return -1;
        }
 
+       sigchain_push(SIGPIPE, SIG_IGN);
+
        strbuf_init(&buf, 256);
 
        for (r = remote_refs; r; r = r->next) {
@@ -1137,11 +1140,13 @@ static int run_pre_push_hook(struct transport *transport,
 
                strbuf_reset(&buf);
                strbuf_addf( &buf, "%s %s %s %s\n",
-                        r->peer_ref->name, sha1_to_hex(r->new_sha1),
-                        r->name, sha1_to_hex(r->old_sha1));
+                        r->peer_ref->name, oid_to_hex(&r->new_oid),
+                        r->name, oid_to_hex(&r->old_oid));
 
-               if (write_in_full(proc.in, buf.buf, buf.len) != buf.len) {
-                       ret = -1;
+               if (write_in_full(proc.in, buf.buf, buf.len) < 0) {
+                       /* We do not mind if a hook does not read all refs. */
+                       if (errno != EPIPE)
+                               ret = -1;
                        break;
                }
        }
@@ -1152,6 +1157,8 @@ static int run_pre_push_hook(struct transport *transport,
        if (!ret)
                ret = x;
 
+       sigchain_pop(SIGPIPE);
+
        x = finish_command(&proc);
        if (!ret)
                ret = x;
@@ -1218,8 +1225,8 @@ int transport_push(struct transport *transport,
                if ((flags & TRANSPORT_RECURSE_SUBMODULES_ON_DEMAND) && !is_bare_repository()) {
                        struct ref *ref = remote_refs;
                        for (; ref; ref = ref->next)
-                               if (!is_null_sha1(ref->new_sha1) &&
-                                   !push_unpushed_submodules(ref->new_sha1,
+                               if (!is_null_oid(&ref->new_oid) &&
+                                   !push_unpushed_submodules(ref->new_oid.hash,
                                            transport->remote->name))
                                    die ("Failed to push all needed submodules!");
                }
@@ -1230,8 +1237,8 @@ int transport_push(struct transport *transport,
                        struct string_list needs_pushing = STRING_LIST_INIT_DUP;
 
                        for (; ref; ref = ref->next)
-                               if (!is_null_sha1(ref->new_sha1) &&
-                                   find_unpushed_submodules(ref->new_sha1,
+                               if (!is_null_oid(&ref->new_oid) &&
+                                   find_unpushed_submodules(ref->new_oid.hash,
                                            transport->remote->name, &needs_pushing))
                                        die_with_unpushed_submodules(&needs_pushing);
                }
@@ -1284,8 +1291,8 @@ int transport_fetch_refs(struct transport *transport, struct ref *refs)
        for (rm = refs; rm; rm = rm->next) {
                nr_refs++;
                if (rm->peer_ref &&
-                   !is_null_sha1(rm->old_sha1) &&
-                   !hashcmp(rm->peer_ref->old_sha1, rm->old_sha1))
+                   !is_null_oid(&rm->old_oid) &&
+                   !oidcmp(&rm->peer_ref->old_oid, &rm->old_oid))
                        continue;
                ALLOC_GROW(heads, nr_heads + 1, nr_alloc);
                heads[nr_heads++] = rm;
index 4336dd33eb301306ff2a57c4c2c4f8c45d61fc50..8ebaaf2cae054bcdfea491fc464f8bf3180b1e03 100644 (file)
@@ -74,15 +74,15 @@ struct transport {
        /**
         * Push the objects and refs. Send the necessary objects, and
         * then, for any refs where peer_ref is set and
-        * peer_ref->new_sha1 is different from old_sha1, tell the
-        * remote side to update each ref in the list from old_sha1 to
-        * peer_ref->new_sha1.
+        * peer_ref->new_oid is different from old_oid, tell the
+        * remote side to update each ref in the list from old_oid to
+        * peer_ref->new_oid.
         *
         * Where possible, set the status for each ref appropriately.
         *
         * The transport must modify new_sha1 in the ref to the new
         * value if the remote accepted the change. Note that this
-        * could be a different value from peer_ref->new_sha1 if the
+        * could be a different value from peer_ref->new_oid if the
         * process involved generating new commits.
         **/
        int (*push_refs)(struct transport *transport, struct ref *refs, int flags);
diff --git a/tree.c b/tree.c
index 413a5b1fa617df2f407d32ffdf78e58d9c42de58..f79ff9813e37529cf7adbcda307027b0495cfb1c 100644 (file)
--- a/tree.c
+++ b/tree.c
@@ -102,7 +102,7 @@ static int read_tree_1(struct tree *tree, struct strbuf *base,
                                    sha1_to_hex(entry.sha1),
                                    base->buf, entry.path);
 
-                       hashcpy(sha1, commit->tree->object.sha1);
+                       hashcpy(sha1, commit->tree->object.oid.hash);
                }
                else
                        continue;
@@ -212,15 +212,15 @@ int parse_tree_gently(struct tree *item, int quiet_on_missing)
 
        if (item->object.parsed)
                return 0;
-       buffer = read_sha1_file(item->object.sha1, &type, &size);
+       buffer = read_sha1_file(item->object.oid.hash, &type, &size);
        if (!buffer)
                return quiet_on_missing ? -1 :
                        error("Could not read %s",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        if (type != OBJ_TREE) {
                free(buffer);
                return error("Object %s not a tree",
-                            sha1_to_hex(item->object.sha1));
+                            oid_to_hex(&item->object.oid));
        }
        return parse_tree_buffer(item, buffer, size);
 }
@@ -248,6 +248,6 @@ struct tree *parse_tree_indirect(const unsigned char *sha1)
                else
                        return NULL;
                if (!obj->parsed)
-                       parse_object(obj->sha1);
+                       parse_object(obj->oid.hash);
        } while (1);
 }
index 08efb1de7b768fd128b4cc56c328acc33b7b009f..b3f6653ffda1a3d6d319752676511307d9cd032b 100644 (file)
@@ -130,14 +130,14 @@ static void create_pack_file(void)
 
        for (i = 0; i < want_obj.nr; i++)
                fprintf(pipe_fd, "%s\n",
-                       sha1_to_hex(want_obj.objects[i].item->sha1));
+                       oid_to_hex(&want_obj.objects[i].item->oid));
        fprintf(pipe_fd, "--not\n");
        for (i = 0; i < have_obj.nr; i++)
                fprintf(pipe_fd, "%s\n",
-                       sha1_to_hex(have_obj.objects[i].item->sha1));
+                       oid_to_hex(&have_obj.objects[i].item->oid));
        for (i = 0; i < extra_edge_obj.nr; i++)
                fprintf(pipe_fd, "%s\n",
-                       sha1_to_hex(extra_edge_obj.objects[i].item->sha1));
+                       oid_to_hex(&extra_edge_obj.objects[i].item->oid));
        fprintf(pipe_fd, "\n");
        fflush(pipe_fd);
        fclose(pipe_fd);
@@ -324,7 +324,7 @@ static int reachable(struct commit *want)
                        break;
                }
                if (!commit->object.parsed)
-                       parse_object(commit->object.sha1);
+                       parse_object(commit->object.oid.hash);
                if (commit->object.flags & REACHABLE)
                        continue;
                commit->object.flags |= REACHABLE;
@@ -491,7 +491,7 @@ static void check_non_tip(void)
                        continue;
                if (!is_our_ref(o))
                        continue;
-               memcpy(namebuf + 1, sha1_to_hex(o->sha1), 40);
+               memcpy(namebuf + 1, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
                if (write_in_full(cmd.in, namebuf, 42) < 0)
                        goto error;
        }
@@ -500,7 +500,7 @@ static void check_non_tip(void)
                o = want_obj.objects[i].item;
                if (is_our_ref(o))
                        continue;
-               memcpy(namebuf, sha1_to_hex(o->sha1), 40);
+               memcpy(namebuf, oid_to_hex(&o->oid), GIT_SHA1_HEXSZ);
                if (write_in_full(cmd.in, namebuf, 41) < 0)
                        goto error;
        }
@@ -534,7 +534,7 @@ static void check_non_tip(void)
                o = want_obj.objects[i].item;
                if (!is_our_ref(o))
                        die("git upload-pack: not our ref %s",
-                           sha1_to_hex(o->sha1));
+                           oid_to_hex(&o->oid));
        }
 }
 
@@ -646,8 +646,8 @@ static void receive_needs(void)
                        struct object *object = &result->item->object;
                        if (!(object->flags & (CLIENT_SHALLOW|NOT_SHALLOW))) {
                                packet_write(1, "shallow %s",
-                                               sha1_to_hex(object->sha1));
-                               register_shallow(object->sha1);
+                                               oid_to_hex(&object->oid));
+                               register_shallow(object->oid.hash);
                                shallow_nr++;
                        }
                        result = result->next;
@@ -658,10 +658,10 @@ static void receive_needs(void)
                        if (object->flags & NOT_SHALLOW) {
                                struct commit_list *parents;
                                packet_write(1, "unshallow %s",
-                                       sha1_to_hex(object->sha1));
+                                       oid_to_hex(&object->oid));
                                object->flags &= ~CLIENT_SHALLOW;
                                /* make sure the real parents are parsed */
-                               unregister_shallow(object->sha1);
+                               unregister_shallow(object->oid.hash);
                                object->parsed = 0;
                                parse_commit_or_die((struct commit *)object);
                                parents = ((struct commit *)object)->parents;
@@ -673,14 +673,14 @@ static void receive_needs(void)
                                add_object_array(object, NULL, &extra_edge_obj);
                        }
                        /* make sure commit traversal conforms to client */
-                       register_shallow(object->sha1);
+                       register_shallow(object->oid.hash);
                }
                packet_flush(1);
        } else
                if (shallows.nr > 0) {
                        int i;
                        for (i = 0; i < shallows.nr; i++)
-                               register_shallow(shallows.objects[i].item->sha1);
+                               register_shallow(shallows.objects[i].item->oid.hash);
                }
 
        shallow_nr += shallows.nr;
index cdeb63f319abc088fe54e4291ed765900d16f1fd..7b7e72b1201850f0ba494f8fc23a5c7f0e46d3eb 100644 (file)
--- a/walker.c
+++ b/walker.c
@@ -19,7 +19,7 @@ static void report_missing(const struct object *obj)
 {
        fprintf(stderr, "Cannot obtain needed %s %s\n",
                obj->type ? typename(obj->type): "object",
-               sha1_to_hex(obj->sha1));
+               oid_to_hex(&obj->oid));
        if (!is_null_sha1(current_commit_sha1))
                fprintf(stderr, "while processing commit %s.\n",
                        sha1_to_hex(current_commit_sha1));
@@ -78,9 +78,9 @@ static int process_commit(struct walker *walker, struct commit *commit)
        if (commit->object.flags & COMPLETE)
                return 0;
 
-       hashcpy(current_commit_sha1, commit->object.sha1);
+       hashcpy(current_commit_sha1, commit->object.oid.hash);
 
-       walker_say(walker, "walk %s\n", sha1_to_hex(commit->object.sha1));
+       walker_say(walker, "walk %s\n", oid_to_hex(&commit->object.oid));
 
        if (walker->get_tree) {
                if (process(walker, &commit->tree->object))
@@ -130,7 +130,7 @@ static int process_object(struct walker *walker, struct object *obj)
        }
        return error("Unable to determine requirements "
                     "of type %s for %s",
-                    typename(obj->type), sha1_to_hex(obj->sha1));
+                    typename(obj->type), oid_to_hex(&obj->oid));
 }
 
 static int process(struct walker *walker, struct object *obj)
@@ -139,14 +139,14 @@ static int process(struct walker *walker, struct object *obj)
                return 0;
        obj->flags |= SEEN;
 
-       if (has_sha1_file(obj->sha1)) {
+       if (has_object_file(&obj->oid)) {
                /* We already have it, so we should scan it now. */
                obj->flags |= TO_SCAN;
        }
        else {
                if (obj->flags & COMPLETE)
                        return 0;
-               walker->prefetch(walker, obj->sha1);
+               walker->prefetch(walker, obj->oid.hash);
        }
 
        object_list_insert(obj, process_queue_end);
@@ -170,13 +170,13 @@ static int loop(struct walker *walker)
                 * the queue because we needed to fetch it first.
                 */
                if (! (obj->flags & TO_SCAN)) {
-                       if (walker->fetch(walker, obj->sha1)) {
+                       if (walker->fetch(walker, obj->oid.hash)) {
                                report_missing(obj);
                                return -1;
                        }
                }
                if (!obj->type)
-                       parse_object(obj->sha1);
+                       parse_object(obj->oid.hash);
                if (process_object(walker, obj))
                        return -1;
        }
@@ -190,7 +190,7 @@ static int interpret_target(struct walker *walker, char *target, unsigned char *
        if (!check_refname_format(target, 0)) {
                struct ref *ref = alloc_ref(target);
                if (!walker->fetch_ref(walker, ref)) {
-                       hashcpy(sha1, ref->old_sha1);
+                       hashcpy(sha1, ref->old_oid.hash);
                        free(ref);
                        return 0;
                }
index 435fc2806ec0a59acf390ee89ed2efc79f229a0e..bba25960b4e3c54a7c1d8861ea895fbe01aa3110 100644 (file)
@@ -1317,15 +1317,14 @@ static int grab_1st_switch(unsigned char *osha1, unsigned char *nsha1,
        target += strlen(" to ");
        strbuf_reset(&cb->buf);
        hashcpy(cb->nsha1, nsha1);
-       for (end = target; *end && *end != '\n'; end++)
-               ;
-       if (!memcmp(target, "HEAD", end - target)) {
+       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_addstr(&cb->buf,
                              find_unique_abbrev(nsha1, DEFAULT_ABBREV));
-               return 1;
        }
-       strbuf_add(&cb->buf, target, end - target);
        return 1;
 }
 
@@ -1347,7 +1346,7 @@ static void wt_status_get_detached_from(struct wt_status_state *state)
            (!hashcmp(cb.nsha1, sha1) ||
             /* perhaps sha1 is a tag, try to dereference to a commit */
             ((commit = lookup_commit_reference_gently(sha1, 1)) != NULL &&
-             !hashcmp(cb.nsha1, commit->object.sha1)))) {
+             !hashcmp(cb.nsha1, commit->object.oid.hash)))) {
                const char *from = ref;
                if (!skip_prefix(from, "refs/tags/", &from))
                        skip_prefix(from, "refs/remotes/", &from);