Imported Upstream version 2.12.3 upstream/2.12.3
authorDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:16:05 +0000 (15:16 +0900)
committerDongHun Kwak <dh0128.kwak@samsung.com>
Wed, 3 Mar 2021 06:16:05 +0000 (15:16 +0900)
83 files changed:
Documentation/RelNotes/2.10.3.txt
Documentation/RelNotes/2.11.2.txt [new file with mode: 0644]
Documentation/RelNotes/2.12.3.txt [new file with mode: 0644]
Documentation/RelNotes/2.4.12.txt [new file with mode: 0644]
Documentation/RelNotes/2.5.6.txt [new file with mode: 0644]
Documentation/RelNotes/2.6.7.txt [new file with mode: 0644]
Documentation/RelNotes/2.7.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.8.5.txt [new file with mode: 0644]
Documentation/RelNotes/2.9.4.txt
Documentation/config.txt
Documentation/git-filter-branch.txt
Documentation/git.txt
GIT-VERSION-GEN
Makefile
RelNotes
builtin/blame.c
builtin/branch.c
builtin/checkout.c
builtin/fetch-pack.c
builtin/index-pack.c
builtin/merge.c
builtin/pack-objects.c
builtin/receive-pack.c
builtin/rev-parse.c
builtin/shortlog.c
cache.h
config.c
contrib/coccinelle/array.cocci
contrib/coccinelle/strbuf.cocci
diffcore-pickaxe.c
environment.c
fast-import.c
fetch-pack.c
fetch-pack.h
git-compat-util.h
git-filter-branch.sh
http-push.c
http-walker.c
ident.c
pack-objects.h
refs.c
remote.h
revision.c
revision.h
run-command.c
setup.c
sha1_file.c
sha1_name.c
shell.c
strbuf.c
strbuf.h
t/gitweb-lib.sh
t/interop/.gitignore [new file with mode: 0644]
t/interop/Makefile [new file with mode: 0644]
t/interop/README [new file with mode: 0644]
t/interop/i0000-basic.sh [new file with mode: 0755]
t/interop/i5500-git-daemon.sh [new file with mode: 0755]
t/interop/interop-lib.sh [new file with mode: 0644]
t/lib-git-daemon.sh
t/perf/p0001-rev-list.sh
t/perf/p7000-filter-branch.sh
t/perf/perf-lib.sh
t/perf/run
t/t0100-previous.sh
t/t3204-branch-name-interpretation.sh [new file with mode: 0755]
t/t3600-rm.sh
t/t4062-diff-pickaxe.sh
t/t5316-pack-delta-depth.sh [new file with mode: 0755]
t/t5500-fetch-pack.sh
t/t5516-fetch-push.sh
t/t5615-alternate-env.sh
t/t7003-filter-branch.sh
t/t7004-tag.sh
t/t7030-verify-tag.sh
t/t7406-submodule-update.sh
t/t7518-ident-corner-cases.sh [new file with mode: 0755]
t/t9200-git-cvsexportcommit.sh
t/t9600-cvsimport.sh
t/test-lib-functions.sh
t/test-lib.sh
transport.c
worktree.c
wt-status.c

index 277a2a1..ad6a01b 100644 (file)
@@ -45,4 +45,11 @@ Fixes since v2.10.2
  * Improve the rule to convert "unsigned char [20]" into "struct
    object_id *" in contrib/coccinelle/
 
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
+
 Also contains minor documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.11.2.txt b/Documentation/RelNotes/2.11.2.txt
new file mode 100644 (file)
index 0000000..7428851
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.11.2 Release Notes
+=========================
+
+Fixes since v2.11.1
+-------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
diff --git a/Documentation/RelNotes/2.12.3.txt b/Documentation/RelNotes/2.12.3.txt
new file mode 100644 (file)
index 0000000..ebca846
--- /dev/null
@@ -0,0 +1,64 @@
+Git v2.12.3 Release Notes
+=========================
+
+Fixes since v2.12.2
+-------------------
+
+ * The "parse_config_key()" API function has been cleaned up.
+
+ * An helper function to make it easier to append the result from
+   real_path() to a strbuf has been added.
+
+ * The t/perf performance test suite was not prepared to test not so
+   old versions of Git, but now it covers versions of Git that are not
+   so ancient.
+
+ * Picking two versions of Git and running tests to make sure the
+   older one and the newer one interoperate happily has now become
+   possible.
+
+ * Teach the "debug" helper used in the test framework that allows a
+   command to run under "gdb" to make the session interactive.
+
+ * "git repack --depth=<n>" for a long time busted the specified depth
+   when reusing delta from existing packs.  This has been corrected.
+
+ * user.email that consists of only cruft chars should consistently
+   error out, but didn't.
+
+ * A few tests were run conditionally under (rare) conditions where
+   they cannot be run (like running cvs tests under 'root' account).
+
+ * "git branch @" created refs/heads/@ as a branch, and in general the
+   code that handled @{-1} and @{upstream} was a bit too loose in
+   disambiguating.
+
+ * "git fetch" that requests a commit by object name, when the other
+   side does not allow such an request, failed without much
+   explanation.
+
+ * "git filter-branch --prune-empty" drops a single-parent commit that
+   becomes a no-op, but did not drop a root commit whose tree is empty.
+
+ * Recent versions of Git treats http alternates (used in dumb http
+   transport) just like HTTP redirects and requires the client to
+   enable following it, due to security concerns.  But we forgot to
+   give a warning when we decide not to honor the alternates.
+
+ * NO_PTHREADS build has been broken for some time; now fixed.
+
+ * Fix for potential segv introduced in v2.11.0 and later (also
+   v2.10.2).
+
+ * A few unterminated here documents in tests were fixed, which in
+   turn revealed incorrect expectations the tests make. These tests
+   have been updated.
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
+
+Also contains various documentation updates and code clean-ups.
diff --git a/Documentation/RelNotes/2.4.12.txt b/Documentation/RelNotes/2.4.12.txt
new file mode 100644 (file)
index 0000000..7d15f94
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.4.12 Release Notes
+=========================
+
+Fixes since v2.4.11
+-------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
diff --git a/Documentation/RelNotes/2.5.6.txt b/Documentation/RelNotes/2.5.6.txt
new file mode 100644 (file)
index 0000000..9cd025b
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.5.6 Release Notes
+========================
+
+Fixes since v2.5.5
+------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
diff --git a/Documentation/RelNotes/2.6.7.txt b/Documentation/RelNotes/2.6.7.txt
new file mode 100644 (file)
index 0000000..1335de4
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.6.7 Release Notes
+========================
+
+Fixes since v2.6.6
+------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
diff --git a/Documentation/RelNotes/2.7.5.txt b/Documentation/RelNotes/2.7.5.txt
new file mode 100644 (file)
index 0000000..83559ce
--- /dev/null
@@ -0,0 +1,14 @@
+Git v2.7.5 Release Notes
+========================
+
+Fixes since v2.7.4
+------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
+
+Also contains a few fixes backported from later development tracks.
diff --git a/Documentation/RelNotes/2.8.5.txt b/Documentation/RelNotes/2.8.5.txt
new file mode 100644 (file)
index 0000000..7bd179f
--- /dev/null
@@ -0,0 +1,12 @@
+Git v2.8.5 Release Notes
+========================
+
+Fixes since v2.8.4
+------------------
+
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
index 01e8642..9768293 100644 (file)
@@ -80,4 +80,11 @@ Fixes since v2.9.3
    the file descriptor still open.  Open tempfile with O_CLOEXEC flag
    to avoid this (on Windows, this is mapped to O_NOINHERIT).
 
+ * "git-shell" rejects a request to serve a repository whose name
+   begins with a dash, which makes it no longer possible to get it
+   confused into spawning service programs like "git-upload-pack" with
+   an option like "--help", which in turn would spawn an interactive
+   pager, instead of working with the repository user asked to access
+   (i.e. the one whose name is "--help").
+
 Also contains minor documentation updates and code clean-ups.
index 953cf69..e43d147 100644 (file)
@@ -668,13 +668,13 @@ alternative to having an `init.templateDir` where you've changed
 default hooks.
 
 core.editor::
-       Commands such as `commit` and `tag` that lets you edit
-       messages by launching an editor uses the value of this
+       Commands such as `commit` and `tag` that let you edit
+       messages by launching an editor use the value of this
        variable when it is set, and the environment variable
        `GIT_EDITOR` is not set.  See linkgit:git-var[1].
 
 core.commentChar::
-       Commands such as `commit` and `tag` that lets you edit
+       Commands such as `commit` and `tag` that let you edit
        messages consider a line that begins with this character
        commented, and removes them after the editor returns
        (default '#').
index 0a09698..6e4bb02 100644 (file)
@@ -167,14 +167,12 @@ to other tags will be rewritten to point to the underlying commit.
        project root. Implies <<Remap_to_ancestor>>.
 
 --prune-empty::
-       Some kind of filters will generate empty commits, that left the tree
-       untouched.  This switch allow git-filter-branch to ignore such
-       commits.  Though, this switch only applies for commits that have one
-       and only one parent, it will hence keep merges points. Also, this
-       option is not compatible with the use of `--commit-filter`. Though you
-       just need to use the function 'git_commit_non_empty_tree "$@"' instead
-       of the `git commit-tree "$@"` idiom in your commit filter to make that
-       happen.
+       Some filters will generate empty commits that leave the tree untouched.
+       This option instructs git-filter-branch to remove such commits if they
+       have exactly one or zero non-pruned parents; merge commits will
+       therefore remain intact.  This option cannot be used together with
+       `--commit-filter`, though the same effect can be achieved by using the
+       provided `git_commit_non_empty_tree` function in a commit filter.
 
 --original <namespace>::
        Use this option to set the namespace where the original commits
index 88c39d3..a52b155 100644 (file)
@@ -44,54 +44,62 @@ unreleased) version of Git, that is available from the 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v2.12.2/git.html[documentation for release 2.12.2]
+* link:v2.12.3/git.html[documentation for release 2.12.3]
 
 * release notes for
-  link:RelNotes/2.12.2.txt[2.12.2].
-  link:RelNotes/2.12.1.txt[2.12.1].
+  link:RelNotes/2.12.3.txt[2.12.3],
+  link:RelNotes/2.12.2.txt[2.12.2],
+  link:RelNotes/2.12.1.txt[2.12.1],
   link:RelNotes/2.12.0.txt[2.12].
 
 * link:v2.11.1/git.html[documentation for release 2.11.1]
 
 * release notes for
+  link:RelNotes/2.11.2.txt[2.11.2],
   link:RelNotes/2.11.1.txt[2.11.1],
   link:RelNotes/2.11.0.txt[2.11].
 
-* link:v2.10.2/git.html[documentation for release 2.10.2]
+* link:v2.10.3/git.html[documentation for release 2.10.3]
 
 * release notes for
+  link:RelNotes/2.10.3.txt[2.10.3],
   link:RelNotes/2.10.2.txt[2.10.2],
   link:RelNotes/2.10.1.txt[2.10.1],
   link:RelNotes/2.10.0.txt[2.10].
 
-* link:v2.9.3/git.html[documentation for release 2.9.3]
+* link:v2.9.4/git.html[documentation for release 2.9.4]
 
 * release notes for
+  link:RelNotes/2.9.4.txt[2.9.4],
   link:RelNotes/2.9.3.txt[2.9.3],
   link:RelNotes/2.9.2.txt[2.9.2],
   link:RelNotes/2.9.1.txt[2.9.1],
   link:RelNotes/2.9.0.txt[2.9].
 
-* link:v2.8.4/git.html[documentation for release 2.8.4]
+* link:v2.8.5/git.html[documentation for release 2.8.5]
 
 * release notes for
+  link:RelNotes/2.8.5.txt[2.8.5],
   link:RelNotes/2.8.4.txt[2.8.4],
   link:RelNotes/2.8.3.txt[2.8.3],
   link:RelNotes/2.8.2.txt[2.8.2],
   link:RelNotes/2.8.1.txt[2.8.1],
   link:RelNotes/2.8.0.txt[2.8].
 
-* link:v2.7.3/git.html[documentation for release 2.7.3]
+* link:v2.7.5/git.html[documentation for release 2.7.5]
 
 * release notes for
+  link:RelNotes/2.7.5.txt[2.7.5],
+  link:RelNotes/2.7.4.txt[2.7.4],
   link:RelNotes/2.7.3.txt[2.7.3],
   link:RelNotes/2.7.2.txt[2.7.2],
   link:RelNotes/2.7.1.txt[2.7.1],
   link:RelNotes/2.7.0.txt[2.7].
 
-* link:v2.6.6/git.html[documentation for release 2.6.6]
+* link:v2.6.7/git.html[documentation for release 2.6.7]
 
 * release notes for
+  link:RelNotes/2.6.7.txt[2.6.7],
   link:RelNotes/2.6.6.txt[2.6.6],
   link:RelNotes/2.6.5.txt[2.6.5],
   link:RelNotes/2.6.4.txt[2.6.4],
@@ -100,9 +108,10 @@ Documentation for older releases are available here:
   link:RelNotes/2.6.1.txt[2.6.1],
   link:RelNotes/2.6.0.txt[2.6].
 
-* link:v2.5.5/git.html[documentation for release 2.5.5]
+* link:v2.5.6/git.html[documentation for release 2.5.6]
 
 * release notes for
+  link:RelNotes/2.5.6.txt[2.5.6],
   link:RelNotes/2.5.5.txt[2.5.5],
   link:RelNotes/2.5.4.txt[2.5.4],
   link:RelNotes/2.5.3.txt[2.5.3],
@@ -110,9 +119,10 @@ Documentation for older releases are available here:
   link:RelNotes/2.5.1.txt[2.5.1],
   link:RelNotes/2.5.0.txt[2.5].
 
-* link:v2.4.11/git.html[documentation for release 2.4.11]
+* link:v2.4.12/git.html[documentation for release 2.4.12]
 
 * release notes for
+  link:RelNotes/2.4.12.txt[2.4.12],
   link:RelNotes/2.4.11.txt[2.4.11],
   link:RelNotes/2.4.10.txt[2.4.10],
   link:RelNotes/2.4.9.txt[2.4.9],
index e83f591..6cf4ab6 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v2.12.2
+DEF_VER=v2.12.3
 
 LF='
 '
index ca9f16d..d5454ca 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -2249,6 +2249,9 @@ endif
 ifdef GIT_PERF_MAKE_OPTS
        @echo GIT_PERF_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_PERF_MAKE_OPTS)))'\' >>$@+
 endif
+ifdef GIT_INTEROP_MAKE_OPTS
+       @echo GIT_INTEROP_MAKE_OPTS=\''$(subst ','\'',$(subst ','\'',$(GIT_INTEROP_MAKE_OPTS)))'\' >>$@+
+endif
 ifdef TEST_GIT_INDEX_VERSION
        @echo TEST_GIT_INDEX_VERSION=\''$(subst ','\'',$(subst ','\'',$(TEST_GIT_INDEX_VERSION)))'\' >>$@+
 endif
index cbee82a..a04927d 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/2.12.2.txt
\ No newline at end of file
+Documentation/RelNotes/2.12.3.txt
\ No newline at end of file
index cffc626..f7aa95f 100644 (file)
@@ -658,8 +658,11 @@ static struct origin *find_rename(struct scoreboard *sb,
 /*
  * Append a new blame entry to a given output queue.
  */
-static void add_blame_entry(struct blame_entry ***queue, struct blame_entry *e)
+static void add_blame_entry(struct blame_entry ***queue,
+                           const struct blame_entry *src)
 {
+       struct blame_entry *e = xmalloc(sizeof(*e));
+       memcpy(e, src, sizeof(*e));
        origin_incref(e->suspect);
 
        e->next = **queue;
@@ -760,21 +763,15 @@ static void split_blame(struct blame_entry ***blamed,
                        struct blame_entry *split,
                        struct blame_entry *e)
 {
-       struct blame_entry *new_entry;
-
        if (split[0].suspect && split[2].suspect) {
                /* The first part (reuse storage for the existing entry e) */
                dup_entry(unblamed, e, &split[0]);
 
                /* The last part -- me */
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(unblamed, new_entry);
+               add_blame_entry(unblamed, &split[2]);
 
                /* ... and the middle part -- parent */
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(blamed, new_entry);
+               add_blame_entry(blamed, &split[1]);
        }
        else if (!split[0].suspect && !split[2].suspect)
                /*
@@ -785,18 +782,12 @@ static void split_blame(struct blame_entry ***blamed,
        else if (split[0].suspect) {
                /* me and then parent */
                dup_entry(unblamed, e, &split[0]);
-
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[1]), sizeof(struct blame_entry));
-               add_blame_entry(blamed, new_entry);
+               add_blame_entry(blamed, &split[1]);
        }
        else {
                /* parent and then me */
                dup_entry(blamed, e, &split[1]);
-
-               new_entry = xmalloc(sizeof(*new_entry));
-               memcpy(new_entry, &(split[2]), sizeof(struct blame_entry));
-               add_blame_entry(unblamed, new_entry);
+               add_blame_entry(unblamed, &split[2]);
        }
 }
 
index 9d30f55..babfd0f 100644 (file)
@@ -190,17 +190,20 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
        int ret = 0;
        int remote_branch = 0;
        struct strbuf bname = STRBUF_INIT;
+       unsigned allowed_interpret;
 
        switch (kinds) {
        case FILTER_REFS_REMOTES:
                fmt = "refs/remotes/%s";
                /* For subsequent UI messages */
                remote_branch = 1;
+               allowed_interpret = INTERPRET_BRANCH_REMOTE;
 
                force = 1;
                break;
        case FILTER_REFS_BRANCHES:
                fmt = "refs/heads/%s";
+               allowed_interpret = INTERPRET_BRANCH_LOCAL;
                break;
        default:
                die(_("cannot use -a with -d"));
@@ -215,7 +218,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds,
                char *target = NULL;
                int flags = 0;
 
-               strbuf_branchname(&bname, argv[i]);
+               strbuf_branchname(&bname, argv[i], allowed_interpret);
                free(name);
                name = mkpathdup(fmt, bname.buf);
 
index f174f50..81f07c3 100644 (file)
@@ -452,7 +452,7 @@ static void setup_branch_path(struct branch_info *branch)
 {
        struct strbuf buf = STRBUF_INIT;
 
-       strbuf_branchname(&buf, branch->name);
+       strbuf_branchname(&buf, branch->name, INTERPRET_BRANCH_LOCAL);
        if (strcmp(buf.buf, branch->name))
                branch->name = xstrdup(buf.buf);
        strbuf_splice(&buf, 0, 0, "refs/heads/", 11);
index cfe9e44..2a1c1c2 100644 (file)
@@ -219,12 +219,7 @@ int cmd_fetch_pack(int argc, const char **argv, const char *prefix)
         * remote no-such-ref' would silently succeed without issuing
         * an error.
         */
-       for (i = 0; i < nr_sought; i++) {
-               if (!sought[i] || sought[i]->matched)
-                       continue;
-               error("no such remote ref %s", sought[i]->name);
-               ret = 1;
-       }
+       ret |= report_unmatched_refs(sought, nr_sought);
 
        while (ref) {
                printf("%s %s\n",
index f4b87c6..88d205f 100644 (file)
@@ -1386,7 +1386,9 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                  unsigned char *sha1)
 {
        const char *report = "pack";
-       char name[PATH_MAX];
+       struct strbuf pack_name = STRBUF_INIT;
+       struct strbuf index_name = STRBUF_INIT;
+       struct strbuf keep_name_buf = STRBUF_INIT;
        int err;
 
        if (!from_stdin) {
@@ -1402,14 +1404,13 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                int keep_fd, keep_msg_len = strlen(keep_msg);
 
                if (!keep_name)
-                       keep_fd = odb_pack_keep(name, sizeof(name), sha1);
-               else
-                       keep_fd = open(keep_name, O_RDWR|O_CREAT|O_EXCL, 0600);
+                       keep_name = odb_pack_name(&keep_name_buf, sha1, "keep");
 
+               keep_fd = odb_pack_keep(keep_name);
                if (keep_fd < 0) {
                        if (errno != EEXIST)
                                die_errno(_("cannot write keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                } else {
                        if (keep_msg_len > 0) {
                                write_or_die(keep_fd, keep_msg, keep_msg_len);
@@ -1417,28 +1418,22 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        }
                        if (close(keep_fd) != 0)
                                die_errno(_("cannot close written keep file '%s'"),
-                                         keep_name ? keep_name : name);
+                                         keep_name);
                        report = "keep";
                }
        }
 
        if (final_pack_name != curr_pack_name) {
-               if (!final_pack_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_pack_name = name;
-               }
+               if (!final_pack_name)
+                       final_pack_name = odb_pack_name(&pack_name, sha1, "pack");
                if (finalize_object_file(curr_pack_name, final_pack_name))
                        die(_("cannot store pack file"));
        } else if (from_stdin)
                chmod(final_pack_name, 0444);
 
        if (final_index_name != curr_index_name) {
-               if (!final_index_name) {
-                       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                                get_object_directory(), sha1_to_hex(sha1));
-                       final_index_name = name;
-               }
+               if (!final_index_name)
+                       final_index_name = odb_pack_name(&index_name, sha1, "idx");
                if (finalize_object_file(curr_index_name, final_index_name))
                        die(_("cannot store index file"));
        } else
@@ -1464,6 +1459,10 @@ static void final(const char *final_pack_name, const char *curr_pack_name,
                        input_offset += err;
                }
        }
+
+       strbuf_release(&index_name);
+       strbuf_release(&pack_name);
+       strbuf_release(&keep_name_buf);
 }
 
 static int git_index_pack_config(const char *k, const char *v, void *cb)
index a96d4fb..848a298 100644 (file)
@@ -438,7 +438,7 @@ static void merge_name(const char *remote, struct strbuf *msg)
        char *found_ref;
        int len, early;
 
-       strbuf_branchname(&bname, remote);
+       strbuf_branchname(&bname, remote, 0);
        remote = bname.buf;
 
        memset(branch_head, 0, sizeof(branch_head));
index 8841f8b..c7af475 100644 (file)
@@ -1539,6 +1539,8 @@ static int pack_offset_sort(const void *_a, const void *_b)
  *   2. Updating our size/type to the non-delta representation. These were
  *      either not recorded initially (size) or overwritten with the delta type
  *      (type) when check_object() decided to reuse the delta.
+ *
+ *   3. Resetting our delta depth, as we are now a base object.
  */
 static void drop_reused_delta(struct object_entry *entry)
 {
@@ -1552,6 +1554,7 @@ static void drop_reused_delta(struct object_entry *entry)
                        p = &(*p)->delta_sibling;
        }
        entry->delta = NULL;
+       entry->depth = 0;
 
        oi.sizep = &entry->size;
        oi.typep = &entry->type;
@@ -1570,39 +1573,123 @@ static void drop_reused_delta(struct object_entry *entry)
  * Follow the chain of deltas from this entry onward, throwing away any links
  * that cause us to hit a cycle (as determined by the DFS state flags in
  * the entries).
+ *
+ * We also detect too-long reused chains that would violate our --depth
+ * limit.
  */
 static void break_delta_chains(struct object_entry *entry)
 {
-       /* If it's not a delta, it can't be part of a cycle. */
-       if (!entry->delta) {
-               entry->dfs_state = DFS_DONE;
-               return;
-       }
+       /*
+        * The actual depth of each object we will write is stored as an int,
+        * as it cannot exceed our int "depth" limit. But before we break
+        * changes based no that limit, we may potentially go as deep as the
+        * number of objects, which is elsewhere bounded to a uint32_t.
+        */
+       uint32_t total_depth;
+       struct object_entry *cur, *next;
+
+       for (cur = entry, total_depth = 0;
+            cur;
+            cur = cur->delta, total_depth++) {
+               if (cur->dfs_state == DFS_DONE) {
+                       /*
+                        * We've already seen this object and know it isn't
+                        * part of a cycle. We do need to append its depth
+                        * to our count.
+                        */
+                       total_depth += cur->depth;
+                       break;
+               }
 
-       switch (entry->dfs_state) {
-       case DFS_NONE:
                /*
-                * This is the first time we've seen the object. We mark it as
-                * part of the active potential cycle and recurse.
+                * We break cycles before looping, so an ACTIVE state (or any
+                * other cruft which made its way into the state variable)
+                * is a bug.
                 */
-               entry->dfs_state = DFS_ACTIVE;
-               break_delta_chains(entry->delta);
-               entry->dfs_state = DFS_DONE;
-               break;
+               if (cur->dfs_state != DFS_NONE)
+                       die("BUG: confusing delta dfs state in first pass: %d",
+                           cur->dfs_state);
 
-       case DFS_DONE:
-               /* object already examined, and not part of a cycle */
-               break;
+               /*
+                * Now we know this is the first time we've seen the object. If
+                * it's not a delta, we're done traversing, but we'll mark it
+                * done to save time on future traversals.
+                */
+               if (!cur->delta) {
+                       cur->dfs_state = DFS_DONE;
+                       break;
+               }
 
-       case DFS_ACTIVE:
                /*
-                * We found a cycle that needs broken. It would be correct to
-                * break any link in the chain, but it's convenient to
-                * break this one.
+                * Mark ourselves as active and see if the next step causes
+                * us to cycle to another active object. It's important to do
+                * this _before_ we loop, because it impacts where we make the
+                * cut, and thus how our total_depth counter works.
+                * E.g., We may see a partial loop like:
+                *
+                *   A -> B -> C -> D -> B
+                *
+                * Cutting B->C breaks the cycle. But now the depth of A is
+                * only 1, and our total_depth counter is at 3. The size of the
+                * error is always one less than the size of the cycle we
+                * broke. Commits C and D were "lost" from A's chain.
+                *
+                * If we instead cut D->B, then the depth of A is correct at 3.
+                * We keep all commits in the chain that we examined.
                 */
-               drop_reused_delta(entry);
-               entry->dfs_state = DFS_DONE;
-               break;
+               cur->dfs_state = DFS_ACTIVE;
+               if (cur->delta->dfs_state == DFS_ACTIVE) {
+                       drop_reused_delta(cur);
+                       cur->dfs_state = DFS_DONE;
+                       break;
+               }
+       }
+
+       /*
+        * And now that we've gone all the way to the bottom of the chain, we
+        * need to clear the active flags and set the depth fields as
+        * appropriate. Unlike the loop above, which can quit when it drops a
+        * delta, we need to keep going to look for more depth cuts. So we need
+        * an extra "next" pointer to keep going after we reset cur->delta.
+        */
+       for (cur = entry; cur; cur = next) {
+               next = cur->delta;
+
+               /*
+                * We should have a chain of zero or more ACTIVE states down to
+                * a final DONE. We can quit after the DONE, because either it
+                * has no bases, or we've already handled them in a previous
+                * call.
+                */
+               if (cur->dfs_state == DFS_DONE)
+                       break;
+               else if (cur->dfs_state != DFS_ACTIVE)
+                       die("BUG: confusing delta dfs state in second pass: %d",
+                           cur->dfs_state);
+
+               /*
+                * If the total_depth is more than depth, then we need to snip
+                * the chain into two or more smaller chains that don't exceed
+                * the maximum depth. Most of the resulting chains will contain
+                * (depth + 1) entries (i.e., depth deltas plus one base), and
+                * the last chain (i.e., the one containing entry) will contain
+                * whatever entries are left over, namely
+                * (total_depth % (depth + 1)) of them.
+                *
+                * Since we are iterating towards decreasing depth, we need to
+                * decrement total_depth as we go, and we need to write to the
+                * entry what its final depth will be after all of the
+                * snipping. Since we're snipping into chains of length (depth
+                * + 1) entries, the final depth of an entry will be its
+                * original depth modulo (depth + 1). Any time we encounter an
+                * entry whose final depth is supposed to be zero, we snip it
+                * from its delta base, thereby making it so.
+                */
+               cur->depth = (total_depth--) % (depth + 1);
+               if (!cur->depth)
+                       drop_reused_delta(cur);
+
+               cur->dfs_state = DFS_DONE;
        }
 }
 
index 8672825..b618d52 100644 (file)
@@ -1125,25 +1125,22 @@ static const char *update(struct command *cmd, struct shallow_info *si)
 static void run_update_post_hook(struct command *commands)
 {
        struct command *cmd;
-       int argc;
        struct child_process proc = CHILD_PROCESS_INIT;
        const char *hook;
 
        hook = find_hook("post-update");
-       for (argc = 0, cmd = commands; cmd; cmd = cmd->next) {
-               if (cmd->error_string || cmd->did_not_exist)
-                       continue;
-               argc++;
-       }
-       if (!argc || !hook)
+       if (!hook)
                return;
 
-       argv_array_push(&proc.args, hook);
        for (cmd = commands; cmd; cmd = cmd->next) {
                if (cmd->error_string || cmd->did_not_exist)
                        continue;
+               if (!proc.args.argc)
+                       argv_array_push(&proc.args, hook);
                argv_array_push(&proc.args, cmd->ref_name);
        }
+       if (!proc.args.argc)
+               return;
 
        proc.no_stdin = 1;
        proc.stdout_to_stderr = 1;
index ff13e59..ed8819b 100644 (file)
@@ -535,6 +535,34 @@ N_("git rev-parse --parseopt [<options>] -- [<args>...]\n"
    "\n"
    "Run \"git rev-parse --parseopt -h\" for more information on the first usage.");
 
+/*
+ * Parse "opt" or "opt=<value>", setting value respectively to either
+ * NULL or the string after "=".
+ */
+static int opt_with_value(const char *arg, const char *opt, const char **value)
+{
+       if (skip_prefix(arg, opt, &arg)) {
+               if (!*arg) {
+                       *value = NULL;
+                       return 1;
+               }
+               if (*arg++ == '=') {
+                       *value = arg;
+                       return 1;
+               }
+       }
+       return 0;
+}
+
+static void handle_ref_opt(const char *pattern, const char *prefix)
+{
+       if (pattern)
+               for_each_glob_ref_in(show_reference, pattern, prefix, NULL);
+       else
+               for_each_ref_in(prefix, show_reference, NULL);
+       clear_ref_exclusion(&ref_excludes);
+}
+
 int cmd_rev_parse(int argc, const char **argv, const char *prefix)
 {
        int i, as_is = 0, verify = 0, quiet = 0, revs_count = 0, type = 0;
@@ -671,14 +699,13 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                flags |= GET_SHA1_QUIETLY;
                                continue;
                        }
-                       if (!strcmp(arg, "--short") ||
-                           starts_with(arg, "--short=")) {
+                       if (opt_with_value(arg, "--short", &arg)) {
                                filter &= ~(DO_FLAGS|DO_NOREV);
                                verify = 1;
                                abbrev = DEFAULT_ABBREV;
-                               if (!arg[7])
+                               if (!arg)
                                        continue;
-                               abbrev = strtoul(arg + 8, NULL, 10);
+                               abbrev = strtoul(arg, NULL, 10);
                                if (abbrev < MINIMUM_ABBREV)
                                        abbrev = MINIMUM_ABBREV;
                                else if (40 <= abbrev)
@@ -701,17 +728,17 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                symbolic = SHOW_SYMBOLIC_FULL;
                                continue;
                        }
-                       if (starts_with(arg, "--abbrev-ref") &&
-                           (!arg[12] || arg[12] == '=')) {
+                       if (opt_with_value(arg, "--abbrev-ref", &arg)) {
                                abbrev_ref = 1;
                                abbrev_ref_strict = warn_ambiguous_refs;
-                               if (arg[12] == '=') {
-                                       if (!strcmp(arg + 13, "strict"))
+                               if (arg) {
+                                       if (!strcmp(arg, "strict"))
                                                abbrev_ref_strict = 1;
-                                       else if (!strcmp(arg + 13, "loose"))
+                                       else if (!strcmp(arg, "loose"))
                                                abbrev_ref_strict = 0;
                                        else
-                                               die("unknown mode for %s", arg);
+                                               die("unknown mode for --abbrev-ref: %s",
+                                                   arg);
                                }
                                continue;
                        }
@@ -719,8 +746,8 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref(show_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--disambiguate=")) {
-                               for_each_abbrev(arg + 15, show_abbrev, NULL);
+                       if (skip_prefix(arg, "--disambiguate=", &arg)) {
+                               for_each_abbrev(arg, show_abbrev, NULL);
                                continue;
                        }
                        if (!strcmp(arg, "--bisect")) {
@@ -728,46 +755,24 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                for_each_ref_in("refs/bisect/good", anti_reference, NULL);
                                continue;
                        }
-                       if (starts_with(arg, "--branches=")) {
-                               for_each_glob_ref_in(show_reference, arg + 11,
-                                       "refs/heads/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (!strcmp(arg, "--branches")) {
-                               for_each_branch_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (starts_with(arg, "--tags=")) {
-                               for_each_glob_ref_in(show_reference, arg + 7,
-                                       "refs/tags/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
-                               continue;
-                       }
-                       if (!strcmp(arg, "--tags")) {
-                               for_each_tag_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--branches", &arg)) {
+                               handle_ref_opt(arg, "refs/heads/");
                                continue;
                        }
-                       if (starts_with(arg, "--glob=")) {
-                               for_each_glob_ref(show_reference, arg + 7, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--tags", &arg)) {
+                               handle_ref_opt(arg, "refs/tags/");
                                continue;
                        }
-                       if (starts_with(arg, "--remotes=")) {
-                               for_each_glob_ref_in(show_reference, arg + 10,
-                                       "refs/remotes/", NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (skip_prefix(arg, "--glob=", &arg)) {
+                               handle_ref_opt(arg, NULL);
                                continue;
                        }
-                       if (!strcmp(arg, "--remotes")) {
-                               for_each_remote_ref(show_reference, NULL);
-                               clear_ref_exclusion(&ref_excludes);
+                       if (opt_with_value(arg, "--remotes", &arg)) {
+                               handle_ref_opt(arg, "refs/remotes/");
                                continue;
                        }
-                       if (starts_with(arg, "--exclude=")) {
-                               add_ref_exclusion(&ref_excludes, arg + 10);
+                       if (skip_prefix(arg, "--exclude=", &arg)) {
+                               add_ref_exclusion(&ref_excludes, arg);
                                continue;
                        }
                        if (!strcmp(arg, "--show-toplevel")) {
@@ -849,20 +854,20 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
                                }
                                continue;
                        }
-                       if (starts_with(arg, "--since=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--since=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--after=")) {
-                               show_datestring("--max-age=", arg+8);
+                       if (skip_prefix(arg, "--after=", &arg)) {
+                               show_datestring("--max-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--before=")) {
-                               show_datestring("--min-age=", arg+9);
+                       if (skip_prefix(arg, "--before=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
-                       if (starts_with(arg, "--until=")) {
-                               show_datestring("--min-age=", arg+8);
+                       if (skip_prefix(arg, "--until=", &arg)) {
+                               show_datestring("--min-age=", arg);
                                continue;
                        }
                        if (show_flag(arg) && verify)
index c9585d4..677f372 100644 (file)
@@ -149,7 +149,6 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
        ctx.fmt = CMIT_FMT_USERFORMAT;
        ctx.abbrev = log->abbrev;
        ctx.subject = "";
-       ctx.after_subject = "";
        ctx.date_mode.type = DATE_NORMAL;
        ctx.output_encoding = get_log_output_encoding();
 
diff --git a/cache.h b/cache.h
index 8a9069d..900796c 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -1316,7 +1316,37 @@ extern char *oid_to_hex_r(char *out, const struct object_id *oid);
 extern char *sha1_to_hex(const unsigned char *sha1);   /* static buffer result! */
 extern char *oid_to_hex(const struct object_id *oid);  /* same static buffer as sha1_to_hex */
 
-extern int interpret_branch_name(const char *str, int len, struct strbuf *);
+/*
+ * This reads short-hand syntax that not only evaluates to a commit
+ * object name, but also can act as if the end user spelled the name
+ * of the branch from the command line.
+ *
+ * - "@{-N}" finds the name of the Nth previous branch we were on, and
+ *   places the name of the branch in the given buf and returns the
+ *   number of characters parsed if successful.
+ *
+ * - "<branch>@{upstream}" finds the name of the other ref that
+ *   <branch> is configured to merge with (missing <branch> defaults
+ *   to the current branch), and places the name of the branch in the
+ *   given buf and returns the number of characters parsed if
+ *   successful.
+ *
+ * If the input is not of the accepted format, it returns a negative
+ * number to signal an error.
+ *
+ * If the input was ok but there are not N branch switches in the
+ * reflog, it returns 0.
+ *
+ * If "allowed" is non-zero, it is a treated as a bitfield of allowable
+ * expansions: local branches ("refs/heads/"), remote branches
+ * ("refs/remotes/"), or "HEAD". If no "allowed" bits are set, any expansion is
+ * allowed, even ones to refs outside of those namespaces.
+ */
+#define INTERPRET_BRANCH_LOCAL (1<<0)
+#define INTERPRET_BRANCH_REMOTE (1<<1)
+#define INTERPRET_BRANCH_HEAD (1<<2)
+extern int interpret_branch_name(const char *str, int len, struct strbuf *,
+                                unsigned allowed);
 extern int get_oid_mb(const char *str, struct object_id *oid);
 
 extern int validate_headref(const char *ref);
@@ -1561,6 +1591,27 @@ extern struct packed_git *find_sha1_pack(const unsigned char *sha1,
 extern void pack_report(void);
 
 /*
+ * Create a temporary file rooted in the object database directory.
+ */
+extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
+
+/*
+ * Generate the filename to be used for a pack file with checksum "sha1" and
+ * extension "ext". The result is written into the strbuf "buf", overwriting
+ * any existing contents. A pointer to buf->buf is returned as a convenience.
+ *
+ * Example: odb_pack_name(out, sha1, "idx") => ".git/objects/pack/pack-1234..idx"
+ */
+extern char *odb_pack_name(struct strbuf *buf, const unsigned char *sha1, const char *ext);
+
+/*
+ * Create a pack .keep file named "name" (which should generally be the output
+ * of odb_pack_name). Returns a file descriptor opened for writing, or -1 on
+ * error.
+ */
+extern int odb_pack_keep(const char *name);
+
+/*
  * mmap the index file for the specified packfile (if it is not
  * already mmapped).  Return 0 on success.
  */
@@ -1816,8 +1867,11 @@ extern int git_config_include(const char *name, const char *value, void *data);
  *
  * (i.e., what gets handed to a config_fn_t). The caller provides the section;
  * we return -1 if it does not match, 0 otherwise. The subsection and key
- * out-parameters are filled by the function (and subsection is NULL if it is
+ * out-parameters are filled by the function (and *subsection is NULL if it is
  * missing).
+ *
+ * If the subsection pointer-to-pointer passed in is NULL, returns 0 only if
+ * there is no subsection at all.
  */
 extern int parse_config_key(const char *var,
                            const char *section,
index 48edc6a..0e9e1eb 100644 (file)
--- a/config.c
+++ b/config.c
@@ -2540,11 +2540,10 @@ int parse_config_key(const char *var,
                     const char **subsection, int *subsection_len,
                     const char **key)
 {
-       int section_len = strlen(section);
        const char *dot;
 
        /* Does it start with "section." ? */
-       if (!starts_with(var, section) || var[section_len] != '.')
+       if (!skip_prefix(var, section, &var) || *var != '.')
                return -1;
 
        /*
@@ -2556,12 +2555,16 @@ int parse_config_key(const char *var,
        *key = dot + 1;
 
        /* Did we have a subsection at all? */
-       if (dot == var + section_len) {
-               *subsection = NULL;
-               *subsection_len = 0;
+       if (dot == var) {
+               if (subsection) {
+                       *subsection = NULL;
+                       *subsection_len = 0;
+               }
        }
        else {
-               *subsection = var + section_len + 1;
+               if (!subsection)
+                       return -1;
+               *subsection = var + 1;
                *subsection_len = dot - *subsection;
        }
 
index 2d7f25d..4ba98b7 100644 (file)
@@ -24,3 +24,19 @@ expression n;
 @@
 - memcpy(dst, src, n * sizeof(T));
 + COPY_ARRAY(dst, src, n);
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc(n * sizeof(*ptr));
++ ALLOC_ARRAY(ptr, n);
+
+@@
+type T;
+T *ptr;
+expression n;
+@@
+- ptr = xmalloc(n * sizeof(T));
++ ALLOC_ARRAY(ptr, n);
index 63995f2..1d580e4 100644 (file)
@@ -38,3 +38,9 @@ expression E1, E2, E3;
 @@
 - strbuf_addstr(E1, find_unique_abbrev(E2, E3));
 + strbuf_add_unique_abbrev(E1, E2, E3);
+
+@@
+expression E1, E2;
+@@
+- strbuf_addstr(E1, real_path(E2));
++ strbuf_add_real_path(E1, E2);
index 9795ca1..341529b 100644 (file)
@@ -81,12 +81,15 @@ static unsigned int contains(mmfile_t *mf, regex_t *regexp, kwset_t kws)
                regmatch_t regmatch;
                int flags = 0;
 
-               while (*data &&
+               while (sz && *data &&
                       !regexec_buf(regexp, data, sz, 1, &regmatch, flags)) {
                        flags |= REG_NOTBOL;
                        data += regmatch.rm_eo;
-                       if (*data && regmatch.rm_so == regmatch.rm_eo)
+                       sz -= regmatch.rm_eo;
+                       if (sz && *data && regmatch.rm_so == regmatch.rm_eo) {
                                data++;
+                               sz--;
+                       }
                        cnt++;
                }
 
index 42dc310..2fdba76 100644 (file)
@@ -296,18 +296,16 @@ int odb_mkstemp(char *template, size_t limit, const char *pattern)
        return xmkstemp_mode(template, mode);
 }
 
-int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1)
+int odb_pack_keep(const char *name)
 {
        int fd;
 
-       snprintf(name, namesz, "%s/pack/pack-%s.keep",
-                get_object_directory(), sha1_to_hex(sha1));
        fd = open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
        if (0 <= fd)
                return fd;
 
        /* slow path */
-       safe_create_leading_directories(name);
+       safe_create_leading_directories_const(name);
        return open(name, O_RDWR|O_CREAT|O_EXCL, 0600);
 }
 
index 64fe602..4d5a7f5 100644 (file)
@@ -940,41 +940,40 @@ static const char *create_index(void)
 
 static char *keep_pack(const char *curr_index_name)
 {
-       static char name[PATH_MAX];
        static const char *keep_msg = "fast-import";
+       struct strbuf name = STRBUF_INIT;
        int keep_fd;
 
-       keep_fd = odb_pack_keep(name, sizeof(name), pack_data->sha1);
+       odb_pack_name(&name, pack_data->sha1, "keep");
+       keep_fd = odb_pack_keep(name.buf);
        if (keep_fd < 0)
                die_errno("cannot create keep file");
        write_or_die(keep_fd, keep_msg, strlen(keep_msg));
        if (close(keep_fd))
                die_errno("failed to write keep file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.pack",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(pack_data->pack_name, name))
+       odb_pack_name(&name, pack_data->sha1, "pack");
+       if (finalize_object_file(pack_data->pack_name, name.buf))
                die("cannot store pack file");
 
-       snprintf(name, sizeof(name), "%s/pack/pack-%s.idx",
-                get_object_directory(), sha1_to_hex(pack_data->sha1));
-       if (finalize_object_file(curr_index_name, name))
+       odb_pack_name(&name, pack_data->sha1, "idx");
+       if (finalize_object_file(curr_index_name, name.buf))
                die("cannot store index file");
        free((void *)curr_index_name);
-       return name;
+       return strbuf_detach(&name, NULL);
 }
 
 static void unkeep_all_packs(void)
 {
-       static char name[PATH_MAX];
+       struct strbuf name = STRBUF_INIT;
        int k;
 
        for (k = 0; k < pack_id; k++) {
                struct packed_git *p = all_packs[k];
-               snprintf(name, sizeof(name), "%s/pack/pack-%s.keep",
-                        get_object_directory(), sha1_to_hex(p->sha1));
-               unlink_or_warn(name);
+               odb_pack_name(&name, p->sha1, "keep");
+               unlink_or_warn(name.buf);
        }
+       strbuf_release(&name);
 }
 
 static int loosen_small_pack(const struct packed_git *p)
@@ -1033,6 +1032,7 @@ static void end_packfile(void)
                        die("core git rejected index %s", idx_name);
                all_packs[pack_id] = new_p;
                install_packed_git(new_p);
+               free(idx_name);
 
                /* Print the boundary */
                if (pack_edges) {
index 601f077..f12bfcd 100644 (file)
@@ -578,7 +578,7 @@ static void filter_refs(struct fetch_pack_args *args,
                                        break; /* definitely do not have it */
                                else if (cmp == 0) {
                                        keep = 1; /* definitely have it */
-                                       sought[i]->matched = 1;
+                                       sought[i]->match_status = REF_MATCHED;
                                }
                                i++;
                        }
@@ -598,22 +598,24 @@ static void filter_refs(struct fetch_pack_args *args,
        }
 
        /* Append unmatched requests to the list */
-       if ((allow_unadvertised_object_request &
-           (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
-               for (i = 0; i < nr_sought; i++) {
-                       unsigned char sha1[20];
+       for (i = 0; i < nr_sought; i++) {
+               unsigned char sha1[20];
 
-                       ref = sought[i];
-                       if (ref->matched)
-                               continue;
-                       if (get_sha1_hex(ref->name, sha1) ||
-                           ref->name[40] != '\0' ||
-                           hashcmp(sha1, ref->old_oid.hash))
-                               continue;
+               ref = sought[i];
+               if (ref->match_status != REF_NOT_MATCHED)
+                       continue;
+               if (get_sha1_hex(ref->name, sha1) ||
+                   ref->name[40] != '\0' ||
+                   hashcmp(sha1, ref->old_oid.hash))
+                       continue;
 
-                       ref->matched = 1;
+               if ((allow_unadvertised_object_request &
+                   (ALLOW_TIP_SHA1 | ALLOW_REACHABLE_SHA1))) {
+                       ref->match_status = REF_MATCHED;
                        *newtail = copy_ref(ref);
                        newtail = &(*newtail)->next;
+               } else {
+                       ref->match_status = REF_UNADVERTISED_NOT_ALLOWED;
                }
        }
        *refs = newlist;
@@ -1094,3 +1096,26 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
        clear_shallow_info(&si);
        return ref_cpy;
 }
+
+int report_unmatched_refs(struct ref **sought, int nr_sought)
+{
+       int i, ret = 0;
+
+       for (i = 0; i < nr_sought; i++) {
+               if (!sought[i])
+                       continue;
+               switch (sought[i]->match_status) {
+               case REF_MATCHED:
+                       continue;
+               case REF_NOT_MATCHED:
+                       error(_("no such remote ref %s"), sought[i]->name);
+                       break;
+               case REF_UNADVERTISED_NOT_ALLOWED:
+                       error(_("Server does not allow request for unadvertised object %s"),
+                             sought[i]->name);
+                       break;
+               }
+               ret = 1;
+       }
+       return ret;
+}
index c912e3d..a2d46e6 100644 (file)
@@ -45,4 +45,10 @@ struct ref *fetch_pack(struct fetch_pack_args *args,
                       struct sha1_array *shallow,
                       char **pack_lockfile);
 
+/*
+ * Print an appropriate error message for each sought ref that wasn't
+ * matched.  Return 0 if all sought refs were matched, otherwise 1.
+ */
+int report_unmatched_refs(struct ref **sought, int nr_sought);
+
 #endif
index e626851..8a4a3f8 100644 (file)
@@ -798,8 +798,6 @@ extern FILE *xfopen(const char *path, const char *mode);
 extern FILE *xfdopen(int fd, const char *mode);
 extern int xmkstemp(char *template);
 extern int xmkstemp_mode(char *template, int mode);
-extern int odb_mkstemp(char *template, size_t limit, const char *pattern);
-extern int odb_pack_keep(char *name, size_t namesz, const unsigned char *sha1);
 extern char *xgetcwd(void);
 extern FILE *fopen_for_writing(const char *path);
 
index 86b2ff1..2b8cdba 100755 (executable)
@@ -46,6 +46,8 @@ git_commit_non_empty_tree()
 {
        if test $# = 3 && test "$1" = $(git rev-parse "$3^{tree}"); then
                map "$3"
+       elif test $# = 1 && test "$1" = 4b825dc642cb6eb9a060e54bf8d69288fbee4904; then
+               :
        else
                git commit-tree "$@"
        fi
index 704b1c8..f0e3108 100644 (file)
@@ -1431,11 +1431,9 @@ static void one_remote_ref(const char *refname)
         */
        if (repo->can_update_info_refs && !has_object_file(&ref->old_oid)) {
                obj = lookup_unknown_object(ref->old_oid.hash);
-               if (obj) {
-                       fprintf(stderr, "  fetch %s for %s\n",
-                               oid_to_hex(&ref->old_oid), refname);
-                       add_fetch_request(obj);
-               }
+               fprintf(stderr, "  fetch %s for %s\n",
+                       oid_to_hex(&ref->old_oid), refname);
+               add_fetch_request(obj);
        }
 
        ref->next = remote_refs;
index 507c200..ee049cb 100644 (file)
@@ -168,6 +168,11 @@ static int is_alternate_allowed(const char *url)
        };
        int i;
 
+       if (http_follow_config != HTTP_FOLLOW_ALWAYS) {
+               warning("alternate disabled by http.followRedirects: %s", url);
+               return 0;
+       }
+
        for (i = 0; i < ARRAY_SIZE(protocols); i++) {
                const char *end;
                if (skip_prefix(url, protocols[i], &end) &&
@@ -317,6 +322,8 @@ static void process_alternates_response(void *callback_data)
                                        while (tail->next != NULL)
                                                tail = tail->next;
                                        tail->next = newalt;
+                               } else {
+                                       strbuf_release(&target);
                                }
                        }
                }
@@ -334,9 +341,6 @@ static void fetch_alternates(struct walker *walker, const char *base)
        struct alternates_request alt_req;
        struct walker_data *cdata = walker->data;
 
-       if (http_follow_config != HTTP_FOLLOW_ALWAYS)
-               return;
-
        /*
         * If another request has already started fetching alternates,
         * wait for them to arrive and return to processing this request's
diff --git a/ident.c b/ident.c
index ac4ae02..c0364fe 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -153,7 +153,7 @@ static void copy_email(const struct passwd *pw, struct strbuf *email,
 
 const char *ident_default_name(void)
 {
-       if (!git_default_name.len) {
+       if (!(ident_config_given & IDENT_NAME_GIVEN) && !git_default_name.len) {
                copy_gecos(xgetpwuid_self(&default_name_is_bogus), &git_default_name);
                strbuf_trim(&git_default_name);
        }
@@ -162,7 +162,7 @@ const char *ident_default_name(void)
 
 const char *ident_default_email(void)
 {
-       if (!git_default_email.len) {
+       if (!(ident_config_given & IDENT_MAIL_GIVEN) && !git_default_email.len) {
                const char *email = getenv("EMAIL");
 
                if (email && email[0]) {
@@ -203,6 +203,15 @@ static int crud(unsigned char c)
                c == '\'';
 }
 
+static int has_non_crud(const char *str)
+{
+       for (; *str; str++) {
+               if (!crud(*str))
+                       return 1;
+       }
+       return 0;
+}
+
 /*
  * Copy over a string to the destination, but avoid special
  * characters ('\n', '<' and '>') and remove crud at the end
@@ -351,19 +360,32 @@ const char *fmt_ident(const char *name, const char *email,
        int want_date = !(flag & IDENT_NO_DATE);
        int want_name = !(flag & IDENT_NO_NAME);
 
+       if (!email) {
+               if (strict && ident_use_config_only
+                   && !(ident_config_given & IDENT_MAIL_GIVEN)) {
+                       fputs(_(env_hint), stderr);
+                       die(_("no email was given and auto-detection is disabled"));
+               }
+               email = ident_default_email();
+               if (strict && default_email_is_bogus) {
+                       fputs(_(env_hint), stderr);
+                       die(_("unable to auto-detect email address (got '%s')"), email);
+               }
+       }
+
        if (want_name) {
                int using_default = 0;
                if (!name) {
                        if (strict && ident_use_config_only
                            && !(ident_config_given & IDENT_NAME_GIVEN)) {
                                fputs(_(env_hint), stderr);
-                               die("no name was given and auto-detection is disabled");
+                               die(_("no name was given and auto-detection is disabled"));
                        }
                        name = ident_default_name();
                        using_default = 1;
                        if (strict && default_name_is_bogus) {
                                fputs(_(env_hint), stderr);
-                               die("unable to auto-detect name (got '%s')", name);
+                               die(_("unable to auto-detect name (got '%s')"), name);
                        }
                }
                if (!*name) {
@@ -371,24 +393,13 @@ const char *fmt_ident(const char *name, const char *email,
                        if (strict) {
                                if (using_default)
                                        fputs(_(env_hint), stderr);
-                               die("empty ident name (for <%s>) not allowed", email);
+                               die(_("empty ident name (for <%s>) not allowed"), email);
                        }
                        pw = xgetpwuid_self(NULL);
                        name = pw->pw_name;
                }
-       }
-
-       if (!email) {
-               if (strict && ident_use_config_only
-                   && !(ident_config_given & IDENT_MAIL_GIVEN)) {
-                       fputs(_(env_hint), stderr);
-                       die("no email was given and auto-detection is disabled");
-               }
-               email = ident_default_email();
-               if (strict && default_email_is_bogus) {
-                       fputs(_(env_hint), stderr);
-                       die("unable to auto-detect email address (got '%s')", email);
-               }
+               if (strict && !has_non_crud(name))
+                       die(_("name consists only of disallowed characters: %s"), name);
        }
 
        strbuf_reset(&ident);
@@ -403,7 +414,7 @@ const char *fmt_ident(const char *name, const char *email,
                strbuf_addch(&ident, ' ');
                if (date_str && date_str[0]) {
                        if (parse_date(date_str, &ident) < 0)
-                               die("invalid date format: %s", date_str);
+                               die(_("invalid date format: %s"), date_str);
                }
                else
                        strbuf_addstr(&ident, ident_default_date());
index cc9b9a9..03f1191 100644 (file)
@@ -30,12 +30,16 @@ struct object_entry {
 
        /*
         * State flags for depth-first search used for analyzing delta cycles.
+        *
+        * The depth is measured in delta-links to the base (so if A is a delta
+        * against B, then A has a depth of 1, and B a depth of 0).
         */
        enum {
                DFS_NONE = 0,
                DFS_ACTIVE,
                DFS_DONE
        } dfs_state;
+       int depth;
 };
 
 struct packing_data {
diff --git a/refs.c b/refs.c
index cd36b64..2d71774 100644 (file)
--- a/refs.c
+++ b/refs.c
@@ -404,7 +404,7 @@ int refname_match(const char *abbrev_name, const char *full_name)
 static char *substitute_branch_name(const char **string, int *len)
 {
        struct strbuf buf = STRBUF_INIT;
-       int ret = interpret_branch_name(*string, *len, &buf);
+       int ret = interpret_branch_name(*string, *len, &buf, 0);
 
        if (ret == *len) {
                size_t size;
@@ -1034,10 +1034,10 @@ static struct string_list *hide_refs;
 
 int parse_hide_refs_config(const char *var, const char *value, const char *section)
 {
+       const char *key;
        if (!strcmp("transfer.hiderefs", var) ||
-           /* NEEDSWORK: use parse_config_key() once both are merged */
-           (starts_with(var, section) && var[strlen(section)] == '.' &&
-            !strcmp(var + strlen(section), ".hiderefs"))) {
+           (!parse_config_key(var, section, NULL, NULL, &key) &&
+            !strcmp(key, "hiderefs"))) {
                char *ref;
                int len;
 
index a5bbbe0..dd8c517 100644 (file)
--- a/remote.h
+++ b/remote.h
@@ -89,8 +89,13 @@ struct ref {
                force:1,
                forced_update:1,
                expect_old_sha1:1,
-               deletion:1,
-               matched:1;
+               deletion:1;
+
+       enum {
+               REF_NOT_MATCHED = 0, /* initial value */
+               REF_MATCHED,
+               REF_UNADVERTISED_NOT_ALLOWED
+       } match_status;
 
        /*
         * Order is important here, as we write to FETCH_HEAD
index b37dbec..771d079 100644 (file)
@@ -147,7 +147,7 @@ static void add_pending_object_with_path(struct rev_info *revs,
                revs->no_walk = 0;
        if (revs->reflog_info && obj->type == OBJ_COMMIT) {
                struct strbuf buf = STRBUF_INIT;
-               int len = interpret_branch_name(name, 0, &buf);
+               int len = interpret_branch_name(name, 0, &buf, 0);
                int st;
 
                if (0 < len && name[len] && buf.len)
index 9fac1a6..14886ec 100644 (file)
@@ -259,8 +259,6 @@ extern void put_revision_mark(const struct rev_info *revs,
 extern void mark_parents_uninteresting(struct commit *commit);
 extern void mark_tree_uninteresting(struct tree *tree);
 
-char *path_name(struct strbuf *path, const char *name);
-
 extern void show_object_with_name(FILE *, struct object *, const char *);
 
 extern void add_pending_object(struct rev_info *revs,
index 5227f78..574b81d 100644 (file)
@@ -48,7 +48,7 @@ static void cleanup_children(int sig, int in_signal)
 
                kill(p->pid, sig);
 
-               if (p->process->wait_after_clean) {
+               if (p->process && p->process->wait_after_clean) {
                        p->next = children_to_wait_for;
                        children_to_wait_for = p;
                } else {
diff --git a/setup.c b/setup.c
index 6b48cb9..8f64fbd 100644 (file)
--- a/setup.c
+++ b/setup.c
@@ -254,7 +254,7 @@ int get_common_dir_noenv(struct strbuf *sb, const char *gitdir)
                if (!is_absolute_path(data.buf))
                        strbuf_addf(&path, "%s/", gitdir);
                strbuf_addbuf(&path, &data);
-               strbuf_addstr(sb, real_path(path.buf));
+               strbuf_add_real_path(sb, path.buf);
                ret = 1;
        } else {
                strbuf_addstr(sb, gitdir);
index 8ce80d4..d77b915 100644 (file)
@@ -203,31 +203,26 @@ static const char *alt_sha1_path(struct alternate_object_database *alt,
        return buf->buf;
 }
 
-/*
- * Return the name of the pack or index file with the specified sha1
- * in its filename.  *base and *name are scratch space that must be
- * provided by the caller.  which should be "pack" or "idx".
- */
-static char *sha1_get_pack_name(const unsigned char *sha1,
-                               struct strbuf *buf,
-                               const char *which)
+ char *odb_pack_name(struct strbuf *buf,
+                    const unsigned char *sha1,
+                    const char *ext)
 {
        strbuf_reset(buf);
        strbuf_addf(buf, "%s/pack/pack-%s.%s", get_object_directory(),
-                   sha1_to_hex(sha1), which);
+                   sha1_to_hex(sha1), ext);
        return buf->buf;
 }
 
 char *sha1_pack_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "pack");
+       return odb_pack_name(&buf, sha1, "pack");
 }
 
 char *sha1_pack_index_name(const unsigned char *sha1)
 {
        static struct strbuf buf = STRBUF_INIT;
-       return sha1_get_pack_name(sha1, &buf, "idx");
+       return odb_pack_name(&buf, sha1, "idx");
 }
 
 struct alternate_object_database *alt_odb_list;
index 73a915f..26ceec1 100644 (file)
@@ -1176,7 +1176,8 @@ static int interpret_empty_at(const char *name, int namelen, int len, struct str
        return 1;
 }
 
-static int reinterpret(const char *name, int namelen, int len, struct strbuf *buf)
+static int reinterpret(const char *name, int namelen, int len,
+                      struct strbuf *buf, unsigned allowed)
 {
        /* we have extra data, which might need further processing */
        struct strbuf tmp = STRBUF_INIT;
@@ -1184,7 +1185,7 @@ static int reinterpret(const char *name, int namelen, int len, struct strbuf *bu
        int ret;
 
        strbuf_add(buf, name + len, namelen - len);
-       ret = interpret_branch_name(buf->buf, buf->len, &tmp);
+       ret = interpret_branch_name(buf->buf, buf->len, &tmp, allowed);
        /* that data was not interpreted, remove our cruft */
        if (ret < 0) {
                strbuf_setlen(buf, used);
@@ -1205,11 +1206,27 @@ static void set_shortened_ref(struct strbuf *buf, const char *ref)
        free(s);
 }
 
+static int branch_interpret_allowed(const char *refname, unsigned allowed)
+{
+       if (!allowed)
+               return 1;
+
+       if ((allowed & INTERPRET_BRANCH_LOCAL) &&
+           starts_with(refname, "refs/heads/"))
+               return 1;
+       if ((allowed & INTERPRET_BRANCH_REMOTE) &&
+           starts_with(refname, "refs/remotes/"))
+               return 1;
+
+       return 0;
+}
+
 static int interpret_branch_mark(const char *name, int namelen,
                                 int at, struct strbuf *buf,
                                 int (*get_mark)(const char *, int),
                                 const char *(*get_data)(struct branch *,
-                                                        struct strbuf *))
+                                                        struct strbuf *),
+                                unsigned allowed)
 {
        int len;
        struct branch *branch;
@@ -1234,64 +1251,55 @@ static int interpret_branch_mark(const char *name, int namelen,
        if (!value)
                die("%s", err.buf);
 
+       if (!branch_interpret_allowed(value, allowed))
+               return -1;
+
        set_shortened_ref(buf, value);
        return len + at;
 }
 
-/*
- * This reads short-hand syntax that not only evaluates to a commit
- * object name, but also can act as if the end user spelled the name
- * of the branch from the command line.
- *
- * - "@{-N}" finds the name of the Nth previous branch we were on, and
- *   places the name of the branch in the given buf and returns the
- *   number of characters parsed if successful.
- *
- * - "<branch>@{upstream}" finds the name of the other ref that
- *   <branch> is configured to merge with (missing <branch> defaults
- *   to the current branch), and places the name of the branch in the
- *   given buf and returns the number of characters parsed if
- *   successful.
- *
- * If the input is not of the accepted format, it returns a negative
- * number to signal an error.
- *
- * If the input was ok but there are not N branch switches in the
- * reflog, it returns 0.
- */
-int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
+int interpret_branch_name(const char *name, int namelen, struct strbuf *buf,
+                         unsigned allowed)
 {
        char *at;
        const char *start;
-       int len = interpret_nth_prior_checkout(name, namelen, buf);
+       int len;
 
        if (!namelen)
                namelen = strlen(name);
 
-       if (!len) {
-               return len; /* syntax Ok, not enough switches */
-       } else if (len > 0) {
-               if (len == namelen)
-                       return len; /* consumed all */
-               else
-                       return reinterpret(name, namelen, len, buf);
+       if (!allowed || (allowed & INTERPRET_BRANCH_LOCAL)) {
+               len = interpret_nth_prior_checkout(name, namelen, buf);
+               if (!len) {
+                       return len; /* syntax Ok, not enough switches */
+               } else if (len > 0) {
+                       if (len == namelen)
+                               return len; /* consumed all */
+                       else
+                               return reinterpret(name, namelen, len, buf, allowed);
+               }
        }
 
        for (start = name;
             (at = memchr(start, '@', namelen - (start - name)));
             start = at + 1) {
 
-               len = interpret_empty_at(name, namelen, at - name, buf);
-               if (len > 0)
-                       return reinterpret(name, namelen, len, buf);
+               if (!allowed || (allowed & INTERPRET_BRANCH_HEAD)) {
+                       len = interpret_empty_at(name, namelen, at - name, buf);
+                       if (len > 0)
+                               return reinterpret(name, namelen, len, buf,
+                                                  allowed);
+               }
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           upstream_mark, branch_get_upstream);
+                                           upstream_mark, branch_get_upstream,
+                                           allowed);
                if (len > 0)
                        return len;
 
                len = interpret_branch_mark(name, namelen, at - name, buf,
-                                           push_mark, branch_get_push);
+                                           push_mark, branch_get_push,
+                                           allowed);
                if (len > 0)
                        return len;
        }
@@ -1299,22 +1307,19 @@ int interpret_branch_name(const char *name, int namelen, struct strbuf *buf)
        return -1;
 }
 
-int strbuf_branchname(struct strbuf *sb, const char *name)
+void strbuf_branchname(struct strbuf *sb, const char *name, unsigned allowed)
 {
        int len = strlen(name);
-       int used = interpret_branch_name(name, len, sb);
+       int used = interpret_branch_name(name, len, sb, allowed);
 
-       if (used == len)
-               return 0;
        if (used < 0)
                used = 0;
        strbuf_add(sb, name + used, len - used);
-       return len;
 }
 
 int strbuf_check_branch_ref(struct strbuf *sb, const char *name)
 {
-       strbuf_branchname(sb, name);
+       strbuf_branchname(sb, name, INTERPRET_BRANCH_LOCAL);
        if (name[0] == '-')
                return -1;
        strbuf_splice(sb, 0, 0, "refs/heads/", 11);
diff --git a/shell.c b/shell.c
index 464ee1a..fe2d314 100644 (file)
--- a/shell.c
+++ b/shell.c
@@ -13,7 +13,7 @@ static int do_generic_cmd(const char *me, char *arg)
        const char *my_argv[4];
 
        setup_path();
-       if (!arg || !(arg = sq_dequote(arg)))
+       if (!arg || !(arg = sq_dequote(arg)) || *arg == '-')
                die("bad argument");
        if (!starts_with(me, "git-"))
                die("bad command");
index 8fec657..ace58e7 100644 (file)
--- a/strbuf.c
+++ b/strbuf.c
@@ -707,6 +707,17 @@ void strbuf_add_absolute_path(struct strbuf *sb, const char *path)
        strbuf_addstr(sb, path);
 }
 
+void strbuf_add_real_path(struct strbuf *sb, const char *path)
+{
+       if (sb->len) {
+               struct strbuf resolved = STRBUF_INIT;
+               strbuf_realpath(&resolved, path, 1);
+               strbuf_addbuf(sb, &resolved);
+               strbuf_release(&resolved);
+       } else
+               strbuf_realpath(sb, path, 1);
+}
+
 int printf_ln(const char *fmt, ...)
 {
        int ret;
index cf1b540..80047b1 100644 (file)
--- a/strbuf.h
+++ b/strbuf.h
@@ -441,6 +441,20 @@ extern int strbuf_getcwd(struct strbuf *sb);
  */
 extern void strbuf_add_absolute_path(struct strbuf *sb, const char *path);
 
+/**
+ * Canonize `path` (make it absolute, resolve symlinks, remove extra
+ * slashes) and append it to `sb`.  Die with an informative error
+ * message if there is a problem.
+ *
+ * The directory part of `path` (i.e., everything up to the last
+ * dir_sep) must denote a valid, existing directory, but the last
+ * component need not exist.
+ *
+ * Callers that don't mind links should use the more lightweight
+ * strbuf_add_absolute_path() instead.
+ */
+extern void strbuf_add_real_path(struct strbuf *sb, const char *path);
+
 
 /**
  * Normalize in-place the path contained in the strbuf. See
@@ -560,7 +574,26 @@ static inline void strbuf_complete_line(struct strbuf *sb)
        strbuf_complete(sb, '\n');
 }
 
-extern int strbuf_branchname(struct strbuf *sb, const char *name);
+/*
+ * Copy "name" to "sb", expanding any special @-marks as handled by
+ * interpret_branch_name(). The result is a non-qualified branch name
+ * (so "foo" or "origin/master" instead of "refs/heads/foo" or
+ * "refs/remotes/origin/master").
+ *
+ * Note that the resulting name may not be a syntactically valid refname.
+ *
+ * If "allowed" is non-zero, restrict the set of allowed expansions. See
+ * interpret_branch_name() for details.
+ */
+extern void strbuf_branchname(struct strbuf *sb, const char *name,
+                             unsigned allowed);
+
+/*
+ * Like strbuf_branchname() above, but confirm that the result is
+ * syntactically valid to be used as a local branch name in refs/heads/.
+ *
+ * The return value is "0" if the result is valid, and "-1" otherwise.
+ */
 extern int strbuf_check_branch_ref(struct strbuf *sb, const char *name);
 
 extern void strbuf_addstr_urlencode(struct strbuf *, const char *,
index d5dab5a..006d2a8 100644 (file)
@@ -110,7 +110,12 @@ perl -MEncode -e '$e="";decode_utf8($e, Encode::FB_CROAK)' >/dev/null 2>&1 || {
 }
 
 perl -MCGI -MCGI::Util -MCGI::Carp -e 0 >/dev/null 2>&1 || {
-       skip_all='skipping gitweb tests, CGI module unusable'
+       skip_all='skipping gitweb tests, CGI & CGI::Util & CGI::Carp modules not available'
+       test_done
+}
+
+perl -mTime::HiRes -e 0 >/dev/null 2>&1 || {
+       skip_all='skipping gitweb tests, Time::HiRes module not available'
        test_done
 }
 
diff --git a/t/interop/.gitignore b/t/interop/.gitignore
new file mode 100644 (file)
index 0000000..49c78d3
--- /dev/null
@@ -0,0 +1,4 @@
+/trash directory*/
+/test-results/
+/.prove/
+/build/
diff --git a/t/interop/Makefile b/t/interop/Makefile
new file mode 100644 (file)
index 0000000..31a4bbc
--- /dev/null
@@ -0,0 +1,16 @@
+-include ../../config.mak
+export GIT_TEST_OPTIONS
+
+SHELL_PATH ?= $(SHELL)
+SHELL_PATH_SQ = $(subst ','\'',$(SHELL_PATH))
+T = $(sort $(wildcard i[0-9][0-9][0-9][0-9]-*.sh))
+
+all: $(T)
+
+$(T):
+       @echo "*** $@ ***"; '$(SHELL_PATH_SQ)' $@ $(GIT_TEST_OPTS)
+
+clean:
+       rm -rf build "trash directory".* test-results
+
+.PHONY: all clean $(T)
diff --git a/t/interop/README b/t/interop/README
new file mode 100644 (file)
index 0000000..72d42bd
--- /dev/null
@@ -0,0 +1,85 @@
+Git version interoperability tests
+==================================
+
+This directory has interoperability tests for git. Each script is
+similar to the normal test scripts found in t/, but with the added twist
+that two special versions of git, "git.a" and "git.b", are available in
+the PATH. Individual tests can then check the interaction between the
+two versions.
+
+When you add a feature that handles backwards compatibility between git
+versions, it's encouraged to add a test here to make sure it behaves as
+you expect.
+
+
+Running Tests
+-------------
+
+The easiest way to run tests is to say "make".  This runs all
+the tests against their default versions.
+
+You can run a single test like:
+
+    $ ./i0000-basic.sh
+    ok 1 - bare git is forbidden
+    ok 2 - git.a version (v1.6.6.3)
+    ok 3 - git.b version (v2.11.1)
+    # passed all 3 test(s)
+    1..3
+
+Each test contains default versions to run against. You may override
+these by setting `GIT_TEST_VERSION_A` and `GIT_TEST_VERSION_B` in the
+environment. Note that not all combinations will give sensible outcomes
+for all tests (e.g., a test checking for a specific old/new interaction
+may want something "old" enough" and something "new" enough; see
+individual tests for details).
+
+Version names should be resolvable as revisions in the current
+repository. They will be exported and built as needed using the
+config.mak files found at the root of your working tree.
+
+The exception is the special version "." which uses the currently-built
+contents of your working tree.
+
+You can set the following variables (in the environment or in your config.mak):
+
+    GIT_INTEROP_MAKE_OPTS
+       Options to pass to `make` when building a git version (e.g.,
+       `-j8`).
+
+You can also pass any command-line options taken by ordinary git tests (e.g.,
+"-v").
+
+
+Naming Tests
+------------
+
+The interop test files are named like:
+
+       iNNNN-short-description.sh
+
+where N is a decimal digit.  The same conventions for choosing NNNN as
+for normal tests apply.
+
+
+Writing Tests
+-------------
+
+An interop test script starts like a normal script, declaring a few
+variables and then including interop-lib.sh (which includes test-lib.sh).
+Besides test_description, you should also set the $VERSION_A and $VERSION_B
+variables to give the default versions to test against. See t0000-basic.sh for
+an example.
+
+You can then use test_expect_success as usual, with a few differences:
+
+  1. The special commands "git.a" and "git.b" correspond to the
+     two versions.
+
+  2. You cannot call a bare "git". This is to prevent accidents where
+     you meant "git.a" or "git.b".
+
+  3. The trash directory is _not_ a git repository by default. You
+     should create one with the appropriate version of git.
+
+At the end of the script, call test_done as usual.
diff --git a/t/interop/i0000-basic.sh b/t/interop/i0000-basic.sh
new file mode 100755 (executable)
index 0000000..903e919
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/sh
+
+# Note that this test only works on real version numbers,
+# as it depends on matching the output to "git version".
+VERSION_A=v1.6.6.3
+VERSION_B=v2.11.1
+
+test_description='sanity test interop library'
+. ./interop-lib.sh
+
+test_expect_success 'bare git is forbidden' '
+       test_must_fail git version
+'
+
+test_expect_success "git.a version ($VERSION_A)" '
+       echo git version ${VERSION_A#v} >expect &&
+       git.a version >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success "git.b version ($VERSION_B)" '
+       echo git version ${VERSION_B#v} >expect &&
+       git.b version >actual &&
+       test_cmp expect actual
+'
+
+test_done
diff --git a/t/interop/i5500-git-daemon.sh b/t/interop/i5500-git-daemon.sh
new file mode 100755 (executable)
index 0000000..1daf694
--- /dev/null
@@ -0,0 +1,41 @@
+#!/bin/sh
+
+VERSION_A=.
+VERSION_B=v1.0.0
+
+: ${LIB_GIT_DAEMON_PORT:=5500}
+LIB_GIT_DAEMON_COMMAND='git.a daemon'
+
+test_description='clone and fetch by older client'
+. ./interop-lib.sh
+. "$TEST_DIRECTORY"/lib-git-daemon.sh
+
+start_git_daemon --export-all
+
+repo=$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo
+
+test_expect_success "create repo served by $VERSION_A" '
+       git.a init "$repo" &&
+       git.a -C "$repo" commit --allow-empty -m one
+'
+
+test_expect_success "clone with $VERSION_B" '
+       git.b clone "$GIT_DAEMON_URL/repo" child &&
+       echo one >expect &&
+       git.a -C child log -1 --format=%s >actual &&
+       test_cmp expect actual
+'
+
+test_expect_success "fetch with $VERSION_B" '
+       git.a -C "$repo" commit --allow-empty -m two &&
+       (
+               cd child &&
+               git.b fetch
+       ) &&
+       echo two >expect &&
+       git.a -C child log -1 --format=%s FETCH_HEAD >actual &&
+       test_cmp expect actual
+'
+
+stop_git_daemon
+test_done
diff --git a/t/interop/interop-lib.sh b/t/interop/interop-lib.sh
new file mode 100644 (file)
index 0000000..3e0a291
--- /dev/null
@@ -0,0 +1,92 @@
+# Interoperability testing framework. Each script should source
+# this after setting default $VERSION_A and $VERSION_B variables.
+
+. ../../GIT-BUILD-OPTIONS
+INTEROP_ROOT=$(pwd)
+BUILD_ROOT=$INTEROP_ROOT/build
+
+build_version () {
+       if test -z "$1"
+       then
+               echo >&2 "error: test script did not set default versions"
+               return 1
+       fi
+
+       if test "$1" = "."
+       then
+               git rev-parse --show-toplevel
+               return 0
+       fi
+
+       sha1=$(git rev-parse "$1^{tree}") || return 1
+       dir=$BUILD_ROOT/$sha1
+
+       if test -e "$dir/.built"
+       then
+               echo "$dir"
+               return 0
+       fi
+
+       echo >&2 "==> Building $1..."
+
+       mkdir -p "$dir" || return 1
+
+       (cd "$(git rev-parse --show-cdup)" && git archive --format=tar "$sha1") |
+       (cd "$dir" && tar x) ||
+       return 1
+
+       for config in config.mak config.mak.autogen config.status
+       do
+               if test -e "$INTEROP_ROOT/../../$config"
+               then
+                       cp "$INTEROP_ROOT/../../$config" "$dir/" || return 1
+               fi
+       done
+
+       (
+               cd "$dir" &&
+               make $GIT_INTEROP_MAKE_OPTS >&2 &&
+               touch .built
+       ) || return 1
+
+       echo "$dir"
+}
+
+# Old versions of git don't have bin-wrappers, so let's give a rough emulation.
+wrap_git () {
+       write_script "$1" <<-EOF
+       GIT_EXEC_PATH="$2"
+       export GIT_EXEC_PATH
+       PATH="$2:\$PATH"
+       export GIT_EXEC_PATH
+       exec git "\$@"
+       EOF
+}
+
+generate_wrappers () {
+       mkdir -p .bin &&
+       wrap_git .bin/git.a "$DIR_A" &&
+       wrap_git .bin/git.b "$DIR_B" &&
+       write_script .bin/git <<-\EOF &&
+       echo >&2 fatal: test tried to run generic git
+       exit 1
+       EOF
+       PATH=$(pwd)/.bin:$PATH
+}
+
+VERSION_A=${GIT_TEST_VERSION_A:-$VERSION_A}
+VERSION_B=${GIT_TEST_VERSION_B:-$VERSION_B}
+
+if ! DIR_A=$(build_version "$VERSION_A") ||
+   ! DIR_B=$(build_version "$VERSION_B")
+then
+       echo >&2 "fatal: unable to build git versions"
+       exit 1
+fi
+
+TEST_DIRECTORY=$INTEROP_ROOT/..
+TEST_OUTPUT_DIRECTORY=$INTEROP_ROOT
+TEST_NO_CREATE_REPO=t
+. "$TEST_DIRECTORY"/test-lib.sh
+
+generate_wrappers || die "unable to set up interop test environment"
index f9cbd47..987d406 100644 (file)
@@ -46,7 +46,8 @@ start_git_daemon() {
 
        say >&3 "Starting git daemon ..."
        mkfifo git_daemon_output
-       git daemon --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
+       ${LIB_GIT_DAEMON_COMMAND:-git daemon} \
+               --listen=127.0.0.1 --port="$LIB_GIT_DAEMON_PORT" \
                --reuseaddr --verbose \
                --base-path="$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
                "$@" "$GIT_DAEMON_DOCUMENT_ROOT_PATH" \
index 16359d5..ebf1724 100755 (executable)
@@ -15,7 +15,8 @@ test_perf 'rev-list --all --objects' '
 '
 
 test_expect_success 'create new unreferenced commit' '
-       commit=$(git commit-tree HEAD^{tree} -p HEAD)
+       commit=$(git commit-tree HEAD^{tree} -p HEAD) &&
+       test_export commit
 '
 
 test_perf 'rev-list $commit --not --all' '
index 15ee5d1..b029586 100755 (executable)
@@ -16,4 +16,9 @@ test_perf 'noop filter' '
        git filter-branch -f base..HEAD
 '
 
+test_perf 'noop prune-empty' '
+       git checkout --detach tip &&
+       git filter-branch -f --prune-empty base..HEAD
+'
+
 test_done
index 46f08ee..ab4b8b0 100644 (file)
@@ -83,7 +83,7 @@ test_perf_create_repo_from () {
        error "bug in the test script: not 2 parameters to test-create-repo"
        repo="$1"
        source="$2"
-       source_git="$(git -C "$source" rev-parse --git-dir)"
+       source_git="$("$MODERN_GIT" -C "$source" rev-parse --git-dir)"
        objects_dir="$("$MODERN_GIT" -C "$source" rev-parse --git-path objects)"
        mkdir -p "$repo/.git"
        (
@@ -102,7 +102,7 @@ test_perf_create_repo_from () {
        ) &&
        (
                cd "$repo" &&
-               git init -q && {
+               "$MODERN_GIT" init -q && {
                        test_have_prereq SYMLINKS ||
                        git config core.symlinks false
                } &&
index e8adeda..c788d71 100755 (executable)
@@ -63,6 +63,9 @@ run_dirs_helper () {
                unset GIT_TEST_INSTALLED
        else
                GIT_TEST_INSTALLED="$mydir/bin-wrappers"
+               # Older versions of git lacked bin-wrappers; fallback to the
+               # files in the root.
+               test -d "$GIT_TEST_INSTALLED" || GIT_TEST_INSTALLED=$mydir
                export GIT_TEST_INSTALLED
        fi
        run_one_dir "$@"
index e0a6940..58c0b7e 100755 (executable)
@@ -56,5 +56,13 @@ test_expect_success 'merge @{-100} before checking out that many branches yet' '
        test_must_fail git merge @{-100}
 '
 
+test_expect_success 'log -g @{-1}' '
+       git checkout -b last_branch &&
+       git checkout -b new_branch &&
+       echo "last_branch@{0}" >expect &&
+       git log -g --format=%gd @{-1} >actual &&
+       test_cmp expect actual
+'
+
 test_done
 
diff --git a/t/t3204-branch-name-interpretation.sh b/t/t3204-branch-name-interpretation.sh
new file mode 100755 (executable)
index 0000000..698d9cc
--- /dev/null
@@ -0,0 +1,133 @@
+#!/bin/sh
+
+test_description='interpreting exotic branch name arguments
+
+Branch name arguments are usually names which are taken to be inside of
+refs/heads/, but we interpret some magic syntax like @{-1}, @{upstream}, etc.
+This script aims to check the behavior of those corner cases.
+'
+. ./test-lib.sh
+
+expect_branch() {
+       git log -1 --format=%s "$1" >actual &&
+       echo "$2" >expect &&
+       test_cmp expect actual
+}
+
+expect_deleted() {
+       test_must_fail git rev-parse --verify "$1"
+}
+
+test_expect_success 'set up repo' '
+       test_commit one &&
+       test_commit two &&
+       git remote add origin foo.git
+'
+
+test_expect_success 'update branch via @{-1}' '
+       git branch previous one &&
+
+       git checkout previous &&
+       git checkout master &&
+
+       git branch -f @{-1} two &&
+       expect_branch previous two
+'
+
+test_expect_success 'update branch via local @{upstream}' '
+       git branch local one &&
+       git branch --set-upstream-to=local &&
+
+       git branch -f @{upstream} two &&
+       expect_branch local two
+'
+
+test_expect_success 'disallow updating branch via remote @{upstream}' '
+       git update-ref refs/remotes/origin/remote one &&
+       git branch --set-upstream-to=origin/remote &&
+
+       test_must_fail git branch -f @{upstream} two
+'
+
+test_expect_success 'create branch with pseudo-qualified name' '
+       git branch refs/heads/qualified two &&
+       expect_branch refs/heads/refs/heads/qualified two
+'
+
+test_expect_success 'delete branch via @{-1}' '
+       git branch previous-del &&
+
+       git checkout previous-del &&
+       git checkout master &&
+
+       git branch -D @{-1} &&
+       expect_deleted previous-del
+'
+
+test_expect_success 'delete branch via local @{upstream}' '
+       git branch local-del &&
+       git branch --set-upstream-to=local-del &&
+
+       git branch -D @{upstream} &&
+       expect_deleted local-del
+'
+
+test_expect_success 'delete branch via remote @{upstream}' '
+       git update-ref refs/remotes/origin/remote-del two &&
+       git branch --set-upstream-to=origin/remote-del &&
+
+       git branch -r -D @{upstream} &&
+       expect_deleted origin/remote-del
+'
+
+# Note that we create two oddly named local branches here. We want to make
+# sure that we do not accidentally delete either of them, even if
+# shorten_unambiguous_ref() tweaks the name to avoid ambiguity.
+test_expect_success 'delete @{upstream} expansion matches -r option' '
+       git update-ref refs/remotes/origin/remote-del two &&
+       git branch --set-upstream-to=origin/remote-del &&
+       git update-ref refs/heads/origin/remote-del two &&
+       git update-ref refs/heads/remotes/origin/remote-del two &&
+
+       test_must_fail git branch -D @{upstream} &&
+       expect_branch refs/heads/origin/remote-del two &&
+       expect_branch refs/heads/remotes/origin/remote-del two
+'
+
+test_expect_success 'disallow deleting remote branch via @{-1}' '
+       git update-ref refs/remotes/origin/previous one &&
+
+       git checkout -b origin/previous two &&
+       git checkout master &&
+
+       test_must_fail git branch -r -D @{-1} &&
+       expect_branch refs/remotes/origin/previous one &&
+       expect_branch refs/heads/origin/previous two
+'
+
+# The thing we are testing here is that "@" is the real branch refs/heads/@,
+# and not refs/heads/HEAD. These tests should not imply that refs/heads/@ is a
+# sane thing, but it _is_ technically allowed for now. If we disallow it, these
+# can be switched to test_must_fail.
+test_expect_success 'create branch named "@"' '
+       git branch -f @ one &&
+       expect_branch refs/heads/@ one
+'
+
+test_expect_success 'delete branch named "@"' '
+       git update-ref refs/heads/@ two &&
+       git branch -D @ &&
+       expect_deleted refs/heads/@
+'
+
+test_expect_success 'checkout does not treat remote @{upstream} as a branch' '
+       git update-ref refs/remotes/origin/checkout one &&
+       git branch --set-upstream-to=origin/checkout &&
+       git update-ref refs/heads/origin/checkout two &&
+       git update-ref refs/heads/remotes/origin/checkout two &&
+
+       git checkout @{upstream} &&
+       expect_branch HEAD one
+'
+
+test_done
index 5aa6db5..3c63455 100755 (executable)
@@ -658,7 +658,7 @@ test_expect_success 'rm of a populated nested submodule with nested untracked fi
        test_cmp expect actual
 '
 
-test_expect_success 'rm of a populated nested submodule with a nested .git directory fails even when forced' '
+test_expect_success "rm absorbs submodule's nested .git directory" '
        git reset --hard &&
        git submodule update --recursive &&
        (cd submod/subsubmod &&
index f0bf50b..7c4903f 100755 (executable)
@@ -19,4 +19,9 @@ test_expect_success '-G matches' '
        test 4096-zeroes.txt = "$(cat out)"
 '
 
+test_expect_success '-S --pickaxe-regex' '
+       git diff --name-only -S0 --pickaxe-regex HEAD^ >out &&
+       verbose test 4096-zeroes.txt = "$(cat out)"
+'
+
 test_done
diff --git a/t/t5316-pack-delta-depth.sh b/t/t5316-pack-delta-depth.sh
new file mode 100755 (executable)
index 0000000..37143ea
--- /dev/null
@@ -0,0 +1,93 @@
+#!/bin/sh
+
+test_description='pack-objects breaks long cross-pack delta chains'
+. ./test-lib.sh
+
+# This mirrors a repeated push setup:
+#
+# 1. A client repeatedly modifies some files, makes a
+#      commit, and pushes the result. It does this N times
+#      before we get around to repacking.
+#
+# 2. Each push generates a thin pack with the new version of
+#    various objects. Let's consider some file in the root tree
+#    which is updated in each commit.
+#
+#    When generating push number X, we feed commit X-1 (and
+#    thus blob X-1) as a preferred base. The resulting pack has
+#    blob X as a thin delta against blob X-1.
+#
+#    On the receiving end, "index-pack --fix-thin" will
+#    complete the pack with a base copy of blob X-1.
+#
+# 3. In older versions of git, if we used the delta from
+#    pack X, then we'd always find blob X-1 as a base in the
+#    same pack (and generate a fresh delta).
+#
+#    But with the pack mru, we jump from delta to delta
+#    following the traversal order:
+#
+#      a. We grab blob X from pack X as a delta, putting it at
+#         the tip of our mru list.
+#
+#      b. Eventually we move onto commit X-1. We need other
+#         objects which are only in pack X-1 (in the test code
+#         below, it's the containing tree). That puts pack X-1
+#         at the tip of our mru list.
+#
+#      c. Eventually we look for blob X-1, and we find the
+#         version in pack X-1 (because it's the mru tip).
+#
+# Now we have blob X as a delta against X-1, which is a delta
+# against X-2, and so forth.
+#
+# In the real world, these small pushes would get exploded by
+# unpack-objects rather than "index-pack --fix-thin", but the
+# same principle applies to larger pushes (they only need one
+# repeatedly-modified file to generate the delta chain).
+
+test_expect_success 'create series of packs' '
+       test-genrandom foo 4096 >content &&
+       prev= &&
+       for i in $(test_seq 1 10)
+       do
+               cat content >file &&
+               echo $i >>file &&
+               git add file &&
+               git commit -m $i &&
+               cur=$(git rev-parse HEAD^{tree}) &&
+               {
+                       test -n "$prev" && echo "-$prev"
+                       echo $cur
+                       echo "$(git rev-parse :file) file"
+               } | git pack-objects --stdout >tmp &&
+               git index-pack --stdin --fix-thin <tmp || return 1
+               prev=$cur
+       done
+'
+
+max_chain() {
+       git index-pack --verify-stat-only "$1" >output &&
+       perl -lne '
+         /chain length = (\d+)/ and $len = $1;
+         END { print $len }
+       ' output
+}
+
+# Note that this whole setup is pretty reliant on the current
+# packing heuristics. We double-check that our test case
+# actually produces a long chain. If it doesn't, it should be
+# adjusted (or scrapped if the heuristics have become too unreliable)
+test_expect_success 'packing produces a long delta' '
+       # Use --window=0 to make sure we are seeing reused deltas,
+       # not computing a new long chain.
+       pack=$(git pack-objects --all --window=0 </dev/null pack) &&
+       test 9 = "$(max_chain pack-$pack.pack)"
+'
+
+test_expect_success '--depth limits depth' '
+       pack=$(git pack-objects --all --depth=5 </dev/null pack) &&
+       test 5 = "$(max_chain pack-$pack.pack)"
+'
+
+test_done
index 505e1b4..b5865b3 100755 (executable)
@@ -484,7 +484,7 @@ test_expect_success 'test lonely missing ref' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy
        ) >/dev/null 2>error-m &&
-       test_cmp expect-error error-m
+       test_i18ncmp expect-error error-m
 '
 
 test_expect_success 'test missing ref after existing' '
@@ -492,7 +492,7 @@ test_expect_success 'test missing ref after existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/A refs/heads/xyzzy
        ) >/dev/null 2>error-em &&
-       test_cmp expect-error error-em
+       test_i18ncmp expect-error error-em
 '
 
 test_expect_success 'test missing ref before existing' '
@@ -500,7 +500,7 @@ test_expect_success 'test missing ref before existing' '
                cd client &&
                test_must_fail git fetch-pack --no-progress .. refs/heads/xyzzy refs/heads/A
        ) >/dev/null 2>error-me &&
-       test_cmp expect-error error-me
+       test_i18ncmp expect-error error-me
 '
 
 test_expect_success 'test --all, --depth, and explicit head' '
index 0fc5a7c..177897e 100755 (executable)
@@ -1098,7 +1098,8 @@ test_expect_success 'fetch exact SHA1' '
                test_must_fail git cat-file -t $the_commit &&
 
                # fetching the hidden object should fail by default
-               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy &&
+               test_must_fail git fetch -v ../testrepo $the_commit:refs/heads/copy 2>err &&
+               test_i18ngrep "Server does not allow request for unadvertised object" err &&
                test_must_fail git rev-parse --verify refs/heads/copy &&
 
                # the server side can allow it to succeed
index 26ebb03..d2d883f 100755 (executable)
@@ -77,6 +77,7 @@ test_expect_success 'mix of quoted and unquoted alternates' '
        check_obj "$quoted:$unquoted" <<-EOF
        $one blob
        $two blob
+       EOF
 '
 
 test_expect_success !MINGW 'broken quoting falls back to interpreting raw' '
index cb8fbd8..7cb6079 100755 (executable)
@@ -313,6 +313,27 @@ test_expect_success 'Tag name filtering allows slashes in tag names' '
        git cat-file tag X/2 > actual &&
        test_cmp expect actual
 '
+test_expect_success 'setup --prune-empty comparisons' '
+       git checkout --orphan master-no-a &&
+       git rm -rf . &&
+       unset test_tick &&
+       test_tick &&
+       GIT_COMMITTER_DATE="@0 +0000" GIT_AUTHOR_DATE="@0 +0000" &&
+       test_commit --notick B B.t B Bx &&
+       git checkout -b branch-no-a Bx &&
+       test_commit D D.t D Dx &&
+       mkdir dir &&
+       test_commit dir/D dir/D.t dir/D dir/Dx &&
+       test_commit E E.t E Ex &&
+       git checkout master-no-a &&
+       test_commit C C.t C Cx &&
+       git checkout branch-no-a &&
+       git merge Cx -m "Merge tag '\''C'\'' into branch" &&
+       git tag Fx &&
+       test_commit G G.t G Gx &&
+       test_commit H H.t H Hx &&
+       git checkout branch
+'
 
 test_expect_success 'Prune empty commits' '
        git rev-list HEAD > expect &&
@@ -341,6 +362,22 @@ test_expect_success 'prune empty works even without index/tree filters' '
        test_cmp expect actual
 '
 
+test_expect_success '--prune-empty is able to prune root commit' '
+       git rev-list branch-no-a >expect &&
+       git branch testing H &&
+       git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t" testing &&
+       git rev-list testing >actual &&
+       git branch -D testing &&
+       test_cmp expect actual
+'
+
+test_expect_success '--prune-empty is able to prune entire branch' '
+       git branch prune-entire B &&
+       git filter-branch -f --prune-empty --index-filter "git update-index --remove A.t B.t" prune-entire &&
+       test_path_is_missing .git/refs/heads/prune-entire &&
+       test_must_fail git reflog exists refs/heads/prune-entire
+'
+
 test_expect_success '--remap-to-ancestor with filename filters' '
        git checkout master &&
        git reset --hard A &&
index 072e6c6..b8ad076 100755 (executable)
@@ -879,18 +879,16 @@ test_expect_success GPG 'verifying a forged tag should fail' '
        test_must_fail git tag -v forged-tag
 '
 
-test_expect_success 'verifying a proper tag with --format pass and format accordingly' '
-       cat >expect <<-\EOF
+test_expect_success GPG 'verifying a proper tag with --format pass and format accordingly' '
+       cat >expect <<-\EOF &&
        tagname : signed-tag
-       EOF &&
+       EOF
        git tag -v --format="tagname : %(tag)" "signed-tag" >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
-       cat >expect <<-\EOF
-       tagname : forged-tag
-       EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+       >expect &&
        test_must_fail git tag -v --format="tagname : %(tag)" "forged-tag" >actual &&
        test_cmp expect actual
 '
index d62ccbb..b4b49ee 100755 (executable)
@@ -125,18 +125,16 @@ test_expect_success GPG 'verify multiple tags' '
        test_cmp expect.stderr actual.stderr
 '
 
-test_expect_success 'verifying tag with --format' '
-       cat >expect <<-\EOF
+test_expect_success GPG 'verifying tag with --format' '
+       cat >expect <<-\EOF &&
        tagname : fourth-signed
-       EOF &&
+       EOF
        git verify-tag --format="tagname : %(tag)" "fourth-signed" >actual &&
        test_cmp expect actual
 '
 
-test_expect_success 'verifying a forged tag with --format fail and format accordingly' '
-       cat >expect <<-\EOF
-       tagname : 7th forged-signed
-       EOF &&
+test_expect_success GPG 'verifying a forged tag with --format should fail silently' '
+       >expect &&
        test_must_fail git verify-tag --format="tagname : %(tag)" $(cat forged1.tag) >actual-forged &&
        test_cmp expect actual-forged
 '
index 347857f..4ac386d 100755 (executable)
@@ -442,11 +442,11 @@ test_expect_success 'submodule update - command in .git/config catches failure -
 '
 
 test_expect_success 'submodule update - command run for initial population of submodule' '
-       cat <<-\ EOF >expect
+       cat >expect <<-EOF &&
        Execution of '\''false $submodulesha1'\'' failed in submodule path '\''submodule'\''
-       EOF &&
+       EOF
        rm -rf super/submodule &&
-       test_must_fail git -C super submodule update >../actual &&
+       test_must_fail git -C super submodule update 2>actual &&
        test_cmp expect actual &&
        git -C super submodule update --checkout
 '
diff --git a/t/t7518-ident-corner-cases.sh b/t/t7518-ident-corner-cases.sh
new file mode 100755 (executable)
index 0000000..b22f631
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/sh
+
+test_description='corner cases in ident strings'
+. ./test-lib.sh
+
+# confirm that we do not segfault _and_ that we do not say "(null)", as
+# glibc systems will quietly handle our NULL pointer
+#
+# Note also that we can't use "env" here because we need to unset a variable,
+# and "-u" is not portable.
+test_expect_success 'empty name and missing email' '
+       (
+               sane_unset GIT_AUTHOR_EMAIL &&
+               GIT_AUTHOR_NAME= &&
+               test_must_fail git commit --allow-empty -m foo 2>err &&
+               test_i18ngrep ! null err
+       )
+'
+
+test_expect_success 'commit rejects all-crud name' '
+       test_must_fail env GIT_AUTHOR_NAME=" .;<>" \
+               git commit --allow-empty -m foo
+'
+
+# We must test the actual error message here, as an unwanted
+# auto-detection could fail for other reasons.
+test_expect_success 'empty configured name does not auto-detect' '
+       (
+               sane_unset GIT_AUTHOR_NAME &&
+               test_must_fail \
+                       git -c user.name= commit --allow-empty -m foo 2>err &&
+               test_i18ngrep "empty ident name" err
+       )
+'
+
+test_done
index bb879a5..1319415 100755 (executable)
@@ -18,6 +18,11 @@ then
     test_done
 fi
 
+if ! test_have_prereq NOT_ROOT; then
+       skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+       test_done
+fi
+
 CVSROOT=$PWD/tmpcvsroot
 CVSWORK=$PWD/cvswork
 GIT_DIR=$PWD/.git
index 4c384ff..804ce38 100755 (executable)
@@ -3,6 +3,11 @@
 test_description='git cvsimport basic tests'
 . ./lib-cvs.sh
 
+if ! test_have_prereq NOT_ROOT; then
+       skip_all='When cvs is compiled with CVS_BADROOT commits as root fail'
+       test_done
+fi
+
 test_expect_success PERL 'setup cvsroot environment' '
        CVSROOT=$(pwd)/cvsroot &&
        export CVSROOT
index bd35770..5ee1243 100644 (file)
@@ -136,17 +136,12 @@ test_tick () {
        export GIT_COMMITTER_DATE GIT_AUTHOR_DATE
 }
 
-# Stop execution and start a shell. This is useful for debugging tests and
-# only makes sense together with "-v".
+# Stop execution and start a shell. This is useful for debugging tests.
 #
 # Be sure to remove all invocations of this command before submitting.
 
 test_pause () {
-       if test "$verbose" = t; then
-               "$SHELL_PATH" <&6 >&3 2>&4
-       else
-               error >&5 "test_pause requires --verbose"
-       fi
+       "$SHELL_PATH" <&6 >&5 2>&7
 }
 
 # Wrap git in gdb. Adding this to a command can make it easier to
@@ -154,7 +149,7 @@ test_pause () {
 #
 # Example: "debug git checkout master".
 debug () {
-        GIT_TEST_GDB=1 "$@"
+        GIT_TEST_GDB=1 "$@" <&6 >&5 2>&7
 }
 
 # Call test_commit with the arguments
index 86d77c1..23c29bc 100644 (file)
@@ -342,6 +342,7 @@ fi
 
 exec 5>&1
 exec 6<&0
+exec 7>&2
 if test "$verbose_log" = "t"
 then
        exec 3>>"$GIT_TEST_TEE_OUTPUT_FILE" 4>&3
index b6c5652..b58f5c8 100644 (file)
@@ -204,6 +204,7 @@ static struct ref *get_refs_via_connect(struct transport *transport, int for_pus
 static int fetch_refs_via_pack(struct transport *transport,
                               int nr_heads, struct ref **to_fetch)
 {
+       int ret = 0;
        struct git_transport_data *data = transport->data;
        struct ref *refs;
        char *dest = xstrdup(transport->url);
@@ -241,19 +242,22 @@ static int fetch_refs_via_pack(struct transport *transport,
                          &transport->pack_lockfile);
        close(data->fd[0]);
        close(data->fd[1]);
-       if (finish_connect(data->conn)) {
-               free_refs(refs);
-               refs = NULL;
-       }
+       if (finish_connect(data->conn))
+               ret = -1;
        data->conn = NULL;
        data->got_remote_heads = 0;
        data->options.self_contained_and_connected =
                args.self_contained_and_connected;
 
+       if (refs == NULL)
+               ret = -1;
+       if (report_unmatched_refs(to_fetch, nr_heads))
+               ret = -1;
+
        free_refs(refs_tmp);
        free_refs(refs);
        free(dest);
-       return (refs ? 0 : -1);
+       return ret;
 }
 
 static int push_had_errors(struct ref *ref)
index 0486e31..fa7bc67 100644 (file)
@@ -175,7 +175,7 @@ struct worktree **get_worktrees(unsigned flags)
        struct dirent *d;
        int counter = 0, alloc = 2;
 
-       list = xmalloc(alloc * sizeof(struct worktree *));
+       ALLOC_ARRAY(list, alloc);
 
        list[counter++] = get_main_worktree();
 
index 234e77a..4bb4678 100644 (file)
@@ -1785,34 +1785,24 @@ static void wt_shortstatus_print_tracking(struct wt_status *s)
 
 static void wt_shortstatus_print(struct wt_status *s)
 {
-       int i;
+       struct string_list_item *it;
 
        if (s->show_branch)
                wt_shortstatus_print_tracking(s);
 
-       for (i = 0; i < s->change.nr; i++) {
-               struct wt_status_change_data *d;
-               struct string_list_item *it;
+       for_each_string_list_item(it, &s->change) {
+               struct wt_status_change_data *d = it->util;
 
-               it = &(s->change.items[i]);
-               d = it->util;
                if (d->stagemask)
                        wt_shortstatus_unmerged(it, s);
                else
                        wt_shortstatus_status(it, s);
        }
-       for (i = 0; i < s->untracked.nr; i++) {
-               struct string_list_item *it;
-
-               it = &(s->untracked.items[i]);
+       for_each_string_list_item(it, &s->untracked)
                wt_shortstatus_other(it, s, "??");
-       }
-       for (i = 0; i < s->ignored.nr; i++) {
-               struct string_list_item *it;
 
-               it = &(s->ignored.items[i]);
+       for_each_string_list_item(it, &s->ignored)
                wt_shortstatus_other(it, s, "!!");
-       }
 }
 
 static void wt_porcelain_print(struct wt_status *s)