Merge branch 'ls/travis-win-fix-status'
authorJunio C Hamano <gitster@pobox.com>
Mon, 1 May 2017 05:14:44 +0000 (14:14 +0900)
committerJunio C Hamano <gitster@pobox.com>
Mon, 1 May 2017 05:14:44 +0000 (14:14 +0900)
Relaying status from Windows build by Travis CI was done with an
unsafe invocation of printf.

* ls/travis-win-fix-status:
travis-ci: printf $STATUS as string

57 files changed:
.travis.yml
Documentation/RelNotes/2.13.0.txt
Documentation/git-archimport.txt
Documentation/git-bisect-lk2009.txt
Documentation/git-push.txt
Documentation/git-rebase.txt
Documentation/git-tools.txt
Documentation/git-worktree.txt
Documentation/gitcore-tutorial.txt
Documentation/gitmodules.txt
Documentation/gitweb.conf.txt
Documentation/howto/rebuild-from-update-hook.txt
GIT-VERSION-GEN
Makefile
bisect.c
builtin/am.c
builtin/branch.c
builtin/commit.c
builtin/config.c
builtin/pull.c
builtin/submodule--helper.c
builtin/worktree.c
cache.h
config.c
connect.c
contrib/completion/git-completion.bash
fast-import.c
git-rebase.sh
notes-merge.c
pathspec.c
prio-queue.c
read-cache.c
refs.c
refs.h
refs/files-backend.c
refs/ref-cache.c [new file with mode: 0644]
refs/ref-cache.h [new file with mode: 0644]
refs/refs-internal.h
sequencer.c
t/helper/.gitignore
t/helper/test-strcmp-offset.c [new file with mode: 0644]
t/perf/aggregate.perl
t/perf/p0000-perf-lib-sanity.sh
t/perf/p0006-read-tree-checkout.sh [new file with mode: 0755]
t/perf/repos/.gitignore [new file with mode: 0644]
t/perf/repos/inflate-repo.sh [new file with mode: 0755]
t/perf/repos/many-files.sh [new file with mode: 0755]
t/t0065-strcmp-offset.sh [new file with mode: 0755]
t/t1305-config-include.sh
t/t1309-early-config.sh
t/t2025-worktree-add.sh
t/t2027-worktree-list.sh
t/t3428-rebase-signoff.sh [new file with mode: 0755]
t/t3701-add-interactive.sh
t/t5533-push-cas.sh
t/t7400-submodule-basic.sh
t/t9902-completion.sh
index c757a111ce3ec59f503274fd13d9191c0c70a0fa..297b0bca979d818faec64791ca5167dbd7b50e86 100644 (file)
@@ -71,6 +71,18 @@ matrix:
         # Use the following command to debug the docker build locally:
         # $ docker run -itv "${PWD}:/usr/src/git" --entrypoint /bin/bash daald/ubuntu32:xenial
         # root@container:/# /usr/src/git/ci/run-linux32-build.sh
+    - env: Static Analysis
+      os: linux
+      compiler:
+      addons:
+        apt:
+          packages:
+          - coccinelle
+      before_install:
+      script:
+        # "before_script" that builds Git is inherited from base job
+        - make coccicheck
+      after_failure:
     - env: Documentation
       os: linux
       compiler: clang
index b757fd119c04a6d9b2fc6414ea3e39042ea4e033..f1560f140616b4086c8da2573b60edede1b9f670 100644 (file)
@@ -186,6 +186,15 @@ UI, Workflows & Features
    size that can be expressed in size_t, which can be larger than
    ulong on some platforms.
 
+ * "git rebase" learns "--signoff" option.
+
+ * The completion script (in contrib/) learned to complete "git push
+   --delete b<TAB>" to complete branch name to be deleted.
+
+ * "git worktree add --lock" allows to lock a worktree immediately
+   after it's created. This helps prevent a race between "git worktree
+   add; git worktree lock" and "git worktree prune".
+
 
 Performance, Internal Implementation, Development Support etc.
 
@@ -302,6 +311,20 @@ Performance, Internal Implementation, Development Support etc.
    file is used.  Omit the validation during normal use, and instead
    verify only in "git fsck".
 
+ * Having a git command on the upstream side of a pipe in a test
+   script will hide the exit status from the command, which may cause
+   us to fail to notice a breakage; rewrite tests in a script to avoid
+   this issue.
+
+ * Travis CI learns to run coccicheck.
+
+ * "git checkout" that handles a lot of paths has been optimized by
+   reducing the number of unnecessary checks of paths in the
+   has_dir_name() function.
+
+ * The internals of the refs API around the cached refs has been
+   streamlined.
+
 Also contains various documentation updates and code clean-ups.
 
 
@@ -535,6 +558,19 @@ notes for details).
    using garbage past the end of the buffer.
    (merge 5781a9a270 dt/xgethostname-nul-termination later to maint).
 
+ * A recent update broke "git add -p ../foo" from a subdirectory.
+
+ * While handy, "git_path()" is a dangerous function to use as a
+   callsite that uses it safely one day can be broken by changes
+   to other code that calls it.  Reduction of its use continues.
+   (merge 16d2676c9e jk/war-on-git-path later to maint).
+
+ * The split-index code configuration code used an unsafe git_path()
+   function without copying its result out.
+
+ * Many stale HTTP(s) links have been updated in our documentation.
+   (merge 613416f0be jk/update-links-in-docs later to maint).
+
  * Other minor doc, test and build updates and code cleanups.
    (merge df2a6e38b7 jk/pager-in-use later to maint).
    (merge 75ec4a6cb0 ab/branch-list-doc later to maint).
@@ -557,3 +593,6 @@ notes for details).
    (merge d8f4481c4f jk/quarantine-received-objects later to maint).
    (merge 7ba1ceef95 xy/format-patch-base later to maint).
    (merge fa1912c89a rs/misc-cppcheck-fixes later to maint).
+   (merge f17d642d3b ab/push-cas-doc-n-test later to maint).
+   (merge 61e282425a ss/gitmodules-ignore-doc later to maint).
+   (merge 8d3047cd5b ss/submodule-shallow-doc later to maint).
index 163b9f6f4140d787e3edccd7c13deadff789c3f3..ea7065336920756d03329475472016391cec83dc 100644 (file)
@@ -96,7 +96,7 @@ OPTIONS
        pruned.
 
 -a::
-       Attempt to auto-register archives at http://mirrors.sourcecontrol.net
+       Attempt to auto-register archives at `http://mirrors.sourcecontrol.net`
        This is particularly useful with the -D option.
 
 -t <tmpdir>::
index e015f5b3ccc7651def48b63466072ea51eaecaa0..8ac75fcc25934bbf594d776f919be6d9dbb1013b 100644 (file)
@@ -1347,12 +1347,12 @@ author to given a talk and for publishing this paper.
 References
 ----------
 
-- [[[1]]] http://www.nist.gov/public_affairs/releases/n02-10.htm['Software Errors Cost U.S. Economy $59.5 Billion Annually'. Nist News Release.]
-- [[[2]]] http://java.sun.com/docs/codeconv/html/CodeConventions.doc.html#16712['Code Conventions for the Java Programming Language'. Sun Microsystems.]
-- [[[3]]] http://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
+- [[[1]]] https://www.nist.gov/sites/default/files/documents/director/planning/report02-3.pdf['The Economic Impacts of Inadequate Infratructure for Software Testing'.  Nist Planning Report 02-3], see Executive Summary and Chapter 8.
+- [[[2]]] http://www.oracle.com/technetwork/java/codeconvtoc-136057.html['Code Conventions for the Java Programming Language'. Sun Microsystems.]
+- [[[3]]] https://en.wikipedia.org/wiki/Software_maintenance['Software maintenance'. Wikipedia.]
 - [[[4]]] http://article.gmane.org/gmane.comp.version-control.git/45195/[Junio C Hamano. 'Automated bisect success story'. Gmane.]
-- [[[5]]] http://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
-- [[[6]]] http://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.]
+- [[[5]]] https://lwn.net/Articles/317154/[Christian Couder. 'Fully automated bisecting with "git bisect run"'. LWN.net.]
+- [[[6]]] https://lwn.net/Articles/277872/[Jonathan Corbet. 'Bisection divides users and developers'. LWN.net.]
 - [[[7]]] http://article.gmane.org/gmane.linux.scsi/36652/[Ingo Molnar. 'Re: BUG 2.6.23-rc3 can't see sd partitions on Alpha'. Gmane.]
-- [[[8]]] http://www.kernel.org/pub/software/scm/git/docs/git-bisect.html[Junio C Hamano and the git-list. 'git-bisect(1) Manual Page'. Linux Kernel Archives.]
-- [[[9]]] http://github.com/Ealdwulf/bbchop[Ealdwulf. 'bbchop'. GitHub.]
+- [[[8]]] https://www.kernel.org/pub/software/scm/git/docs/git-bisect.html[Junio C Hamano and the git-list. 'git-bisect(1) Manual Page'. Linux Kernel Archives.]
+- [[[9]]] https://github.com/Ealdwulf/bbchop[Ealdwulf. 'bbchop'. GitHub.]
index 1624a35888c2e39f70b925a2f176d53dd02b809e..0a639664fd67f497b1597cf903015a284947509d 100644 (file)
@@ -217,6 +217,47 @@ with this feature.
 +
 "--no-force-with-lease" will cancel all the previous --force-with-lease on the
 command line.
++
+A general note on safety: supplying this option without an expected
+value, i.e. as `--force-with-lease` or `--force-with-lease=<refname>`
+interacts very badly with anything that implicitly runs `git fetch` on
+the remote to be pushed to in the background, e.g. `git fetch origin`
+on your repository in a cronjob.
++
+The protection it offers over `--force` is ensuring that subsequent
+changes your work wasn't based on aren't clobbered, but this is
+trivially defeated if some background process is updating refs in the
+background. We don't have anything except the remote tracking info to
+go by as a heuristic for refs you're expected to have seen & are
+willing to clobber.
++
+If your editor or some other system is running `git fetch` in the
+background for you a way to mitigate this is to simply set up another
+remote:
++
+       git remote add origin-push $(git config remote.origin.url)
+       git fetch origin-push
++
+Now when the background process runs `git fetch origin` the references
+on `origin-push` won't be updated, and thus commands like:
++
+       git push --force-with-lease origin-push
++
+Will fail unless you manually run `git fetch origin-push`. This method
+is of course entirely defeated by something that runs `git fetch
+--all`, in that case you'd need to either disable it or do something
+more tedious like:
++
+       git fetch              # update 'master' from remote
+       git tag base master    # mark our base point
+       git rebase -i master   # rewrite some commits
+       git push --force-with-lease=master:base master:master
++
+I.e. create a `base` tag for versions of the upstream code that you've
+seen and are willing to overwrite, then rewrite history, and finally
+force push changes to `master` if the remote version is still at
+`base`, regardless of what your local `remotes/origin/master` has been
+updated to in the background.
 
 -f::
 --force::
index 67d48e68831561303c4f39f46bf105371ed0d916..53f4e144444ae7b30ce2634c0f364400927b27d2 100644 (file)
@@ -370,6 +370,11 @@ default is `--no-fork-point`, otherwise the default is `--fork-point`.
        of the rebased commits (see linkgit:git-am[1]).
        Incompatible with the --interactive option.
 
+--signoff::
+       This flag is passed to 'git am' to sign off all the rebased
+       commits (see linkgit:git-am[1]). Incompatible with the
+       --interactive option.
+
 -i::
 --interactive::
        Make a list of the commits which are about to be rebased.  Let the
index 2f4ff501568d3a3e0a65212cf177e6903343a6d9..d0fec4cddd2a500758e1500af869d5c84cd7ebe8 100644 (file)
@@ -7,4 +7,4 @@ maintained here. These days, however, search engines fill that role much
 more efficiently, so this manually-maintained list has been retired.
 
 See also the `contrib/` area, and the Git wiki:
-http://git.or.cz/gitwiki/InterfacesFrontendsAndTools
+https://git.wiki.kernel.org/index.php/InterfacesFrontendsAndTools
index 553cf8413f1792b2eec4c8827996e32f143b817a..b472acc3567f2cb0ed574a48459adb1c9a6f0334 100644 (file)
@@ -9,7 +9,7 @@ git-worktree - Manage multiple working trees
 SYNOPSIS
 --------
 [verse]
-'git worktree add' [-f] [--detach] [--checkout] [-b <new-branch>] <path> [<branch>]
+'git worktree add' [-f] [--detach] [--checkout] [--lock] [-b <new-branch>] <path> [<branch>]
 'git worktree list' [--porcelain]
 'git worktree lock' [--reason <string>] <worktree>
 'git worktree prune' [-n] [-v] [--expire <expire>]
@@ -107,6 +107,11 @@ OPTIONS
        such as configuring sparse-checkout. See "Sparse checkout"
        in linkgit:git-read-tree[1].
 
+--lock::
+       Keep the working tree locked after creation. This is the
+       equivalent of `git worktree lock` after `git worktree add`,
+       but without race condition.
+
 -n::
 --dry-run::
        With `prune`, do not remove anything; just report what it would
index 3a0ec8c53a53e2a36e3d8c657d07b11b641a1c24..7577f27ec20c90eabea2652a438ca09961559f6a 100644 (file)
@@ -1429,7 +1429,7 @@ Although Git is a truly distributed system, it is often
 convenient to organize your project with an informal hierarchy
 of developers. Linux kernel development is run this way. There
 is a nice illustration (page 17, "Merges to Mainline") in
-http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation].
+https://web.archive.org/web/20120915203609/http://www.xenotime.net/linux/mentor/linux-mentoring-2006.pdf[Randy Dunlap's presentation].
 
 It should be stressed that this hierarchy is purely *informal*.
 There is nothing fundamental in Git that enforces the "chain of
index 8f7c50f330f36f625951db7f4cc4657a31fa7113..db5d47eb19b8f2aa21c11a9d3eedf40b9d71e84e 100644 (file)
@@ -66,17 +66,26 @@ submodule.<name>.fetchRecurseSubmodules::
 
 submodule.<name>.ignore::
        Defines under what circumstances "git status" and the diff family show
-       a submodule as modified. When set to "all", it will never be considered
-       modified (but will nonetheless show up in the output of status and
-       commit when it has been staged), "dirty" will ignore all changes
-       to the submodules work tree and
-       takes only differences between the HEAD of the submodule and the commit
-       recorded in the superproject into account. "untracked" will additionally
-       let submodules with modified tracked files in their work tree show up.
-       Using "none" (the default when this option is not set) also shows
-       submodules that have untracked files in their work tree as changed.
-       If this option is also present in the submodules entry in .git/config of
-       the superproject, the setting there will override the one found in
+       a submodule as modified. The following values are supported:
+
+       all;; The submodule will never be considered modified (but will
+           nonetheless show up in the output of status and commit when it has
+           been staged).
+
+       dirty;; All changes to the submodule's work tree will be ignored, only
+           committed differences between the HEAD of the submodule and its
+           recorded state in the superproject are taken into account.
+
+       untracked;; Only untracked files in submodules will be ignored.
+           Committed differences and modifications to tracked files will show
+           up.
+
+       none;; No modifiations to submodules are ignored, all of committed
+           differences, and modifications to tracked and untracked files are
+           shown. This is the default option.
+
+       If this option is also present in the submodules entry in .git/config
+       of the superproject, the setting there will override the one found in
        .gitmodules.
        Both settings can be overridden on the command line by using the
        "--ignore-submodule" option. The 'git submodule' commands are not
@@ -84,8 +93,8 @@ submodule.<name>.ignore::
 
 submodule.<name>.shallow::
        When set to true, a clone of this submodule will be performed as a
-       shallow clone unless the user explicitly asks for a non-shallow
-       clone.
+       shallow clone (with a history depth of 1) unless the user explicitly
+       asks for a non-shallow clone.
 
 
 EXAMPLES
index e6320891b118cc4ddf58293660f8877ac02232ad..9c8982ec98744852e69e96455ec6b2bbce093556 100644 (file)
@@ -368,8 +368,8 @@ $logo_url::
 $logo_label::
        URI and label (title) for the Git logo link (or your site logo,
        if you chose to use different logo image). By default, these both
-       refer to Git homepage, http://git-scm.com[]; in the past, they pointed
-       to Git documentation at http://www.kernel.org[].
+       refer to Git homepage, https://git-scm.com[]; in the past, they pointed
+       to Git documentation at https://www.kernel.org[].
 
 
 Changing gitweb's look
index 25378f68d30bd343f610228ae1b69a1d0cb8b0a1..db219f5c0745dc7b331361ea1237f8e5c102e452 100644 (file)
@@ -4,13 +4,13 @@ From: Junio C Hamano <gitster@pobox.com>
 Date: Fri, 26 Aug 2005 18:19:10 -0700
 Abstract: In this how-to article, JC talks about how he
  uses the post-update hook to automate Git documentation page
- shown at http://www.kernel.org/pub/software/scm/git/docs/.
+ shown at https://www.kernel.org/pub/software/scm/git/docs/.
 Content-type: text/asciidoc
 
 How to rebuild from update hook
 ===============================
 
-The pages under http://www.kernel.org/pub/software/scm/git/docs/
+The pages under https://www.kernel.org/pub/software/scm/git/docs/
 are built from Documentation/ directory of the git.git project
 and needed to be kept up-to-date.  The www.kernel.org/ servers
 are mirrored and I was told that the origin of the mirror is on
index e681ede9d8b825569cfe2b39ec02fddca3a8fe40..598353ea087804d24438cf8e7f3a1236ece347d9 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.13.0-rc0
+DEF_VER=v2.13.0-rc1
 
 LF='
 '
index eb1a1a7cffd5efc06ada671ca482eb638cd25d61..e35542e63181144397053bf7932fad2cf97027bb 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -639,6 +639,7 @@ TEST_PROGRAMS_NEED_X += test-scrap-cache-tree
 TEST_PROGRAMS_NEED_X += test-sha1
 TEST_PROGRAMS_NEED_X += test-sha1-array
 TEST_PROGRAMS_NEED_X += test-sigchain
+TEST_PROGRAMS_NEED_X += test-strcmp-offset
 TEST_PROGRAMS_NEED_X += test-string-list
 TEST_PROGRAMS_NEED_X += test-submodule-config
 TEST_PROGRAMS_NEED_X += test-subprocess
@@ -816,6 +817,7 @@ LIB_OBJS += reflog-walk.o
 LIB_OBJS += refs.o
 LIB_OBJS += refs/files-backend.o
 LIB_OBJS += refs/iterator.o
+LIB_OBJS += refs/ref-cache.o
 LIB_OBJS += ref-filter.o
 LIB_OBJS += remote.o
 LIB_OBJS += replace_object.o
index 03af06c66cebcbc0699b8a074f9ef120da09248a..08c9fb7266c3b2ff8b970c21d350be4adfe7b0fc 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -432,6 +432,7 @@ static int read_bisect_refs(void)
 
 static GIT_PATH_FUNC(git_path_bisect_names, "BISECT_NAMES")
 static GIT_PATH_FUNC(git_path_bisect_expected_rev, "BISECT_EXPECTED_REV")
+static GIT_PATH_FUNC(git_path_bisect_terms, "BISECT_TERMS")
 
 static void read_bisect_paths(struct argv_array *array)
 {
@@ -906,7 +907,7 @@ static void show_diff_tree(const char *prefix, struct commit *commit)
 void read_bisect_terms(const char **read_bad, const char **read_good)
 {
        struct strbuf str = STRBUF_INIT;
-       const char *filename = git_path("BISECT_TERMS");
+       const char *filename = git_path_bisect_terms();
        FILE *fp = fopen(filename, "r");
 
        if (!fp) {
index 805f56cec2f523f8248e2893e10d99cc4e1d1b88..a95dd8b4e6c793d9e51658e2ccbe535cf5a5af45 100644 (file)
@@ -134,17 +134,15 @@ struct am_state {
 };
 
 /**
- * Initializes am_state with the default values. The state directory is set to
- * dir.
+ * Initializes am_state with the default values.
  */
-static void am_state_init(struct am_state *state, const char *dir)
+static void am_state_init(struct am_state *state)
 {
        int gpgsign;
 
        memset(state, 0, sizeof(*state));
 
-       assert(dir);
-       state->dir = xstrdup(dir);
+       state->dir = git_pathdup("rebase-apply");
 
        state->prec = 4;
 
@@ -1185,42 +1183,39 @@ static void NORETURN die_user_resolve(const struct am_state *state)
        exit(128);
 }
 
-static void am_signoff(struct strbuf *sb)
+/**
+ * Appends signoff to the "msg" field of the am_state.
+ */
+static void am_append_signoff(struct am_state *state)
 {
        char *cp;
        struct strbuf mine = STRBUF_INIT;
+       struct strbuf sb = STRBUF_INIT;
 
-       /* Does it end with our own sign-off? */
+       strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
+
+       /* our sign-off */
        strbuf_addf(&mine, "\n%s%s\n",
                    sign_off_header,
                    fmt_name(getenv("GIT_COMMITTER_NAME"),
                             getenv("GIT_COMMITTER_EMAIL")));
-       if (mine.len < sb->len &&
-           !strcmp(mine.buf, sb->buf + sb->len - mine.len))
+
+       /* Does sb end with it already? */
+       if (mine.len < sb.len &&
+           !strcmp(mine.buf, sb.buf + sb.len - mine.len))
                goto exit; /* no need to duplicate */
 
        /* Does it have any Signed-off-by: in the text */
-       for (cp = sb->buf;
+       for (cp = sb.buf;
             cp && *cp && (cp = strstr(cp, sign_off_header)) != NULL;
             cp = strchr(cp, '\n')) {
-               if (sb->buf == cp || cp[-1] == '\n')
+               if (sb.buf == cp || cp[-1] == '\n')
                        break;
        }
 
-       strbuf_addstr(sb, mine.buf + !!cp);
+       strbuf_addstr(&sb, mine.buf + !!cp);
 exit:
        strbuf_release(&mine);
-}
-
-/**
- * Appends signoff to the "msg" field of the am_state.
- */
-static void am_append_signoff(struct am_state *state)
-{
-       struct strbuf sb = STRBUF_INIT;
-
-       strbuf_attach(&sb, state->msg, state->msg_len, state->msg_len);
-       am_signoff(&sb);
        state->msg = strbuf_detach(&sb, &state->msg_len);
 }
 
@@ -1325,9 +1320,6 @@ static int parse_mail(struct am_state *state, const char *mail)
        strbuf_addbuf(&msg, &mi.log_message);
        strbuf_stripspace(&msg, 0);
 
-       if (state->signoff)
-               am_signoff(&msg);
-
        assert(!state->author_name);
        state->author_name = strbuf_detach(&author_name, NULL);
 
@@ -1852,6 +1844,9 @@ static void am_run(struct am_state *state, int resume)
                        if (skip)
                                goto next; /* mail should be skipped */
 
+                       if (state->signoff)
+                               am_append_signoff(state);
+
                        write_author_script(state);
                        write_commit_msg(state);
                }
@@ -2326,7 +2321,7 @@ int cmd_am(int argc, const char **argv, const char *prefix)
 
        git_config(git_am_config, NULL);
 
-       am_state_init(&state, git_path("rebase-apply"));
+       am_state_init(&state);
 
        in_progress = am_in_progress(&state);
        if (in_progress)
index 0552c42ad115bba218f35d4836f1021e3385f6c2..48a513a84dbf6cf2516a10cbcced267369bc747c 100644 (file)
@@ -504,7 +504,7 @@ static void rename_branch(const char *oldname, const char *newname, int force)
        strbuf_release(&newsection);
 }
 
-static const char edit_description[] = "BRANCH_DESCRIPTION";
+static GIT_PATH_FUNC(edit_description, "EDIT_DESCRIPTION")
 
 static int edit_branch_description(const char *branch_name)
 {
@@ -519,9 +519,9 @@ static int edit_branch_description(const char *branch_name)
                      "  %s\n"
                      "Lines starting with '%c' will be stripped.\n"),
                    branch_name, comment_line_char);
-       write_file_buf(git_path(edit_description), buf.buf, buf.len);
+       write_file_buf(edit_description(), buf.buf, buf.len);
        strbuf_reset(&buf);
-       if (launch_editor(git_path(edit_description), &buf, NULL)) {
+       if (launch_editor(edit_description(), &buf, NULL)) {
                strbuf_release(&buf);
                return -1;
        }
index ad188fea9ec552c65c4b82c3558977dd6648fe77..1d805f5da8abd1cfafc41c5c47fd048b2549fbf4 100644 (file)
@@ -821,9 +821,9 @@ static int prepare_to_commit(const char *index_file, const char *prefix,
                                        "If this is not correct, please remove the file\n"
                                        "       %s\n"
                                        "and try again.\n"),
-                               git_path(whence == FROM_MERGE
-                                        ? "MERGE_HEAD"
-                                        : "CHERRY_PICK_HEAD"));
+                               whence == FROM_MERGE ?
+                                       git_path_merge_head() :
+                                       git_path_cherry_pick_head());
                }
 
                fprintf(s->fp, "\n");
index 3f7c8763d21764a43749163ef26d7c3c30877bf3..3a554ad50ca60cd327ce94433022f8d42f024f52 100644 (file)
@@ -26,7 +26,8 @@ static int use_global_config, use_system_config, use_local_config;
 static struct git_config_source given_config_source;
 static int actions, types;
 static int end_null;
-static int respect_includes = -1;
+static int respect_includes_opt = -1;
+static struct config_options config_options;
 static int show_origin;
 
 #define ACTION_GET (1<<0)
@@ -81,7 +82,7 @@ static struct option builtin_config_options[] = {
        OPT_GROUP(N_("Other")),
        OPT_BOOL('z', "null", &end_null, N_("terminate values with NUL byte")),
        OPT_BOOL(0, "name-only", &omit_values, N_("show variable names only")),
-       OPT_BOOL(0, "includes", &respect_includes, N_("respect include directives on lookup")),
+       OPT_BOOL(0, "includes", &respect_includes_opt, N_("respect include directives on lookup")),
        OPT_BOOL(0, "show-origin", &show_origin, N_("show origin of config (file, standard input, blob, command line)")),
        OPT_END(),
 };
@@ -242,7 +243,7 @@ static int get_value(const char *key_, const char *regex_)
        }
 
        git_config_with_options(collect_config, &values,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        ret = !values.nr;
 
@@ -320,7 +321,7 @@ static void get_color(const char *var, const char *def_color)
        get_color_found = 0;
        parsed_color[0] = '\0';
        git_config_with_options(git_get_color_config, NULL,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        if (!get_color_found && def_color) {
                if (color_parse(def_color, parsed_color) < 0)
@@ -352,7 +353,7 @@ static int get_colorbool(const char *var, int print)
        get_diff_color_found = -1;
        get_color_ui_found = -1;
        git_config_with_options(git_get_colorbool_config, NULL,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        if (get_colorbool_found < 0) {
                if (!strcmp(get_colorbool_slot, "color.diff"))
@@ -441,7 +442,7 @@ static int get_urlmatch(const char *var, const char *url)
        }
 
        git_config_with_options(urlmatch_config_entry, &config,
-                               &given_config_source, respect_includes);
+                               &given_config_source, &config_options);
 
        ret = !values.nr;
 
@@ -530,8 +531,10 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                                prefix_filename(prefix, given_config_source.file);
        }
 
-       if (respect_includes == -1)
-               respect_includes = !given_config_source.file;
+       if (respect_includes_opt == -1)
+               config_options.respect_includes = !given_config_source.file;
+       else
+               config_options.respect_includes = respect_includes_opt;
 
        if (end_null) {
                term = '\0';
@@ -578,7 +581,7 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                check_argc(argc, 0, 0);
                if (git_config_with_options(show_all_config, NULL,
                                            &given_config_source,
-                                           respect_includes) < 0) {
+                                           &config_options) < 0) {
                        if (given_config_source.file)
                                die_errno("unable to read config file '%s'",
                                          given_config_source.file);
@@ -597,8 +600,9 @@ int cmd_config(int argc, const char **argv, const char *prefix)
                if (given_config_source.blob)
                        die("editing blobs is not supported");
                git_config(git_default_config, NULL);
-               config_file = xstrdup(given_config_source.file ?
-                                     given_config_source.file : git_path("config"));
+               config_file = given_config_source.file ?
+                               xstrdup(given_config_source.file) :
+                               git_pathdup("config");
                if (use_global_config) {
                        int fd = open(config_file, O_CREAT | O_EXCL | O_WRONLY, 0666);
                        if (fd >= 0) {
index d8aa26d8abe4e436f95f4476133967cf17f29988..dd1a4a94e41ed31617d31c80e097cbc044b3e3f3 100644 (file)
@@ -332,7 +332,7 @@ static int git_pull_config(const char *var, const char *value, void *cb)
  */
 static void get_merge_heads(struct oid_array *merge_heads)
 {
-       const char *filename = git_path("FETCH_HEAD");
+       const char *filename = git_path_fetch_head();
        FILE *fp;
        struct strbuf sb = STRBUF_INIT;
        struct object_id oid;
@@ -791,7 +791,7 @@ int cmd_pull(int argc, const char **argv, const char *prefix)
        if (read_cache_unmerged())
                die_resolve_conflict("pull");
 
-       if (file_exists(git_path("MERGE_HEAD")))
+       if (file_exists(git_path_merge_head()))
                die_conclude_merge();
 
        if (get_oid("HEAD", &orig_head))
index 36e4231821c0acbb6fe21948c752d4ee7c96405d..566a5b6a6f8937742e83577918a94f0dcb20c66c 100644 (file)
@@ -376,12 +376,12 @@ static void init_submodule(const char *path, const char *prefix, int quiet)
        strbuf_reset(&sb);
        strbuf_addf(&sb, "submodule.%s.url", sub->name);
        if (git_config_get_string(sb.buf, &url)) {
-               url = xstrdup(sub->url);
-
-               if (!url)
+               if (!sub->url)
                        die(_("No url found for submodule path '%s' in .gitmodules"),
                                displaypath);
 
+               url = xstrdup(sub->url);
+
                /* Possibly a url relative to parent */
                if (starts_with_dot_dot_slash(url) ||
                    starts_with_dot_slash(url)) {
index 9993ded41aaaaa9a9291bf51b9949a97418a5e76..1722a9bdc2a07ce3d98921299c6b57d03a50e870 100644 (file)
@@ -24,6 +24,7 @@ struct add_opts {
        int force;
        int detach;
        int checkout;
+       int keep_locked;
        const char *new_branch;
        int force_new_branch;
 };
@@ -106,8 +107,7 @@ static void prune_worktrees(void)
                        printf("%s\n", reason.buf);
                if (show_only)
                        continue;
-               strbuf_reset(&path);
-               strbuf_addstr(&path, git_path("worktrees/%s", d->d_name));
+               git_path_buf(&path, "worktrees/%s", d->d_name);
                ret = remove_dir_recursively(&path, 0);
                if (ret < 0 && errno == ENOTDIR)
                        ret = unlink(path.buf);
@@ -215,8 +215,7 @@ static int add_worktree(const char *path, const char *refname,
        }
 
        name = worktree_basename(path, &len);
-       strbuf_addstr(&sb_repo,
-                     git_path("worktrees/%.*s", (int)(path + len - name), name));
+       git_path_buf(&sb_repo, "worktrees/%.*s", (int)(path + len - name), name);
        len = sb_repo.len;
        if (safe_create_leading_directories_const(sb_repo.buf))
                die_errno(_("could not create leading directories of '%s'"),
@@ -242,7 +241,10 @@ static int add_worktree(const char *path, const char *refname,
         * after the preparation is over.
         */
        strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       write_file(sb.buf, "initializing");
+       if (!opts->keep_locked)
+               write_file(sb.buf, "initializing");
+       else
+               write_file(sb.buf, "added with --lock");
 
        strbuf_addf(&sb_git, "%s/.git", path);
        if (safe_create_leading_directories_const(sb_git.buf))
@@ -303,9 +305,11 @@ static int add_worktree(const char *path, const char *refname,
        junk_git_dir = NULL;
 
 done:
-       strbuf_reset(&sb);
-       strbuf_addf(&sb, "%s/locked", sb_repo.buf);
-       unlink_or_warn(sb.buf);
+       if (ret || !opts->keep_locked) {
+               strbuf_reset(&sb);
+               strbuf_addf(&sb, "%s/locked", sb_repo.buf);
+               unlink_or_warn(sb.buf);
+       }
        argv_array_clear(&child_env);
        strbuf_release(&sb);
        strbuf_release(&symref);
@@ -328,6 +332,7 @@ static int add(int ac, const char **av, const char *prefix)
                           N_("create or reset a branch")),
                OPT_BOOL(0, "detach", &opts.detach, N_("detach HEAD at named commit")),
                OPT_BOOL(0, "checkout", &opts.checkout, N_("populate the new working tree")),
+               OPT_BOOL(0, "lock", &opts.keep_locked, N_("keep the new working tree locked")),
                OPT_END()
        };
 
diff --git a/cache.h b/cache.h
index ef0fe43a9df9437bac8e1f78cc1a908798b290fc..e1f0e182ad011d5a3b6912f48b597dcc6d1070b0 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -599,6 +599,7 @@ extern int write_locked_index(struct index_state *, struct lock_file *lock, unsi
 extern int discard_index(struct index_state *);
 extern int unmerged_index(const struct index_state *);
 extern int verify_path(const char *path);
+extern int strcmp_offset(const char *s1, const char *s2, size_t *first_change);
 extern int index_dir_exists(struct index_state *istate, const char *name, int namelen);
 extern void adjust_dirname_case(struct index_state *istate, char *name);
 extern struct cache_entry *index_file_exists(struct index_state *istate, const char *name, int namelen, int igncase);
@@ -1891,6 +1892,11 @@ enum config_origin_type {
        CONFIG_ORIGIN_CMDLINE
 };
 
+struct config_options {
+       unsigned int respect_includes : 1;
+       const char *git_dir;
+};
+
 typedef int (*config_fn_t)(const char *, const char *, void *);
 extern int git_default_config(const char *, const char *, void *);
 extern int git_config_from_file(config_fn_t fn, const char *, void *);
@@ -1904,7 +1910,7 @@ extern void read_early_config(config_fn_t cb, void *data);
 extern void git_config(config_fn_t fn, void *);
 extern int git_config_with_options(config_fn_t fn, void *,
                                   struct git_config_source *config_source,
-                                  int respect_includes);
+                                  const struct config_options *opts);
 extern int git_parse_ulong(const char *, unsigned long *);
 extern int git_parse_maybe_bool(const char *);
 extern int git_config_int(const char *, const char *);
@@ -1957,6 +1963,7 @@ struct config_include_data {
        int depth;
        config_fn_t fn;
        void *data;
+       const struct config_options *opts;
 };
 #define CONFIG_INCLUDE_INIT { 0 }
 extern int git_config_include(const char *name, const char *value, void *data);
index 0daaed338eabecb209e42ee5944e1dc432f94070..b4a3205da32faf43db1ab990f08c0bb941af87d0 100644 (file)
--- a/config.c
+++ b/config.c
@@ -207,13 +207,22 @@ static int prepare_include_condition_pattern(struct strbuf *pat)
        return prefix;
 }
 
-static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
+static int include_by_gitdir(const struct config_options *opts,
+                            const char *cond, size_t cond_len, int icase)
 {
        struct strbuf text = STRBUF_INIT;
        struct strbuf pattern = STRBUF_INIT;
        int ret = 0, prefix;
+       const char *git_dir;
 
-       strbuf_realpath(&text, get_git_dir(), 1);
+       if (opts->git_dir)
+               git_dir = opts->git_dir;
+       else if (have_git_dir())
+               git_dir = get_git_dir();
+       else
+               goto done;
+
+       strbuf_realpath(&text, git_dir, 1);
        strbuf_add(&pattern, cond, cond_len);
        prefix = prepare_include_condition_pattern(&pattern);
 
@@ -242,13 +251,14 @@ static int include_by_gitdir(const char *cond, size_t cond_len, int icase)
        return ret;
 }
 
-static int include_condition_is_true(const char *cond, size_t cond_len)
+static int include_condition_is_true(const struct config_options *opts,
+                                    const char *cond, size_t cond_len)
 {
 
        if (skip_prefix_mem(cond, cond_len, "gitdir:", &cond, &cond_len))
-               return include_by_gitdir(cond, cond_len, 0);
+               return include_by_gitdir(opts, cond, cond_len, 0);
        else if (skip_prefix_mem(cond, cond_len, "gitdir/i:", &cond, &cond_len))
-               return include_by_gitdir(cond, cond_len, 1);
+               return include_by_gitdir(opts, cond, cond_len, 1);
 
        /* unknown conditionals are always false */
        return 0;
@@ -273,7 +283,7 @@ int git_config_include(const char *var, const char *value, void *data)
                ret = handle_path_include(value, inc);
 
        if (!parse_config_key(var, "includeif", &cond, &cond_len, &key) &&
-           (cond && include_condition_is_true(cond, cond_len)) &&
+           (cond && include_condition_is_true(inc->opts, cond, cond_len)) &&
            !strcmp(key, "path"))
                ret = handle_path_include(value, inc);
 
@@ -1511,12 +1521,20 @@ int git_config_system(void)
        return !git_env_bool("GIT_CONFIG_NOSYSTEM", 0);
 }
 
-static int do_git_config_sequence(config_fn_t fn, void *data)
+static int do_git_config_sequence(const struct config_options *opts,
+                                 config_fn_t fn, void *data)
 {
        int ret = 0;
        char *xdg_config = xdg_config_home("config");
        char *user_config = expand_user_path("~/.gitconfig", 0);
-       char *repo_config = have_git_dir() ? git_pathdup("config") : NULL;
+       char *repo_config;
+
+       if (opts->git_dir)
+               repo_config = mkpathdup("%s/config", opts->git_dir);
+       else if (have_git_dir())
+               repo_config = git_pathdup("config");
+       else
+               repo_config = NULL;
 
        current_parsing_scope = CONFIG_SCOPE_SYSTEM;
        if (git_config_system() && !access_or_die(git_etc_gitconfig(), R_OK, 0))
@@ -1547,13 +1565,14 @@ static int do_git_config_sequence(config_fn_t fn, void *data)
 
 int git_config_with_options(config_fn_t fn, void *data,
                            struct git_config_source *config_source,
-                           int respect_includes)
+                           const struct config_options *opts)
 {
        struct config_include_data inc = CONFIG_INCLUDE_INIT;
 
-       if (respect_includes) {
+       if (opts->respect_includes) {
                inc.fn = fn;
                inc.data = data;
+               inc.opts = opts;
                fn = git_config_include;
                data = &inc;
        }
@@ -1569,12 +1588,15 @@ int git_config_with_options(config_fn_t fn, void *data,
        else if (config_source && config_source->blob)
                return git_config_from_blob_ref(fn, config_source->blob, data);
 
-       return do_git_config_sequence(fn, data);
+       return do_git_config_sequence(opts, fn, data);
 }
 
 static void git_config_raw(config_fn_t fn, void *data)
 {
-       if (git_config_with_options(fn, data, NULL, 1) < 0)
+       struct config_options opts = {0};
+
+       opts.respect_includes = 1;
+       if (git_config_with_options(fn, data, NULL, &opts) < 0)
                /*
                 * git_config_with_options() normally returns only
                 * zero, as most errors are fatal, and
@@ -1614,10 +1636,13 @@ static void configset_iter(struct config_set *cs, config_fn_t fn, void *data)
 
 void read_early_config(config_fn_t cb, void *data)
 {
+       struct config_options opts = {0};
        struct strbuf buf = STRBUF_INIT;
 
-       git_config_with_options(cb, data, NULL, 1);
+       opts.respect_includes = 1;
 
+       if (have_git_dir())
+               opts.git_dir = get_git_dir();
        /*
         * When setup_git_directory() was not yet asked to discover the
         * GIT_DIR, we ask discover_git_directory() to figure out whether there
@@ -1626,14 +1651,11 @@ void read_early_config(config_fn_t cb, void *data)
         * notably, the current working directory is still the same after the
         * call).
         */
-       if (!have_git_dir() && discover_git_directory(&buf)) {
-               struct git_config_source repo_config;
+       else if (discover_git_directory(&buf))
+               opts.git_dir = buf.buf;
+
+       git_config_with_options(cb, data, NULL, &opts);
 
-               memset(&repo_config, 0, sizeof(repo_config));
-               strbuf_addstr(&buf, "/config");
-               repo_config.file = buf.buf;
-               git_config_with_options(cb, data, &repo_config, 1);
-       }
        strbuf_release(&buf);
 }
 
index 568a35f754e41d54dc0e7133bbfaa0d1d02ccb27..cd21a1b6f725fc80e40759a8f9b26450633df6a8 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -738,8 +738,10 @@ static void handle_ssh_variant(const char *ssh_command, int is_cmdline,
                         * any longer.
                         */
                        free(ssh_argv);
-               } else
+               } else {
+                       free(p);
                        return;
+               }
        }
 
        if (!strcasecmp(variant, "plink") ||
index 1150164d5ce0e7396191a192189b43444883f739..af658995d55025418c94061842f8d84809b27ef0 100644 (file)
 # completion style.  For example '!f() { : git commit ; ... }; f' will
 # tell the completion to use commit completion.  This also works with aliases
 # of form "!sh -c '...'".  For example, "!sh -c ': git commit ; ... '".
+#
+# You can set the following environment variables to influence the behavior of
+# the completion routines:
+#
+#   GIT_COMPLETION_CHECKOUT_NO_GUESS
+#
+#     When set to "1", do not include "DWIM" suggestions in git-checkout
+#     completion (e.g., completing "foo" when "origin/foo" exists).
 
 case "$COMP_WORDBREAKS" in
 *:*) : great ;;
@@ -709,6 +717,7 @@ __git_complete_remote_or_refspec ()
                i="${words[c]}"
                case "$i" in
                --mirror) [ "$cmd" = "push" ] && no_complete_refspec=1 ;;
+               -d|--delete) [ "$cmd" = "push" ] && lhs=0 ;;
                --all)
                        case "$cmd" in
                        push) no_complete_refspec=1 ;;
@@ -1248,7 +1257,8 @@ _git_checkout ()
                # check if --track, --no-track, or --no-guess was specified
                # if so, disable DWIM mode
                local flags="--track --no-track --no-guess" track_opt="--track"
-               if [ -n "$(__git_find_on_cmdline "$flags")" ]; then
+               if [ "$GIT_COMPLETION_CHECKOUT_NO_GUESS" = "1" ] ||
+                  [ -n "$(__git_find_on_cmdline "$flags")" ]; then
                        track_opt=''
                fi
                __git_complete_refs $track_opt
index 1ea3a08609bd8ece7a22ce33c78a0a798304375f..cf58f875b889eb0360df1f58cacf4f4017c6c10f 100644 (file)
@@ -3202,7 +3202,7 @@ static char* make_fast_import_path(const char *path)
 {
        if (!relative_marks_paths || is_absolute_path(path))
                return xstrdup(path);
-       return xstrdup(git_path("info/fast-import/%s", path));
+       return git_pathdup("info/fast-import/%s", path);
 }
 
 static void option_import_marks(const char *marks,
index 48d7c5ded40e1801a36a897849e0f32b314e5981..db1deed8464f0643763ed6e3c5e54221cad8c985 100755 (executable)
@@ -34,6 +34,7 @@ root!              rebase all reachable commits up to the root(s)
 autosquash         move commits that begin with squash!/fixup! under -i
 committer-date-is-author-date! passed to 'git am'
 ignore-date!       passed to 'git am'
+signoff            passed to 'git am'
 whitespace=!       passed to 'git apply'
 ignore-whitespace! passed to 'git apply'
 C=!                passed to 'git apply'
@@ -321,7 +322,7 @@ do
        --ignore-whitespace)
                git_am_opt="$git_am_opt $1"
                ;;
-       --committer-date-is-author-date|--ignore-date)
+       --committer-date-is-author-date|--ignore-date|--signoff|--no-signoff)
                git_am_opt="$git_am_opt $1"
                force_rebase=t
                ;;
index 5998605acc64471193dc9d945610db8491f34bd8..32caaaff7477d3640c81a19c54c1c92ffb8ca8b1 100644 (file)
@@ -676,7 +676,7 @@ int notes_merge_commit(struct notes_merge_options *o,
        const char *msg = strstr(buffer, "\n\n");
        int baselen;
 
-       strbuf_addstr(&path, git_path(NOTES_MERGE_WORKTREE));
+       git_path_buf(&path, NOTES_MERGE_WORKTREE);
        if (o->verbosity >= 3)
                printf("Committing notes in notes merge worktree at %s\n",
                        path.buf);
@@ -741,7 +741,7 @@ int notes_merge_abort(struct notes_merge_options *o)
        struct strbuf buf = STRBUF_INIT;
        int ret;
 
-       strbuf_addstr(&buf, git_path(NOTES_MERGE_WORKTREE));
+       git_path_buf(&buf, NOTES_MERGE_WORKTREE);
        if (o->verbosity >= 3)
                printf("Removing notes merge worktree at %s/*\n", buf.buf);
        ret = remove_dir_recursively(&buf, REMOVE_DIR_KEEP_TOPLEVEL);
index 69ef86b85a7735df428b8cab9f9cce477a4fcd89..50f76fff458e45f57ae67c3474de8378f390f978 100644 (file)
@@ -505,7 +505,7 @@ static void init_pathspec_item(struct pathspec_item *item, unsigned flags,
         * original. Useful for passing to another command.
         */
        if ((flags & PATHSPEC_PREFIX_ORIGIN) &&
-           prefixlen && !get_literal_global()) {
+           !get_literal_global()) {
                struct strbuf sb = STRBUF_INIT;
 
                /* Preserve the actual prefix length of each pattern */
index 17252d231b31f2b8d82d7160d7b95709e14779ff..fc3860fdcb9fc50d4d883e1a8638cca474cfb8d0 100644 (file)
@@ -21,7 +21,7 @@ void prio_queue_reverse(struct prio_queue *queue)
 
        if (queue->compare != NULL)
                die("BUG: prio_queue_reverse() on non-LIFO queue");
-       for (i = 0; i <= (j = (queue->nr - 1) - i); i++)
+       for (i = 0; i < (j = (queue->nr - 1) - i); i++)
                swap(queue, i, j);
 }
 
index 008b335844c48fad9408452c11bd921d7b9b0b44..b3d0f3c30bfd7ee054d406afd8d8df29342105c2 100644 (file)
@@ -887,9 +887,32 @@ static int has_file_name(struct index_state *istate,
        return retval;
 }
 
+
+/*
+ * Like strcmp(), but also return the offset of the first change.
+ * If strings are equal, return the length.
+ */
+int strcmp_offset(const char *s1, const char *s2, size_t *first_change)
+{
+       size_t k;
+
+       if (!first_change)
+               return strcmp(s1, s2);
+
+       for (k = 0; s1[k] == s2[k]; k++)
+               if (s1[k] == '\0')
+                       break;
+
+       *first_change = k;
+       return (unsigned char)s1[k] - (unsigned char)s2[k];
+}
+
 /*
  * Do we have another file with a pathname that is a proper
  * subset of the name we're trying to add?
+ *
+ * That is, is there another file in the index with a path
+ * that matches a sub-directory in the given entry?
  */
 static int has_dir_name(struct index_state *istate,
                        const struct cache_entry *ce, int pos, int ok_to_replace)
@@ -898,9 +921,51 @@ static int has_dir_name(struct index_state *istate,
        int stage = ce_stage(ce);
        const char *name = ce->name;
        const char *slash = name + ce_namelen(ce);
+       size_t len_eq_last;
+       int cmp_last = 0;
+
+       /*
+        * We are frequently called during an iteration on a sorted
+        * list of pathnames and while building a new index.  Therefore,
+        * there is a high probability that this entry will eventually
+        * be appended to the index, rather than inserted in the middle.
+        * If we can confirm that, we can avoid binary searches on the
+        * components of the pathname.
+        *
+        * Compare the entry's full path with the last path in the index.
+        */
+       if (istate->cache_nr > 0) {
+               cmp_last = strcmp_offset(name,
+                       istate->cache[istate->cache_nr - 1]->name,
+                       &len_eq_last);
+               if (cmp_last > 0) {
+                       if (len_eq_last == 0) {
+                               /*
+                                * The entry sorts AFTER the last one in the
+                                * index and their paths have no common prefix,
+                                * so there cannot be a F/D conflict.
+                                */
+                               return retval;
+                       } else {
+                               /*
+                                * The entry sorts AFTER the last one in the
+                                * index, but has a common prefix.  Fall through
+                                * to the loop below to disect the entry's path
+                                * and see where the difference is.
+                                */
+                       }
+               } else if (cmp_last == 0) {
+                       /*
+                        * The entry exactly matches the last one in the
+                        * index, but because of multiple stage and CE_REMOVE
+                        * items, we fall through and let the regular search
+                        * code handle it.
+                        */
+               }
+       }
 
        for (;;) {
-               int len;
+               size_t len;
 
                for (;;) {
                        if (*--slash == '/')
@@ -910,6 +975,67 @@ static int has_dir_name(struct index_state *istate,
                }
                len = slash - name;
 
+               if (cmp_last > 0) {
+                       /*
+                        * (len + 1) is a directory boundary (including
+                        * the trailing slash).  And since the loop is
+                        * decrementing "slash", the first iteration is
+                        * the longest directory prefix; subsequent
+                        * iterations consider parent directories.
+                        */
+
+                       if (len + 1 <= len_eq_last) {
+                               /*
+                                * The directory prefix (including the trailing
+                                * slash) also appears as a prefix in the last
+                                * entry, so the remainder cannot collide (because
+                                * strcmp said the whole path was greater).
+                                *
+                                * EQ: last: xxx/A
+                                *     this: xxx/B
+                                *
+                                * LT: last: xxx/file_A
+                                *     this: xxx/file_B
+                                */
+                               return retval;
+                       }
+
+                       if (len > len_eq_last) {
+                               /*
+                                * This part of the directory prefix (excluding
+                                * the trailing slash) is longer than the known
+                                * equal portions, so this sub-directory cannot
+                                * collide with a file.
+                                *
+                                * GT: last: xxxA
+                                *     this: xxxB/file
+                                */
+                               return retval;
+                       }
+
+                       if (istate->cache_nr > 0 &&
+                               ce_namelen(istate->cache[istate->cache_nr - 1]) > len) {
+                               /*
+                                * The directory prefix lines up with part of
+                                * a longer file or directory name, but sorts
+                                * after it, so this sub-directory cannot
+                                * collide with a file.
+                                *
+                                * last: xxx/yy-file (because '-' sorts before '/')
+                                * this: xxx/yy/abc
+                                */
+                               return retval;
+                       }
+
+                       /*
+                        * This is a possible collision. Fall through and
+                        * let the regular search code handle it.
+                        *
+                        * last: xxx
+                        * this: xxx/file
+                        */
+               }
+
                pos = index_name_stage_pos(istate, name, len, stage);
                if (pos >= 0) {
                        /*
@@ -1001,7 +1127,16 @@ static int add_index_entry_with_check(struct index_state *istate, struct cache_e
 
        if (!(option & ADD_CACHE_KEEP_CACHE_TREE))
                cache_tree_invalidate_path(istate, ce->name);
-       pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
+
+       /*
+        * If this entry's path sorts after the last entry in the index,
+        * we can avoid searching for it.
+        */
+       if (istate->cache_nr > 0 &&
+               strcmp(ce->name, istate->cache[istate->cache_nr - 1]->name) > 0)
+               pos = -istate->cache_nr - 1;
+       else
+               pos = index_name_stage_pos(istate, ce->name, ce_namelen(ce), ce_stage(ce));
 
        /* existing match? Just replace it. */
        if (pos >= 0) {
@@ -1689,9 +1824,10 @@ int do_read_index(struct index_state *istate, const char *path, int must_exist)
  */
 static void freshen_shared_index(char *base_sha1_hex, int warn)
 {
-       const char *shared_index = git_path("sharedindex.%s", base_sha1_hex);
+       char *shared_index = git_pathdup("sharedindex.%s", base_sha1_hex);
        if (!check_and_freshen_file(shared_index, 1) && warn)
                warning("could not freshen shared index '%s'", shared_index);
+       free(shared_index);
 }
 
 int read_index_from(struct index_state *istate, const char *path)
diff --git a/refs.c b/refs.c
index a3d5f42e37345096628ab83fee629f22df1bb7b5..df75f8e0d695fc235c1a2497069a9b916a4a04e8 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -5,6 +5,7 @@
 #include "cache.h"
 #include "hashmap.h"
 #include "lockfile.h"
+#include "iterator.h"
 #include "refs.h"
 #include "refs/refs-internal.h"
 #include "object.h"
@@ -1238,6 +1239,18 @@ int head_ref(each_ref_fn fn, void *cb_data)
        return head_ref_submodule(NULL, fn, cb_data);
 }
 
+struct ref_iterator *refs_ref_iterator_begin(
+               struct ref_store *refs,
+               const char *prefix, int trim, int flags)
+{
+       struct ref_iterator *iter;
+
+       iter = refs->be->iterator_begin(refs, prefix, flags);
+       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+
+       return iter;
+}
+
 /*
  * Call fn for each reference in the specified submodule for which the
  * refname begins with prefix. If trim is non-zero, then trim that
@@ -1255,8 +1268,7 @@ static int do_for_each_ref(struct ref_store *refs, const char *prefix,
        if (!refs)
                return 0;
 
-       iter = refs->be->iterator_begin(refs, prefix, flags);
-       iter = prefix_ref_iterator_begin(iter, prefix, trim);
+       iter = refs_ref_iterator_begin(refs, prefix, trim, flags);
 
        return do_for_each_ref_iterator(iter, fn, cb_data);
 }
@@ -1334,6 +1346,13 @@ int for_each_rawref(each_ref_fn fn, void *cb_data)
        return refs_for_each_rawref(get_main_ref_store(), fn, cb_data);
 }
 
+int refs_read_raw_ref(struct ref_store *ref_store,
+                     const char *refname, unsigned char *sha1,
+                     struct strbuf *referent, unsigned int *type)
+{
+       return ref_store->be->read_raw_ref(ref_store, refname, sha1, referent, type);
+}
+
 /* This function needs to return a meaningful errno on failure */
 const char *refs_resolve_ref_unsafe(struct ref_store *refs,
                                    const char *refname,
@@ -1370,8 +1389,8 @@ const char *refs_resolve_ref_unsafe(struct ref_store *refs,
        for (symref_count = 0; symref_count < SYMREF_MAXDEPTH; symref_count++) {
                unsigned int read_flags = 0;
 
-               if (refs->be->read_raw_ref(refs, refname,
-                                          sha1, &sb_refname, &read_flags)) {
+               if (refs_read_raw_ref(refs, refname,
+                                     sha1, &sb_refname, &read_flags)) {
                        *flags |= read_flags;
                        if (errno != ENOENT || (resolve_flags & RESOLVE_REF_READING))
                                return NULL;
@@ -1654,11 +1673,91 @@ int ref_transaction_commit(struct ref_transaction *transaction,
 
 int refs_verify_refname_available(struct ref_store *refs,
                                  const char *refname,
-                                 const struct string_list *extra,
+                                 const struct string_list *extras,
                                  const struct string_list *skip,
                                  struct strbuf *err)
 {
-       return refs->be->verify_refname_available(refs, refname, extra, skip, err);
+       const char *slash;
+       const char *extra_refname;
+       struct strbuf dirname = STRBUF_INIT;
+       struct strbuf referent = STRBUF_INIT;
+       struct object_id oid;
+       unsigned int type;
+       struct ref_iterator *iter;
+       int ok;
+       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 (skip && string_list_has_string(skip, dirname.buf))
+                       continue;
+
+               if (!refs_read_raw_ref(refs, dirname.buf, oid.hash, &referent, &type)) {
+                       strbuf_addf(err, "'%s' exists; cannot create '%s'",
+                                   dirname.buf, refname);
+                       goto cleanup;
+               }
+
+               if (extras && string_list_has_string(extras, dirname.buf)) {
+                       strbuf_addf(err, "cannot process '%s' and '%s' at the same time",
+                                   refname, dirname.buf);
+                       goto cleanup;
+               }
+       }
+
+       /*
+        * 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, '/');
+
+       iter = refs_ref_iterator_begin(refs, dirname.buf, 0,
+                                      DO_FOR_EACH_INCLUDE_BROKEN);
+       while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+               if (skip &&
+                   string_list_has_string(skip, iter->refname))
+                       continue;
+
+               strbuf_addf(err, "'%s' exists; cannot create '%s'",
+                           iter->refname, refname);
+               ref_iterator_abort(iter);
+               goto cleanup;
+       }
+
+       if (ok != ITER_DONE)
+               die("BUG: error while iterating over references");
+
+       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(&referent);
+       strbuf_release(&dirname);
+       return ret;
 }
 
 int refs_for_each_reflog(struct ref_store *refs, each_ref_fn fn, void *cb_data)
diff --git a/refs.h b/refs.h
index 49e97d7d5fdf1ce385ea8917bda00a4cce68561f..07cf4cd41b15ee5dafdb5c99983b136db84aaa81 100644 (file)
--- a/refs.h
+++ b/refs.h
@@ -97,7 +97,7 @@ int read_ref(const char *refname, unsigned char *sha1);
 
 int refs_verify_refname_available(struct ref_store *refs,
                                  const char *refname,
-                                 const struct string_list *extra,
+                                 const struct string_list *extras,
                                  const struct string_list *skip,
                                  struct strbuf *err);
 
index c9d900fd1287e98ee59421649aee1e34b73e4fa0..83ea080e013f90e95d1897258dd80b44ac9b5db6 100644 (file)
@@ -1,6 +1,7 @@
 #include "../cache.h"
 #include "../refs.h"
 #include "refs-internal.h"
+#include "ref-cache.h"
 #include "../iterator.h"
 #include "../dir-iterator.h"
 #include "../lockfile.h"
@@ -13,511 +14,6 @@ struct ref_lock {
        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 files_ref_store;
-
-/*
- * 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 files_ref_store that contains this ref_dir. */
-       struct files_ref_store *ref_store;
-
-       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 files_ref_store *ref_store,
-                                         const char *dirname, size_t len,
-                                         int incomplete);
-static void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *entry);
-static int files_log_ref_write(struct files_ref_store *refs,
-                              const char *refname, const unsigned char *old_sha1,
-                              const unsigned char *new_sha1, const char *msg,
-                              int flags, struct strbuf *err);
-
-static struct ref_dir *get_ref_dir(struct ref_entry *entry)
-{
-       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_store,
-                                                              "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)
-{
-       struct ref_entry *ref;
-
-       if (check_name &&
-           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
-               die("Reference has invalid format: '%s'", refname);
-       FLEX_ALLOC_STR(ref, name, refname);
-       hashcpy(ref->u.value.oid.hash, sha1);
-       oidclr(&ref->u.value.peeled);
-       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 files_ref_store *ref_store,
-                                         const char *dirname, size_t len,
-                                         int incomplete)
-{
-       struct ref_entry *direntry;
-       FLEX_ALLOC_MEM(direntry, name, dirname, len);
-       direntry->u.subdir.ref_store = ref_store;
-       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_store, 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, 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 true if refname, which has the specified oid and flags, can
  * be resolved to an object in the database. If the referred-to object
@@ -536,358 +32,8 @@ static int ref_resolves_to_object(const char *refname,
        return 1;
 }
 
-/*
- * Return true if the reference described by entry can be resolved to
- * an object in the database; otherwise, emit a warning and return
- * false.
- */
-static int entry_resolves_to_object(struct ref_entry *entry)
-{
-       return ref_resolves_to_object(entry->name,
-                                     &entry->u.value.oid, entry->flag);
-}
-
-typedef int each_ref_entry_fn(struct ref_entry *entry, void *cb_data);
-
-/*
- * 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;
-}
-
-/*
- * 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));
-       }
-}
-
-/*
- * A level in the reference hierarchy that is currently being iterated
- * through.
- */
-struct cache_ref_iterator_level {
-       /*
-        * The ref_dir being iterated over at this level. The ref_dir
-        * is sorted before being stored here.
-        */
-       struct ref_dir *dir;
-
-       /*
-        * The index of the current entry within dir (which might
-        * itself be a directory). If index == -1, then the iteration
-        * hasn't yet begun. If index == dir->nr, then the iteration
-        * through this level is over.
-        */
-       int index;
-};
-
-/*
- * Represent an iteration through a ref_dir in the memory cache. The
- * iteration recurses through subdirectories.
- */
-struct cache_ref_iterator {
-       struct ref_iterator base;
-
-       /*
-        * The number of levels currently on the stack. This is always
-        * at least 1, because when it becomes zero the iteration is
-        * ended and this struct is freed.
-        */
-       size_t levels_nr;
-
-       /* The number of levels that have been allocated on the stack */
-       size_t levels_alloc;
-
-       /*
-        * A stack of levels. levels[0] is the uppermost level that is
-        * being iterated over in this iteration. (This is not
-        * necessary the top level in the references hierarchy. If we
-        * are iterating through a subtree, then levels[0] will hold
-        * the ref_dir for that subtree, and subsequent levels will go
-        * on from there.)
-        */
-       struct cache_ref_iterator_level *levels;
-};
-
-static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
-{
-       struct cache_ref_iterator *iter =
-               (struct cache_ref_iterator *)ref_iterator;
-
-       while (1) {
-               struct cache_ref_iterator_level *level =
-                       &iter->levels[iter->levels_nr - 1];
-               struct ref_dir *dir = level->dir;
-               struct ref_entry *entry;
-
-               if (level->index == -1)
-                       sort_ref_dir(dir);
-
-               if (++level->index == level->dir->nr) {
-                       /* This level is exhausted; pop up a level */
-                       if (--iter->levels_nr == 0)
-                               return ref_iterator_abort(ref_iterator);
-
-                       continue;
-               }
-
-               entry = dir->entries[level->index];
-
-               if (entry->flag & REF_DIR) {
-                       /* push down a level */
-                       ALLOC_GROW(iter->levels, iter->levels_nr + 1,
-                                  iter->levels_alloc);
-
-                       level = &iter->levels[iter->levels_nr++];
-                       level->dir = get_ref_dir(entry);
-                       level->index = -1;
-               } else {
-                       iter->base.refname = entry->name;
-                       iter->base.oid = &entry->u.value.oid;
-                       iter->base.flags = entry->flag;
-                       return ITER_OK;
-               }
-       }
-}
-
-static enum peel_status peel_entry(struct ref_entry *entry, int repeel);
-
-static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
-                                  struct object_id *peeled)
-{
-       struct cache_ref_iterator *iter =
-               (struct cache_ref_iterator *)ref_iterator;
-       struct cache_ref_iterator_level *level;
-       struct ref_entry *entry;
-
-       level = &iter->levels[iter->levels_nr - 1];
-
-       if (level->index == -1)
-               die("BUG: peel called before advance for cache iterator");
-
-       entry = level->dir->entries[level->index];
-
-       if (peel_entry(entry, 0))
-               return -1;
-       oidcpy(peeled, &entry->u.value.peeled);
-       return 0;
-}
-
-static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
-{
-       struct cache_ref_iterator *iter =
-               (struct cache_ref_iterator *)ref_iterator;
-
-       free(iter->levels);
-       base_ref_iterator_free(ref_iterator);
-       return ITER_DONE;
-}
-
-static struct ref_iterator_vtable cache_ref_iterator_vtable = {
-       cache_ref_iterator_advance,
-       cache_ref_iterator_peel,
-       cache_ref_iterator_abort
-};
-
-static struct ref_iterator *cache_ref_iterator_begin(struct ref_dir *dir)
-{
-       struct cache_ref_iterator *iter;
-       struct ref_iterator *ref_iterator;
-       struct cache_ref_iterator_level *level;
-
-       iter = xcalloc(1, sizeof(*iter));
-       ref_iterator = &iter->base;
-       base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
-       ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
-
-       iter->levels_nr = 1;
-       level = &iter->levels[0];
-       level->index = -1;
-       level->dir = dir;
-
-       return ref_iterator;
-}
-
-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;
+       struct ref_cache *cache;
 
        /*
         * Count of references to the data structure in this instance,
@@ -922,7 +68,7 @@ struct files_ref_store {
        char *gitcommondir;
        char *packed_refs_path;
 
-       struct ref_entry *loose;
+       struct ref_cache *loose;
        struct packed_ref_cache *packed;
 };
 
@@ -944,7 +90,7 @@ static void acquire_packed_ref_cache(struct packed_ref_cache *packed_refs)
 static int release_packed_ref_cache(struct packed_ref_cache *packed_refs)
 {
        if (!--packed_refs->referrers) {
-               free_ref_entry(packed_refs->root);
+               free_ref_cache(packed_refs->cache);
                stat_validity_clear(&packed_refs->validity);
                free(packed_refs);
                return 1;
@@ -968,7 +114,7 @@ static void clear_packed_ref_cache(struct files_ref_store *refs)
 static void clear_loose_ref_cache(struct files_ref_store *refs)
 {
        if (refs->loose) {
-               free_ref_entry(refs->loose);
+               free_ref_cache(refs->loose);
                refs->loose = NULL;
        }
 }
@@ -1141,7 +287,7 @@ static void read_packed_refs(FILE *f, struct ref_dir *dir)
                        if (peeled == PEELED_FULLY ||
                            (peeled == PEELED_TAGS && starts_with(refname, "refs/tags/")))
                                last->flag |= REF_KNOWS_PEELED;
-                       add_ref(dir, last);
+                       add_ref_entry(dir, last);
                        continue;
                }
                if (last &&
@@ -1229,11 +375,12 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 
                refs->packed = xcalloc(1, sizeof(*refs->packed));
                acquire_packed_ref_cache(refs->packed);
-               refs->packed->root = create_dir_entry(refs, "", 0, 0);
+               refs->packed->cache = create_ref_cache(&refs->base, NULL);
+               refs->packed->cache->root->flag &= ~REF_INCOMPLETE;
                f = fopen(packed_refs_file, "r");
                if (f) {
                        stat_validity_update(&refs->packed->validity, fileno(f));
-                       read_packed_refs(f, get_ref_dir(refs->packed->root));
+                       read_packed_refs(f, get_ref_dir(refs->packed->cache->root));
                        fclose(f);
                }
        }
@@ -1242,7 +389,7 @@ static struct packed_ref_cache *get_packed_ref_cache(struct files_ref_store *ref
 
 static struct ref_dir *get_packed_ref_dir(struct packed_ref_cache *packed_ref_cache)
 {
-       return get_ref_dir(packed_ref_cache->root);
+       return get_ref_dir(packed_ref_cache->cache->root);
 }
 
 static struct ref_dir *get_packed_refs(struct files_ref_store *refs)
@@ -1263,8 +410,8 @@ static void add_packed_ref(struct files_ref_store *refs,
 
        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));
+       add_ref_entry(get_packed_ref_dir(packed_ref_cache),
+                     create_ref_entry(refname, sha1, REF_ISPACKED, 1));
 }
 
 /*
@@ -1272,9 +419,11 @@ static void add_packed_ref(struct files_ref_store *refs,
  * (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)
+static void loose_fill_ref_dir(struct ref_store *ref_store,
+                              struct ref_dir *dir, const char *dirname)
 {
-       struct files_ref_store *refs = dir->ref_store;
+       struct files_ref_store *refs =
+               files_downcast(ref_store, REF_STORE_READ, "fill_ref_dir");
        DIR *d;
        struct dirent *de;
        int dirnamelen = strlen(dirname);
@@ -1310,7 +459,7 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
                } else if (S_ISDIR(st.st_mode)) {
                        strbuf_addch(&refname, '/');
                        add_entry_to_dir(dir,
-                                        create_dir_entry(refs, refname.buf,
+                                        create_dir_entry(dir->cache, refname.buf,
                                                          refname.len, 1));
                } else {
                        if (!refs_resolve_ref_unsafe(&refs->base,
@@ -1347,9 +496,24 @@ static void read_loose_refs(const char *dirname, struct ref_dir *dir)
        strbuf_release(&refname);
        strbuf_release(&path);
        closedir(d);
+
+       /*
+        * Manually add refs/bisect, which, being per-worktree, might
+        * not appear in the directory listing for refs/ in the main
+        * repo.
+        */
+       if (!strcmp(dirname, "refs/")) {
+               int pos = search_ref_dir(dir, "refs/bisect/", 12);
+
+               if (pos < 0) {
+                       struct ref_entry *child_entry = create_dir_entry(
+                                       dir->cache, "refs/bisect/", 12, 1);
+                       add_entry_to_dir(dir, child_entry);
+               }
+       }
 }
 
-static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
+static struct ref_cache *get_loose_ref_cache(struct files_ref_store *refs)
 {
        if (!refs->loose) {
                /*
@@ -1357,14 +521,19 @@ static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
                 * are about to read the only subdirectory that can
                 * hold references:
                 */
-               refs->loose = create_dir_entry(refs, "", 0, 0);
+               refs->loose = create_ref_cache(&refs->base, loose_fill_ref_dir);
+
+               /* We're going to fill the top level ourselves: */
+               refs->loose->root->flag &= ~REF_INCOMPLETE;
+
                /*
-                * Create an incomplete entry for "refs/":
+                * Add an incomplete entry for "refs/" (to be filled
+                * lazily):
                 */
-               add_entry_to_dir(get_ref_dir(refs->loose),
-                                create_dir_entry(refs, "refs/", 5, 1));
+               add_entry_to_dir(get_ref_dir(refs->loose->root),
+                                create_dir_entry(refs->loose, "refs/", 5, 1));
        }
-       return get_ref_dir(refs->loose);
+       return refs->loose;
 }
 
 /*
@@ -1374,7 +543,7 @@ static struct ref_dir *get_loose_refs(struct files_ref_store *refs)
 static struct ref_entry *get_packed_ref(struct files_ref_store *refs,
                                        const char *refname)
 {
-       return find_ref(get_packed_refs(refs), refname);
+       return find_ref_entry(get_packed_refs(refs), refname);
 }
 
 /*
@@ -1564,7 +733,7 @@ static void unlock_ref(struct ref_lock *lock)
  *
  * If the reference doesn't already exist, verify that refname doesn't
  * have a D/F conflict with any existing references. extras and skip
- * are passed to verify_refname_available_dir() for this check.
+ * are passed to refs_verify_refname_available() for this check.
  *
  * If mustexist is not set and the reference is not found or is
  * broken, lock the reference anyway but clear sha1.
@@ -1579,7 +748,7 @@ static void unlock_ref(struct ref_lock *lock)
  *
  * but it includes a lot more code to
  * - Deal with possible races with other processes
- * - Avoid calling verify_refname_available_dir() when it can be
+ * - Avoid calling refs_verify_refname_available() when it can be
  *   avoided, namely if we were successfully able to read the ref
  * - Generate informative error messages in the case of failure
  */
@@ -1636,7 +805,8 @@ static int lock_raw_ref(struct files_ref_store *refs,
                        } else {
                                /*
                                 * The error message set by
-                                * verify_refname_available_dir() is OK.
+                                * refs_verify_refname_available() is
+                                * OK.
                                 */
                                ret = TRANSACTION_NAME_CONFLICT;
                        }
@@ -1726,10 +896,9 @@ static int lock_raw_ref(struct files_ref_store *refs,
                                goto error_return;
                        } else if (remove_dir_recursively(&ref_file,
                                                          REMOVE_DIR_EMPTY_ONLY)) {
-                               if (verify_refname_available_dir(
-                                                   refname, extras, skip,
-                                                   get_loose_refs(refs),
-                                                   err)) {
+                               if (refs_verify_refname_available(
+                                                   &refs->base, refname,
+                                                   extras, skip, err)) {
                                        /*
                                         * The error message set by
                                         * verify_refname_available() is OK.
@@ -1761,16 +930,13 @@ static int lock_raw_ref(struct files_ref_store *refs,
 
                /*
                 * 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.
+                * make sure there is no existing ref that conflicts
+                * with refname:
                 */
-               if (verify_refname_available_dir(
-                                   refname, extras, skip,
-                                   get_packed_refs(refs),
-                                   err)) {
+               if (refs_verify_refname_available(
+                                   &refs->base, refname,
+                                   extras, skip, err))
                        goto error_return;
-               }
        }
 
        ret = 0;
@@ -1785,41 +951,6 @@ static int lock_raw_ref(struct files_ref_store *refs,
        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;
-}
-
 static int files_peel_ref(struct ref_store *ref_store,
                          const char *refname, unsigned char *sha1)
 {
@@ -1935,7 +1066,6 @@ static struct ref_iterator *files_ref_iterator_begin(
                const char *prefix, unsigned int flags)
 {
        struct files_ref_store *refs;
-       struct ref_dir *loose_dir, *packed_dir;
        struct ref_iterator *loose_iter, *packed_iter;
        struct files_ref_iterator *iter;
        struct ref_iterator *ref_iterator;
@@ -1959,41 +1089,24 @@ static struct ref_iterator *files_ref_iterator_begin(
         * condition if loose refs are migrated to the packed-refs
         * file by a simultaneous process, but our in-memory view is
         * from before the migration. We ensure this as follows:
-        * First, we call prime_ref_dir(), which pre-reads the loose
-        * references for the subtree into the cache. (If they've
-        * already been read, that's OK; we only need to guarantee
-        * that they're read before the packed refs, not *how much*
-        * before.) After that, we call get_packed_ref_cache(), which
-        * internally checks whether the packed-ref cache is up to
-        * date with what is on disk, and re-reads it if not.
+        * First, we call start the loose refs iteration with its
+        * `prime_ref` argument set to true. This causes the loose
+        * references in the subtree to be pre-read into the cache.
+        * (If they've already been read, that's OK; we only need to
+        * guarantee that they're read before the packed refs, not
+        * *how much* before.) After that, we call
+        * get_packed_ref_cache(), which internally checks whether the
+        * packed-ref cache is up to date with what is on disk, and
+        * re-reads it if not.
         */
 
-       loose_dir = get_loose_refs(refs);
-
-       if (prefix && *prefix)
-               loose_dir = find_containing_dir(loose_dir, prefix, 0);
-
-       if (loose_dir) {
-               prime_ref_dir(loose_dir);
-               loose_iter = cache_ref_iterator_begin(loose_dir);
-       } else {
-               /* There's nothing to iterate over. */
-               loose_iter = empty_ref_iterator_begin();
-       }
+       loose_iter = cache_ref_iterator_begin(get_loose_ref_cache(refs),
+                                             prefix, 1);
 
        iter->packed_ref_cache = get_packed_ref_cache(refs);
        acquire_packed_ref_cache(iter->packed_ref_cache);
-       packed_dir = get_packed_ref_dir(iter->packed_ref_cache);
-
-       if (prefix && *prefix)
-               packed_dir = find_containing_dir(packed_dir, prefix, 0);
-
-       if (packed_dir) {
-               packed_iter = cache_ref_iterator_begin(packed_dir);
-       } else {
-               /* There's nothing to iterate over. */
-               packed_iter = empty_ref_iterator_begin();
-       }
+       packed_iter = cache_ref_iterator_begin(iter->packed_ref_cache->cache,
+                                              prefix, 0);
 
        iter->iter0 = overlay_ref_iterator_begin(loose_iter, packed_iter);
        iter->flags = flags;
@@ -2096,9 +1209,9 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
                 */
                if (remove_empty_directories(&ref_file)) {
                        last_errno = errno;
-                       if (!verify_refname_available_dir(
-                                           refname, extras, skip,
-                                           get_loose_refs(refs), err))
+                       if (!refs_verify_refname_available(
+                                           &refs->base,
+                                           refname, extras, skip, err))
                                strbuf_addf(err, "there are still refs under '%s'",
                                            refname);
                        goto error_return;
@@ -2110,9 +1223,8 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
        if (!resolved) {
                last_errno = errno;
                if (last_errno != ENOTDIR ||
-                   !verify_refname_available_dir(
-                                   refname, extras, skip,
-                                   get_loose_refs(refs), err))
+                   !refs_verify_refname_available(&refs->base, refname,
+                                                  extras, skip, err))
                        strbuf_addf(err, "unable to resolve reference '%s': %s",
                                    refname, strerror(last_errno));
 
@@ -2126,9 +1238,8 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
         * our refname.
         */
        if (is_null_oid(&lock->old_oid) &&
-           verify_refname_available_dir(refname, extras, skip,
-                                        get_packed_refs(refs),
-                                        err)) {
+           refs_verify_refname_available(&refs->base, refname,
+                                         extras, skip, err)) {
                last_errno = ENOTDIR;
                goto error_return;
        }
@@ -2163,30 +1274,15 @@ static struct ref_lock *lock_ref_sha1_basic(struct files_ref_store *refs,
  * 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)
+static void write_packed_entry(FILE *fh, const char *refname,
+                              const unsigned char *sha1,
+                              const 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
@@ -2232,9 +1328,10 @@ static int commit_packed_refs(struct files_ref_store *refs)
 {
        struct packed_ref_cache *packed_ref_cache =
                get_packed_ref_cache(refs);
-       int error = 0;
+       int ok, error = 0;
        int save_errno = 0;
        FILE *out;
+       struct ref_iterator *iter;
 
        files_assert_main_repository(refs, "commit_packed_refs");
 
@@ -2246,8 +1343,18 @@ static int commit_packed_refs(struct files_ref_store *refs)
                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);
+
+       iter = cache_ref_iterator_begin(packed_ref_cache->cache, NULL, 0);
+       while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+               struct object_id peeled;
+               int peel_error = ref_iterator_peel(iter, &peeled);
+
+               write_packed_entry(out, iter->refname, iter->oid->hash,
+                                  peel_error ? NULL : peeled.hash);
+       }
+
+       if (ok != ITER_DONE)
+               die("error while iterating over references");
 
        if (commit_lock_file(packed_ref_cache->lock)) {
                save_errno = errno;
@@ -2285,65 +1392,6 @@ struct ref_to_prune {
        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) || !entry_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)) {
-               struct ref_to_prune *n;
-               FLEX_ALLOC_STR(n, name, entry->name);
-               hashcpy(n->sha1, entry->u.value.oid.hash);
-               n->next = cb->ref_to_prune;
-               cb->ref_to_prune = n;
-       }
-       return 0;
-}
-
 enum {
        REMOVE_EMPTY_PARENTS_REF = 0x01,
        REMOVE_EMPTY_PARENTS_REFLOG = 0x02
@@ -2433,21 +1481,73 @@ static int files_pack_refs(struct ref_store *ref_store, unsigned int flags)
        struct files_ref_store *refs =
                files_downcast(ref_store, REF_STORE_WRITE | REF_STORE_ODB,
                               "pack_refs");
-       struct pack_refs_cb_data cbdata;
-
-       memset(&cbdata, 0, sizeof(cbdata));
-       cbdata.flags = flags;
+       struct ref_iterator *iter;
+       struct ref_dir *packed_refs;
+       int ok;
+       struct ref_to_prune *refs_to_prune = NULL;
 
        lock_packed_refs(refs, LOCK_DIE_ON_ERROR);
-       cbdata.packed_refs = get_packed_refs(refs);
+       packed_refs = get_packed_refs(refs);
+
+       iter = cache_ref_iterator_begin(get_loose_ref_cache(refs), NULL, 0);
+       while ((ok = ref_iterator_advance(iter)) == ITER_OK) {
+               /*
+                * 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 refs_to_prune.
+                */
+               struct ref_entry *packed_entry;
+               int is_tag_ref = starts_with(iter->refname, "refs/tags/");
 
-       do_for_each_entry_in_dir(get_loose_refs(refs), 0,
-                                pack_if_possible_fn, &cbdata);
+               /* Do not pack per-worktree refs: */
+               if (ref_type(iter->refname) != REF_TYPE_NORMAL)
+                       continue;
+
+               /* ALWAYS pack tags */
+               if (!(flags & PACK_REFS_ALL) && !is_tag_ref)
+                       continue;
+
+               /* Do not pack symbolic or broken refs: */
+               if (iter->flags & REF_ISSYMREF)
+                       continue;
+
+               if (!ref_resolves_to_object(iter->refname, iter->oid, iter->flags))
+                       continue;
+
+               /*
+                * Create an entry in the packed-refs cache equivalent
+                * to the one from the loose ref cache, except that
+                * we don't copy the peeled status, because we want it
+                * to be re-peeled.
+                */
+               packed_entry = find_ref_entry(packed_refs, iter->refname);
+               if (packed_entry) {
+                       /* Overwrite existing packed entry with info from loose entry */
+                       packed_entry->flag = REF_ISPACKED;
+                       oidcpy(&packed_entry->u.value.oid, iter->oid);
+               } else {
+                       packed_entry = create_ref_entry(iter->refname, iter->oid->hash,
+                                                       REF_ISPACKED, 0);
+                       add_ref_entry(packed_refs, packed_entry);
+               }
+               oidclr(&packed_entry->u.value.peeled);
+
+               /* Schedule the loose reference for pruning if requested. */
+               if ((flags & PACK_REFS_PRUNE)) {
+                       struct ref_to_prune *n;
+                       FLEX_ALLOC_STR(n, name, iter->refname);
+                       hashcpy(n->sha1, iter->oid->hash);
+                       n->next = refs_to_prune;
+                       refs_to_prune = n;
+               }
+       }
+       if (ok != ITER_DONE)
+               die("error while iterating over references");
 
        if (commit_packed_refs(refs))
                die_errno("unable to overwrite old ref-pack file");
 
-       prune_refs(refs, cbdata.ref_to_prune);
+       prune_refs(refs, refs_to_prune);
        return 0;
 }
 
@@ -2488,7 +1588,7 @@ static int repack_without_refs(struct files_ref_store *refs,
 
        /* Remove refnames from the cache */
        for_each_string_list_item(refname, refnames)
-               if (remove_entry(packed, refname->string) != -1)
+               if (remove_entry_from_dir(packed, refname->string) != -1)
                        removed = 1;
        if (!removed) {
                /*
@@ -2608,26 +1708,6 @@ static int rename_tmp_log(struct files_ref_store *refs, const char *newrefname)
        return ret;
 }
 
-static int files_verify_refname_available(struct ref_store *ref_store,
-                                         const char *newname,
-                                         const struct string_list *extras,
-                                         const struct string_list *skip,
-                                         struct strbuf *err)
-{
-       struct files_ref_store *refs =
-               files_downcast(ref_store, REF_STORE_READ, "verify_refname_available");
-       struct ref_dir *packed_refs = get_packed_refs(refs);
-       struct ref_dir *loose_refs = get_loose_refs(refs);
-
-       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 files_ref_store *refs,
@@ -4294,7 +3374,6 @@ struct ref_storage_be refs_be_files = {
 
        files_ref_iterator_begin,
        files_read_raw_ref,
-       files_verify_refname_available,
 
        files_reflog_iterator_begin,
        files_for_each_reflog_ent,
diff --git a/refs/ref-cache.c b/refs/ref-cache.c
new file mode 100644 (file)
index 0000000..6059362
--- /dev/null
@@ -0,0 +1,523 @@
+#include "../cache.h"
+#include "../refs.h"
+#include "refs-internal.h"
+#include "ref-cache.h"
+#include "../iterator.h"
+
+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;
+}
+
+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) {
+               if (!dir->cache->fill_ref_dir)
+                       die("BUG: incomplete ref_store without fill_ref_dir function");
+
+               dir->cache->fill_ref_dir(dir->cache->ref_store, dir, entry->name);
+               entry->flag &= ~REF_INCOMPLETE;
+       }
+       return dir;
+}
+
+struct ref_entry *create_ref_entry(const char *refname,
+                                  const unsigned char *sha1, int flag,
+                                  int check_name)
+{
+       struct ref_entry *ref;
+
+       if (check_name &&
+           check_refname_format(refname, REFNAME_ALLOW_ONELEVEL))
+               die("Reference has invalid format: '%s'", refname);
+       FLEX_ALLOC_STR(ref, name, refname);
+       hashcpy(ref->u.value.oid.hash, sha1);
+       oidclr(&ref->u.value.peeled);
+       ref->flag = flag;
+       return ref;
+}
+
+struct ref_cache *create_ref_cache(struct ref_store *refs,
+                                  fill_ref_dir_fn *fill_ref_dir)
+{
+       struct ref_cache *ret = xcalloc(1, sizeof(*ret));
+
+       ret->ref_store = refs;
+       ret->fill_ref_dir = fill_ref_dir;
+       ret->root = create_dir_entry(ret, "", 0, 1);
+       return ret;
+}
+
+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);
+}
+
+void free_ref_cache(struct ref_cache *cache)
+{
+       free_ref_entry(cache->root);
+       free(cache);
+}
+
+/*
+ * 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;
+}
+
+struct ref_entry *create_dir_entry(struct ref_cache *cache,
+                                  const char *dirname, size_t len,
+                                  int incomplete)
+{
+       struct ref_entry *direntry;
+
+       FLEX_ALLOC_MEM(direntry, name, dirname, len);
+       direntry->u.subdir.cache = 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];
+}
+
+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->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., it 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;
+}
+
+struct ref_entry *find_ref_entry(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;
+}
+
+int remove_entry_from_dir(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;
+}
+
+int add_ref_entry(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, 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;
+}
+
+/*
+ * Load all of the refs from `dir` (recursively) into our in-memory
+ * cache.
+ */
+static void prime_ref_dir(struct ref_dir *dir)
+{
+       /*
+        * 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.
+        */
+       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));
+       }
+}
+
+/*
+ * A level in the reference hierarchy that is currently being iterated
+ * through.
+ */
+struct cache_ref_iterator_level {
+       /*
+        * The ref_dir being iterated over at this level. The ref_dir
+        * is sorted before being stored here.
+        */
+       struct ref_dir *dir;
+
+       /*
+        * The index of the current entry within dir (which might
+        * itself be a directory). If index == -1, then the iteration
+        * hasn't yet begun. If index == dir->nr, then the iteration
+        * through this level is over.
+        */
+       int index;
+};
+
+/*
+ * Represent an iteration through a ref_dir in the memory cache. The
+ * iteration recurses through subdirectories.
+ */
+struct cache_ref_iterator {
+       struct ref_iterator base;
+
+       /*
+        * The number of levels currently on the stack. This is always
+        * at least 1, because when it becomes zero the iteration is
+        * ended and this struct is freed.
+        */
+       size_t levels_nr;
+
+       /* The number of levels that have been allocated on the stack */
+       size_t levels_alloc;
+
+       /*
+        * A stack of levels. levels[0] is the uppermost level that is
+        * being iterated over in this iteration. (This is not
+        * necessary the top level in the references hierarchy. If we
+        * are iterating through a subtree, then levels[0] will hold
+        * the ref_dir for that subtree, and subsequent levels will go
+        * on from there.)
+        */
+       struct cache_ref_iterator_level *levels;
+};
+
+static int cache_ref_iterator_advance(struct ref_iterator *ref_iterator)
+{
+       struct cache_ref_iterator *iter =
+               (struct cache_ref_iterator *)ref_iterator;
+
+       while (1) {
+               struct cache_ref_iterator_level *level =
+                       &iter->levels[iter->levels_nr - 1];
+               struct ref_dir *dir = level->dir;
+               struct ref_entry *entry;
+
+               if (level->index == -1)
+                       sort_ref_dir(dir);
+
+               if (++level->index == level->dir->nr) {
+                       /* This level is exhausted; pop up a level */
+                       if (--iter->levels_nr == 0)
+                               return ref_iterator_abort(ref_iterator);
+
+                       continue;
+               }
+
+               entry = dir->entries[level->index];
+
+               if (entry->flag & REF_DIR) {
+                       /* push down a level */
+                       ALLOC_GROW(iter->levels, iter->levels_nr + 1,
+                                  iter->levels_alloc);
+
+                       level = &iter->levels[iter->levels_nr++];
+                       level->dir = get_ref_dir(entry);
+                       level->index = -1;
+               } else {
+                       iter->base.refname = entry->name;
+                       iter->base.oid = &entry->u.value.oid;
+                       iter->base.flags = entry->flag;
+                       return ITER_OK;
+               }
+       }
+}
+
+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;
+}
+
+static int cache_ref_iterator_peel(struct ref_iterator *ref_iterator,
+                                  struct object_id *peeled)
+{
+       struct cache_ref_iterator *iter =
+               (struct cache_ref_iterator *)ref_iterator;
+       struct cache_ref_iterator_level *level;
+       struct ref_entry *entry;
+
+       level = &iter->levels[iter->levels_nr - 1];
+
+       if (level->index == -1)
+               die("BUG: peel called before advance for cache iterator");
+
+       entry = level->dir->entries[level->index];
+
+       if (peel_entry(entry, 0))
+               return -1;
+       oidcpy(peeled, &entry->u.value.peeled);
+       return 0;
+}
+
+static int cache_ref_iterator_abort(struct ref_iterator *ref_iterator)
+{
+       struct cache_ref_iterator *iter =
+               (struct cache_ref_iterator *)ref_iterator;
+
+       free(iter->levels);
+       base_ref_iterator_free(ref_iterator);
+       return ITER_DONE;
+}
+
+static struct ref_iterator_vtable cache_ref_iterator_vtable = {
+       cache_ref_iterator_advance,
+       cache_ref_iterator_peel,
+       cache_ref_iterator_abort
+};
+
+struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
+                                             const char *prefix,
+                                             int prime_dir)
+{
+       struct ref_dir *dir;
+       struct cache_ref_iterator *iter;
+       struct ref_iterator *ref_iterator;
+       struct cache_ref_iterator_level *level;
+
+       dir = get_ref_dir(cache->root);
+       if (prefix && *prefix)
+               dir = find_containing_dir(dir, prefix, 0);
+       if (!dir)
+               /* There's nothing to iterate over. */
+               return  empty_ref_iterator_begin();
+
+       if (prime_dir)
+               prime_ref_dir(dir);
+
+       iter = xcalloc(1, sizeof(*iter));
+       ref_iterator = &iter->base;
+       base_ref_iterator_init(ref_iterator, &cache_ref_iterator_vtable);
+       ALLOC_GROW(iter->levels, 10, iter->levels_alloc);
+
+       iter->levels_nr = 1;
+       level = &iter->levels[0];
+       level->index = -1;
+       level->dir = dir;
+
+       if (prefix && *prefix)
+               ref_iterator = prefix_ref_iterator_begin(ref_iterator,
+                                                        prefix, 0);
+
+       return ref_iterator;
+}
diff --git a/refs/ref-cache.h b/refs/ref-cache.h
new file mode 100644 (file)
index 0000000..ffdc54f
--- /dev/null
@@ -0,0 +1,267 @@
+#ifndef REFS_REF_CACHE_H
+#define REFS_REF_CACHE_H
+
+struct ref_dir;
+
+/*
+ * If this ref_cache is filled lazily, this function is used to load
+ * information into the specified ref_dir (shallow or deep, at the
+ * option of the ref_store). dirname includes a trailing slash.
+ */
+typedef void fill_ref_dir_fn(struct ref_store *ref_store,
+                            struct ref_dir *dir, const char *dirname);
+
+struct ref_cache {
+       struct ref_entry *root;
+
+       /* A pointer to the ref_store whose cache this is: */
+       struct ref_store *ref_store;
+
+       /*
+        * Function used (if necessary) to lazily-fill cache. May be
+        * NULL.
+        */
+       fill_ref_dir_fn *fill_ref_dir;
+};
+
+/*
+ * 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;
+};
+
+/*
+ * 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;
+
+       /* The ref_cache containing this entry: */
+       struct ref_cache *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 refs_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];
+};
+
+/*
+ * 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.
+ */
+int search_ref_dir(struct ref_dir *dir, const char *refname, size_t len);
+
+struct ref_dir *get_ref_dir(struct ref_entry *entry);
+
+/*
+ * 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.
+ */
+struct ref_entry *create_dir_entry(struct ref_cache *cache,
+                                  const char *dirname, size_t len,
+                                  int incomplete);
+
+struct ref_entry *create_ref_entry(const char *refname,
+                                  const unsigned char *sha1, int flag,
+                                  int check_name);
+
+/*
+ * Return a pointer to a new `ref_cache`. Its top-level starts out
+ * marked incomplete. If `fill_ref_dir` is non-NULL, it is the
+ * function called to fill in incomplete directories in the
+ * `ref_cache` when they are accessed. If it is NULL, then the whole
+ * `ref_cache` must be filled (including clearing its directories'
+ * `REF_INCOMPLETE` bits) before it is used.
+ */
+struct ref_cache *create_ref_cache(struct ref_store *refs,
+                                  fill_ref_dir_fn *fill_ref_dir);
+
+/*
+ * Free the `ref_cache` and all of its associated data.
+ */
+void free_ref_cache(struct ref_cache *cache);
+
+/*
+ * Add a ref_entry to the end of dir (unsorted).  Entry is always
+ * stored directly in dir; no recursion into subdirectories is
+ * done.
+ */
+void add_entry_to_dir(struct ref_dir *dir, struct ref_entry *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.
+ */
+int remove_entry_from_dir(struct ref_dir *dir, const char *refname);
+
+/*
+ * 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.
+ */
+int add_ref_entry(struct ref_dir *dir, struct ref_entry *ref);
+
+/*
+ * 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.
+ */
+struct ref_entry *find_ref_entry(struct ref_dir *dir, const char *refname);
+
+/*
+ * Start iterating over references in `cache`. If `prefix` is
+ * specified, only include references whose names start with that
+ * prefix. If `prime_dir` is true, then fill any incomplete
+ * directories before beginning the iteration.
+ */
+struct ref_iterator *cache_ref_iterator_begin(struct ref_cache *cache,
+                                             const char *prefix,
+                                             int prime_dir);
+
+/*
+ * 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.
+ */
+enum peel_status peel_entry(struct ref_entry *entry, int repeel);
+
+#endif /* REFS_REF_CACHE_H */
index 690498698e41b75cf7a194ac03cafd778f0f19f1..3d46131efbcc0a76cc3054a97a669bc5bdd036e7 100644 (file)
@@ -165,6 +165,10 @@ struct ref_update {
        const char refname[FLEX_ARRAY];
 };
 
+int refs_read_raw_ref(struct ref_store *ref_store,
+                     const char *refname, unsigned char *sha1,
+                     struct strbuf *referent, unsigned int *type);
+
 /*
  * Add a ref_update with the specified properties to transaction, and
  * return a pointer to the new object. This function does not verify
@@ -331,6 +335,17 @@ struct ref_iterator *empty_ref_iterator_begin(void);
  */
 int is_empty_ref_iterator(struct ref_iterator *ref_iterator);
 
+/*
+ * Return an iterator that goes over each reference in `refs` for
+ * which the refname begins with prefix. If trim is non-zero, then
+ * trim that many characters off the beginning of each refname. flags
+ * can be DO_FOR_EACH_INCLUDE_BROKEN to include broken references in
+ * the iteration.
+ */
+struct ref_iterator *refs_ref_iterator_begin(
+               struct ref_store *refs,
+               const char *prefix, int trim, int flags);
+
 /*
  * A callback function used to instruct merge_ref_iterator how to
  * interleave the entries from iter0 and iter1. The function should
@@ -575,12 +590,6 @@ typedef int read_raw_ref_fn(struct ref_store *ref_store,
                            const char *refname, unsigned char *sha1,
                            struct strbuf *referent, unsigned int *type);
 
-typedef int verify_refname_available_fn(struct ref_store *ref_store,
-                                       const char *newname,
-                                       const struct string_list *extras,
-                                       const struct string_list *skip,
-                                       struct strbuf *err);
-
 struct ref_storage_be {
        struct ref_storage_be *next;
        const char *name;
@@ -597,7 +606,6 @@ struct ref_storage_be {
 
        ref_iterator_begin_fn *iterator_begin;
        read_raw_ref_fn *read_raw_ref;
-       verify_refname_available_fn *verify_refname_available;
 
        reflog_iterator_begin_fn *reflog_iterator_begin;
        for_each_reflog_ent_fn *for_each_reflog_ent;
index 77afecaebf0980bec4f938b15bd08ae35ba09d83..130cc868e511e646badde9ebc0894602e27ea37b 100644 (file)
@@ -1065,12 +1065,12 @@ static int do_pick_commit(enum todo_command command, struct commit *commit,
                        flags |= CLEANUP_MSG;
                        msg_file = rebase_path_fixup_msg();
                } else {
-                       const char *dest = git_path("SQUASH_MSG");
+                       const char *dest = git_path_squash_msg();
                        unlink(dest);
                        if (copy_file(dest, rebase_path_squash_msg(), 0666))
                                return error(_("could not rename '%s' to '%s'"),
                                             rebase_path_squash_msg(), dest);
-                       unlink(git_path("MERGE_MSG"));
+                       unlink(git_path_merge_msg());
                        msg_file = dest;
                        flags |= EDIT_MSG;
                }
@@ -1820,10 +1820,10 @@ static int error_failed_squash(struct commit *commit,
                return error(_("could not rename '%s' to '%s'"),
                        rebase_path_squash_msg(), rebase_path_message());
        unlink(rebase_path_fixup_msg());
-       unlink(git_path("MERGE_MSG"));
-       if (copy_file(git_path("MERGE_MSG"), rebase_path_message(), 0666))
+       unlink(git_path_merge_msg());
+       if (copy_file(git_path_merge_msg(), rebase_path_message(), 0666))
                return error(_("could not copy '%s' to '%s'"),
-                            rebase_path_message(), git_path("MERGE_MSG"));
+                            rebase_path_message(), git_path_merge_msg());
        return error_with_patch(commit, subject, subject_len, opts, 1, 0);
 }
 
@@ -2167,7 +2167,7 @@ static int commit_staged_changes(struct replay_opts *opts)
        if (has_unstaged_changes(1))
                return error(_("cannot rebase: You have unstaged changes."));
        if (!has_uncommitted_changes(0)) {
-               const char *cherry_pick_head = git_path("CHERRY_PICK_HEAD");
+               const char *cherry_pick_head = git_path_cherry_pick_head();
 
                if (file_exists(cherry_pick_head) && unlink(cherry_pick_head))
                        return error(_("could not remove CHERRY_PICK_HEAD"));
index acd5db180f31f5b81877c75f8a695c3e4437e34d..721650256e63eb51c66d5985c04bc3095f183c1c 100644 (file)
@@ -28,6 +28,7 @@
 /test-sha1
 /test-sha1-array
 /test-sigchain
+/test-strcmp-offset
 /test-string-list
 /test-submodule-config
 /test-subprocess
diff --git a/t/helper/test-strcmp-offset.c b/t/helper/test-strcmp-offset.c
new file mode 100644 (file)
index 0000000..4a45a54
--- /dev/null
@@ -0,0 +1,22 @@
+#include "cache.h"
+
+int cmd_main(int argc, const char **argv)
+{
+       int result;
+       size_t offset;
+
+       if (!argv[1] || !argv[2])
+               die("usage: %s <string1> <string2>", argv[0]);
+
+       result = strcmp_offset(argv[1], argv[2], &offset);
+
+       /*
+        * Because differnt CRTs behave differently, only rely on signs
+        * of the result values.
+        */
+       result = (result < 0 ? -1 :
+                         result > 0 ? 1 :
+                         0);
+       printf("%d %"PRIuMAX"\n", result, (uintmax_t)offset);
+       return 0;
+}
index 924b19dab4122a36112bd908a6252e754ecc531a..1dbc85b21407dcfc83d9dc6a56e8e0f21c748c0c 100755 (executable)
@@ -88,6 +88,7 @@ sub format_times {
 sub read_descr {
        my $name = shift;
        open my $fh, "<", $name or return "<error reading description>";
+       binmode $fh, ":utf8" or die "PANIC on binmode: $!";
        my $line = <$fh>;
        close $fh or die "cannot close $name";
        chomp $line;
@@ -147,6 +148,8 @@ sub have_slash {
 my $totalwidth = 3*@dirs+$descrlen;
 $totalwidth += $_ for (@colwidth);
 
+binmode STDOUT, ":utf8" or die "PANIC on binmode: $!";
+
 printf "%-${descrlen}s", "Test";
 for my $i (0..$#dirs) {
        my $d = $dirs[$i];
index cf8e1efce7cd29679c3ffdb2fa7d03489c38c1c9..002c21e52a6763ee14ac243b2b3a1af242fc2834 100755 (executable)
@@ -33,6 +33,8 @@ test_perf 'export a weird var' '
        test_export bar
 '
 
+test_perf 'éḿíẗ ńöń-ÁŚĆÍÍ ćḧáŕáćẗéŕś' 'true'
+
 test_expect_success 'test_export works with weird vars' '
        echo "$bar" &&
        test "$bar" = "weird # variable"
diff --git a/t/perf/p0006-read-tree-checkout.sh b/t/perf/p0006-read-tree-checkout.sh
new file mode 100755 (executable)
index 0000000..78cc23f
--- /dev/null
@@ -0,0 +1,67 @@
+#!/bin/sh
+#
+# This test measures the performance of various read-tree
+# and checkout operations.  It is primarily interested in
+# the algorithmic costs of index operations and recursive
+# tree traversal -- and NOT disk I/O on thousands of files.
+
+test_description="Tests performance of read-tree"
+
+. ./perf-lib.sh
+
+test_perf_default_repo
+
+# If the test repo was generated by ./repos/many-files.sh
+# then we know something about the data shape and branches,
+# so we can isolate testing to the ballast-related commits
+# and setup sparse-checkout so we don't have to populate
+# the ballast files and directories.
+#
+# Otherwise, we make some general assumptions about the
+# repo and consider the entire history of the current
+# branch to be the ballast.
+
+test_expect_success "setup repo" '
+       if git rev-parse --verify refs/heads/p0006-ballast^{commit}
+       then
+               echo Assuming synthetic repo from many-files.sh
+               git branch br_base            master
+               git branch br_ballast         p0006-ballast^
+               git branch br_ballast_alias   p0006-ballast^
+               git branch br_ballast_plus_1  p0006-ballast
+               git config --local core.sparsecheckout 1
+               cat >.git/info/sparse-checkout <<-EOF
+               /*
+               !ballast/*
+               EOF
+       else
+               echo Assuming non-synthetic repo...
+               git branch br_base            $(git rev-list HEAD | tail -n 1)
+               git branch br_ballast         HEAD^ || error "no ancestor commit from current head"
+               git branch br_ballast_alias   HEAD^
+               git branch br_ballast_plus_1  HEAD
+       fi &&
+       git checkout -q br_ballast &&
+       nr_files=$(git ls-files | wc -l)
+'
+
+test_perf "read-tree br_base br_ballast ($nr_files)" '
+       git read-tree -m br_base br_ballast -n
+'
+
+test_perf "switch between br_base br_ballast ($nr_files)" '
+       git checkout -q br_base &&
+       git checkout -q br_ballast
+'
+
+test_perf "switch between br_ballast br_ballast_plus_1 ($nr_files)" '
+       git checkout -q br_ballast_plus_1 &&
+       git checkout -q br_ballast
+'
+
+test_perf "switch between aliases ($nr_files)" '
+       git checkout -q br_ballast_alias &&
+       git checkout -q br_ballast
+'
+
+test_done
diff --git a/t/perf/repos/.gitignore b/t/perf/repos/.gitignore
new file mode 100644 (file)
index 0000000..72e3dc3
--- /dev/null
@@ -0,0 +1 @@
+gen-*/
diff --git a/t/perf/repos/inflate-repo.sh b/t/perf/repos/inflate-repo.sh
new file mode 100755 (executable)
index 0000000..fcfc992
--- /dev/null
@@ -0,0 +1,85 @@
+#!/bin/sh
+# Inflate the size of an EXISTING repo.
+#
+# This script should be run inside the worktree of a TEST repo.
+# It will use the contents of the current HEAD to generate a
+# commit containing copies of the current worktree such that the
+# total size of the commit has at least <target_size> files.
+#
+# Usage: [-t target_size] [-b branch_name]
+
+set -e
+
+target_size=10000
+branch_name=p0006-ballast
+ballast=ballast
+
+while test "$#" -ne 0
+do
+    case "$1" in
+       -b)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -b requires an argument' >&2; exit 1; }
+           branch_name=$1;
+           shift ;;
+       -t)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -t requires an argument' >&2; exit 1; }
+           target_size=$1;
+           shift ;;
+       *)
+           echo "error: unknown option '$1'" >&2; exit 1 ;;
+    esac
+done
+
+git ls-tree -r HEAD >GEN_src_list
+nr_src_files=$(cat GEN_src_list | wc -l)
+
+src_branch=$(git symbolic-ref --short HEAD)
+
+echo "Branch $src_branch initially has $nr_src_files files."
+
+if test $target_size -le $nr_src_files
+then
+    echo "Repository already exceeds target size $target_size."
+    rm GEN_src_list
+    exit 1
+fi
+
+# Create well-known branch and add 1 file change to start
+# if off before the ballast.
+git checkout -b $branch_name HEAD
+echo "$target_size" > inflate-repo.params
+git add inflate-repo.params
+git commit -q -m params
+
+# Create ballast for in our branch.
+copy=1
+nr_files=$nr_src_files
+while test $nr_files -lt $target_size
+do
+    sed -e "s| |       $ballast/$copy/|" <GEN_src_list |
+       git update-index --index-info
+
+    nr_files=$(expr $nr_files + $nr_src_files)
+    copy=$(expr $copy + 1)
+done
+rm GEN_src_list
+git commit -q -m "ballast"
+
+# Modify 1 file and commit.
+echo "$target_size" >> inflate-repo.params
+git add inflate-repo.params
+git commit -q -m "ballast plus 1"
+
+nr_files=$(git ls-files | wc -l)
+
+# Checkout master to put repo in canonical state (because
+# the perf test may need to clone and enable sparse-checkout
+# before attempting to checkout a commit with the ballast
+# (because it may contain 100K directories and 1M files)).
+git checkout $src_branch
+
+echo "Repository inflated. Branch $branch_name has $nr_files files."
+
+exit 0
diff --git a/t/perf/repos/many-files.sh b/t/perf/repos/many-files.sh
new file mode 100755 (executable)
index 0000000..28720e4
--- /dev/null
@@ -0,0 +1,110 @@
+#!/bin/sh
+# Generate test data repository using the given parameters.
+# When omitted, we create "gen-many-files-d-w-f.git".
+#
+# Usage: [-r repo] [-d depth] [-w width] [-f files]
+#
+# -r repo: path to the new repo to be generated
+# -d depth: the depth of sub-directories
+# -w width: the number of sub-directories at each level
+# -f files: the number of files created in each directory
+#
+# Note that all files will have the same SHA-1 and each
+# directory at a level will have the same SHA-1, so we
+# will potentially have a large index, but not a large
+# ODB.
+#
+# Ballast will be created under "ballast/".
+
+EMPTY_BLOB=e69de29bb2d1d6434b8b29ae775ad8c2e48c5391
+
+set -e
+
+# (5, 10, 9) will create 999,999 ballast files.
+# (4, 10, 9) will create  99,999 ballast files.
+depth=5
+width=10
+files=9
+
+while test "$#" -ne 0
+do
+    case "$1" in
+       -r)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -r requires an argument' >&2; exit 1; }
+           repo=$1;
+           shift ;;
+       -d)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -d requires an argument' >&2; exit 1; }
+           depth=$1;
+           shift ;;
+       -w)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -w requires an argument' >&2; exit 1; }
+           width=$1;
+           shift ;;
+       -f)
+           shift;
+           test "$#" -ne 0 || { echo 'error: -f requires an argument' >&2; exit 1; }
+           files=$1;
+           shift ;;
+       *)
+           echo "error: unknown option '$1'" >&2; exit 1 ;;
+       esac
+done
+
+# Inflate the index with thousands of empty files.
+# usage: dir depth width files
+fill_index() {
+       awk -v arg_dir=$1 -v arg_depth=$2 -v arg_width=$3 -v arg_files=$4 '
+               function make_paths(dir, depth, width, files, f, w) {
+                       for (f = 1; f <= files; f++) {
+                               print dir "/file" f
+                       }
+                       if (depth > 0) {
+                               for (w = 1; w <= width; w++) {
+                                       make_paths(dir "/dir" w, depth - 1, width, files)
+                               }
+                       }
+               }
+               END { make_paths(arg_dir, arg_depth, arg_width, arg_files) }
+               ' </dev/null |
+       sed "s/^/100644 $EMPTY_BLOB     /" |
+       git update-index --index-info
+       return 0
+}
+
+[ -z "$repo" ] && repo=gen-many-files-$depth.$width.$files.git
+
+mkdir $repo
+cd $repo
+git init .
+
+# Create an initial commit just to define master.
+touch many-files.empty
+echo "$depth $width $files" >many-files.params
+git add many-files.*
+git commit -q -m params
+
+# Create ballast for p0006 based upon the given params and
+# inflate the index with thousands of empty files and commit.
+git checkout -b p0006-ballast
+fill_index "ballast" $depth $width $files
+git commit -q -m "ballast"
+
+nr_files=$(git ls-files | wc -l)
+
+# Modify 1 file and commit.
+echo "$depth $width $files" >>many-files.params
+git add many-files.params
+git commit -q -m "ballast plus 1"
+
+# Checkout master to put repo in canonical state (because
+# the perf test may need to clone and enable sparse-checkout
+# before attempting to checkout a commit with the ballast
+# (because it may contain 100K directories and 1M files)).
+git checkout master
+
+echo "Repository "$repo" ($depth, $width, $files) created.  Ballast $nr_files."
+exit 0
diff --git a/t/t0065-strcmp-offset.sh b/t/t0065-strcmp-offset.sh
new file mode 100755 (executable)
index 0000000..7d6d214
--- /dev/null
@@ -0,0 +1,21 @@
+#!/bin/sh
+
+test_description='Test strcmp_offset functionality'
+
+. ./test-lib.sh
+
+while read s1 s2 expect
+do
+       test_expect_success "strcmp_offset($s1, $s2)" '
+               echo "$expect" >expect &&
+               test-strcmp-offset "$s1" "$s2" >actual &&
+               test_cmp expect actual
+       '
+done <<-EOF
+abc abc 0 3
+abc def -1 0
+abc abz -1 2
+abc abcdef -1 3
+EOF
+
+test_done
index 8fbc7a029f79b1fd9ffae2fa6183ebdd4bbbb923..933915ec06ec725f7cf6a9ed3e251f6ff2390f76 100755 (executable)
@@ -218,6 +218,17 @@ test_expect_success 'conditional include, both unanchored, icase' '
        )
 '
 
+test_expect_success 'conditional include, early config reading' '
+       (
+               cd foo &&
+               echo "[includeIf \"gitdir:foo/\"]path=bar6" >>.git/config &&
+               echo "[test]six=6" >.git/bar6 &&
+               echo 6 >expect &&
+               test-config read_early_config test.six >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_expect_success SYMLINKS 'conditional include, set up symlinked $HOME' '
        mkdir real-home &&
        ln -s real-home home &&
index b97357b8ab3aa47aff100e4012083891b57d9b46..1af8c454cfa715d5694c16b4848428ae0b75eda6 100755 (executable)
@@ -47,6 +47,24 @@ test_expect_success 'ceiling #2' '
        test xdg = "$(cat output)"
 '
 
+cmdline_config="'test.source=cmdline'"
+test_expect_success 'read config file in right order' '
+       echo "[test]source = home" >>.gitconfig &&
+       git init foo &&
+       (
+               cd foo &&
+               echo "[test]source = repo" >>.git/config &&
+               GIT_CONFIG_PARAMETERS=$cmdline_config test-config \
+                       read_early_config test.source >actual &&
+               cat >expected <<-\EOF &&
+               home
+               repo
+               cmdline
+               EOF
+               test_cmp expected actual
+       )
+'
+
 test_with_config () {
        rm -rf throwaway &&
        git init throwaway &&
index b618d6be21ff8926cb0d9dff1881c6e5e9eeb35a..b5c47ac602d65ebfb0f4baebc098f26d5d1a464c 100755 (executable)
@@ -63,6 +63,12 @@ test_expect_success '"add" worktree' '
        )
 '
 
+test_expect_success '"add" worktree with lock' '
+       git rev-parse HEAD >expect &&
+       git worktree add --detach --lock here-with-lock master &&
+       test -f .git/worktrees/here-with-lock/locked
+'
+
 test_expect_success '"add" worktree from a subdir' '
        (
                mkdir sub &&
index 848da5f3684ac5382063a64588c49a9128235cf1..720063bf0d09bb8c577b78e099187abfc7da71b0 100755 (executable)
@@ -20,7 +20,7 @@ test_expect_success 'rev-parse --git-common-dir on main worktree' '
 
 test_expect_success 'rev-parse --git-path objects linked worktree' '
        echo "$(git rev-parse --show-toplevel)/.git/objects" >expect &&
-       test_when_finished "rm -rf linked-tree && git worktree prune" &&
+       test_when_finished "rm -rf linked-tree actual expect && git worktree prune" &&
        git worktree add --detach linked-tree master &&
        git -C linked-tree rev-parse --git-path objects >actual &&
        test_cmp expect actual
@@ -28,19 +28,21 @@ test_expect_success 'rev-parse --git-path objects linked worktree' '
 
 test_expect_success '"list" all worktrees from main' '
        echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here out actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git worktree list | sed "s/  */ /g" >actual &&
+       git worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
 test_expect_success '"list" all worktrees from linked' '
        echo "$(git rev-parse --show-toplevel) $(git rev-parse --short HEAD) [$(git symbolic-ref --short HEAD)]" >expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here out actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "$(git -C here rev-parse --show-toplevel) $(git rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C here worktree list | sed "s/  */ /g" >actual &&
+       git -C here worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
@@ -49,7 +51,7 @@ test_expect_success '"list" all worktrees --porcelain' '
        echo "HEAD $(git rev-parse HEAD)" >>expect &&
        echo "branch $(git symbolic-ref HEAD)" >>expect &&
        echo >>expect &&
-       test_when_finished "rm -rf here && git worktree prune" &&
+       test_when_finished "rm -rf here actual expect && git worktree prune" &&
        git worktree add --detach here master &&
        echo "worktree $(git -C here rev-parse --show-toplevel)" >>expect &&
        echo "HEAD $(git rev-parse HEAD)" >>expect &&
@@ -69,16 +71,17 @@ test_expect_success 'bare repo setup' '
 '
 
 test_expect_success '"list" all worktrees from bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "$(pwd)/bare1 (bare)" >expect &&
        echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C bare1 worktree list | sed "s/  */ /g" >actual &&
+       git -C bare1 worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
 test_expect_success '"list" all worktrees --porcelain from bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "worktree $(pwd)/bare1" >expect &&
        echo "bare" >>expect &&
@@ -92,11 +95,12 @@ test_expect_success '"list" all worktrees --porcelain from bare main' '
 '
 
 test_expect_success '"list" all worktrees from linked with a bare main' '
-       test_when_finished "rm -rf there && git -C bare1 worktree prune" &&
+       test_when_finished "rm -rf there out actual expect && git -C bare1 worktree prune" &&
        git -C bare1 worktree add --detach ../there master &&
        echo "$(pwd)/bare1 (bare)" >expect &&
        echo "$(git -C there rev-parse --show-toplevel) $(git -C there rev-parse --short HEAD) (detached HEAD)" >>expect &&
-       git -C there worktree list | sed "s/  */ /g" >actual &&
+       git -C there worktree list >out &&
+       sed "s/  */ /g" <out >actual &&
        test_cmp expect actual
 '
 
@@ -118,9 +122,11 @@ test_expect_success 'broken main worktree still at the top' '
                cd linked &&
                echo "worktree $(pwd)" >expected &&
                echo "ref: .broken" >../.git/HEAD &&
-               git worktree list --porcelain | head -n 3 >actual &&
+               git worktree list --porcelain >out &&
+               head -n 3 out >actual &&
                test_cmp ../expected actual &&
-               git worktree list | head -n 1 >actual.2 &&
+               git worktree list >out &&
+               head -n 1 out >actual.2 &&
                grep -F "(error)" actual.2
        )
 '
@@ -134,7 +140,8 @@ test_expect_success 'linked worktrees are sorted' '
                test_commit new &&
                git worktree add ../first &&
                git worktree add ../second &&
-               git worktree list --porcelain | grep ^worktree >actual
+               git worktree list --porcelain >out &&
+               grep ^worktree out >actual
        ) &&
        cat >expected <<-EOF &&
        worktree $(pwd)/sorted/main
diff --git a/t/t3428-rebase-signoff.sh b/t/t3428-rebase-signoff.sh
new file mode 100755 (executable)
index 0000000..2afb564
--- /dev/null
@@ -0,0 +1,46 @@
+#!/bin/sh
+
+test_description='git rebase --signoff
+
+This test runs git rebase --signoff and make sure that it works.
+'
+
+. ./test-lib.sh
+
+# A simple file to commit
+cat >file <<EOF
+a
+EOF
+
+# Expected commit message after rebase --signoff
+cat >expected-signed <<EOF
+first
+
+Signed-off-by: $(git var GIT_COMMITTER_IDENT | sed -e "s/>.*/>/")
+EOF
+
+# Expected commit message after rebase without --signoff (or with --no-signoff)
+cat >expected-unsigned <<EOF
+first
+EOF
+
+
+# We configure an alias to do the rebase --signoff so that
+# on the next subtest we can show that --no-signoff overrides the alias
+test_expect_success 'rebase --signoff adds a sign-off line' '
+       git commit --allow-empty -m "Initial empty commit" &&
+       git add file && git commit -m first &&
+       git config alias.rbs "rebase --signoff" &&
+       git rbs HEAD^ &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       test_cmp expected-signed actual
+'
+
+test_expect_success 'rebase --no-signoff does not add a sign-off line' '
+       git commit --amend -m "first" &&
+       git rbs --no-signoff HEAD^ &&
+       git cat-file commit HEAD | sed -e "1,/^\$/d" > actual &&
+       test_cmp expected-unsigned actual
+'
+
+test_done
index f9528fa00c4ad382a99460fbf5b2c9b7a96ec34b..2ecb43a61652032862d5a264ede8af473ef345e1 100755 (executable)
@@ -436,6 +436,28 @@ test_expect_success 'add -p handles globs' '
        test_cmp expect actual
 '
 
+test_expect_success 'add -p handles relative paths' '
+       git reset --hard &&
+
+       echo base >relpath.c &&
+       git add "*.c" &&
+       git commit -m relpath &&
+
+       echo change >relpath.c &&
+       mkdir -p subdir &&
+       git -C subdir add -p .. 2>error <<-\EOF &&
+       y
+       EOF
+
+       test_must_be_empty error &&
+
+       cat >expect <<-\EOF &&
+       relpath.c
+       EOF
+       git diff --cached --name-only >actual &&
+       test_cmp expect actual
+'
+
 test_expect_success 'add -p does not expand argument lists' '
        git reset --hard &&
 
index a2c9e7439f362d8f247c369ba515a55b26dee8ec..d38ecee2172a6e5599cfbeb18e472816e98e553c 100755 (executable)
@@ -229,4 +229,33 @@ test_expect_success 'new branch already exists' '
        )
 '
 
+test_expect_success 'background updates of REMOTE can be mitigated with a non-updated REMOTE-push' '
+       rm -rf src dst &&
+       git init --bare src.bare &&
+       test_when_finished "rm -rf src.bare" &&
+       git clone --no-local src.bare dst &&
+       test_when_finished "rm -rf dst" &&
+       (
+               cd dst &&
+               test_commit G &&
+               git remote add origin-push ../src.bare &&
+               git push origin-push master:master
+       ) &&
+       git clone --no-local src.bare dst2 &&
+       test_when_finished "rm -rf dst2" &&
+       (
+               cd dst2 &&
+               test_commit H &&
+               git push
+       ) &&
+       (
+               cd dst &&
+               test_commit I &&
+               git fetch origin &&
+               test_must_fail git push --force-with-lease origin-push &&
+               git fetch origin-push &&
+               git push --force-with-lease origin-push
+       )
+'
+
 test_done
index c2706fe4723778b68b9673fd85267ea475f791f2..1b8f1dbd3a3d8a59a74888155d36d0d49e94cb64 100755 (executable)
@@ -38,6 +38,14 @@ test_expect_success 'submodule update aborts on missing .gitmodules file' '
        test_i18ngrep "Submodule path .sub. not initialized" actual
 '
 
+test_expect_success 'submodule update aborts on missing gitmodules url' '
+       test_when_finished "git update-index --remove sub" &&
+       git update-index --add --cacheinfo 160000,$(git rev-parse HEAD),sub &&
+       test_when_finished "rm -f .gitmodules" &&
+       git config -f .gitmodules submodule.s.path sub &&
+       test_must_fail git submodule init
+'
+
 test_expect_success 'configuration parsing' '
        test_when_finished "rm -f .gitmodules" &&
        cat >.gitmodules <<-\EOF &&
index 5ed28135bea13a6c4082413044d8dcb205449c38..2cb999ecfab69eb7825ad05c79a874114f921716 100755 (executable)
@@ -1457,4 +1457,38 @@ test_expect_failure 'complete with tilde expansion' '
        test_completion "git add ~/tmp/" "~/tmp/file"
 '
 
+test_expect_success 'setup other remote for remote reference completion' '
+       git remote add other otherrepo &&
+       git fetch other
+'
+
+for flag in -d --delete
+do
+       test_expect_success "__git_complete_remote_or_refspec - push $flag other" '
+               sed -e "s/Z$//" >expected <<-EOF &&
+               master-in-other Z
+               EOF
+               (
+                       words=(git push '$flag' other ma) &&
+                       cword=${#words[@]} cur=${words[cword-1]} &&
+                       __git_complete_remote_or_refspec &&
+                       print_comp
+               ) &&
+               test_cmp expected out
+       '
+
+       test_expect_failure "__git_complete_remote_or_refspec - push other $flag" '
+               sed -e "s/Z$//" >expected <<-EOF &&
+               master-in-other Z
+               EOF
+               (
+                       words=(git push other '$flag' ma) &&
+                       cword=${#words[@]} cur=${words[cword-1]} &&
+                       __git_complete_remote_or_refspec &&
+                       print_comp
+               ) &&
+               test_cmp expected out
+       '
+done
+
 test_done