Merge branch 'ap/remote-hg-unquote-cquote' into maint
authorJunio C Hamano <gitster@pobox.com>
Fri, 8 Nov 2013 20:01:13 +0000 (12:01 -0800)
committerJunio C Hamano <gitster@pobox.com>
Fri, 8 Nov 2013 20:01:14 +0000 (12:01 -0800)
A fast-import stream expresses a pathname with funny characters by
quoting them in C style; remote-hg remote helper (in contrib/)
forgot to unquote such a path.

* ap/remote-hg-unquote-cquote:
remote-hg: unquote C-style paths when exporting

58 files changed:
Documentation/RelNotes/1.8.4.2.txt
Documentation/RelNotes/1.8.4.3.txt [new file with mode: 0644]
Documentation/git-branch.txt
Documentation/git-unpack-objects.txt
Documentation/git.txt
Documentation/gitignore.txt
Documentation/howto/setup-git-server-over-http.txt
GIT-VERSION-GEN
RelNotes
bisect.c
branch.c
builtin/checkout.c
builtin/clone.c
builtin/ls-files.c
builtin/pack-objects.c
builtin/rev-list.c
builtin/shortlog.c
cache.h
commit.h
connect.c
contrib/completion/git-prompt.sh
contrib/subtree/Makefile
dir.c
dir.h
fetch-pack.c
graph.c
http-push.c
http.c
http.h
ident.c
list-objects.c
list-objects.h
merge-recursive.c
pretty.c
remote-curl.c
revision.c
sha1_file.c
shallow.c
t/lib-httpd.sh
t/lib-httpd/apache.conf
t/t2010-checkout-ambiguous.sh
t/t2024-checkout-dwim.sh
t/t3010-ls-files-killed-modified.sh
t/t3200-branch.sh
t/t4014-format-patch.sh
t/t4201-shortlog.sh
t/t4212-log-corrupt.sh
t/t5500-fetch-pack.sh
t/t5505-remote.sh
t/t5530-upload-pack-error.sh
t/t5551-http-fetch.sh
t/t5570-git-daemon.sh
t/t5601-clone.sh
t/t5701-clone-local.sh
t/t5702-clone-options.sh
t/t6000-rev-list-misc.sh
t/test-lib.sh
upload-pack.c
index ebe5e68fcba6bfb1d0c4d81afdfd3db717083f0c..9adccb1efb8d3fe8a117fdd13b40abb1cb757fb7 100644 (file)
@@ -4,6 +4,33 @@ Git v1.8.4.2 Release Notes
 Fixes since v1.8.4.1
 --------------------
 
+ * "git clone" gave some progress messages to the standard output, not
+   to the standard error, and did not allow suppressing them with the
+   "--no-progress" option.
+
+ * "format-patch --from=<whom>" forgot to omit unnecessary in-body
+   from line, i.e. when <whom> is the same as the real author.
+
+ * "git shortlog" used to choke and die when there is a malformed
+   commit (e.g. missing authors); it now simply ignore such a commit
+   and keeps going.
+
+ * "git merge-recursive" did not parse its "--diff-algorithm=" command
+   line option correctly.
+
+ * "git branch --track" had a minor regression in v1.8.3.2 and later
+   that made it impossible to base your local work on anything but a
+   local branch of the upstream repository you are tracking from.
+
+ * "git ls-files -k" needs to crawl only the part of the working tree
+   that may overlap the paths in the index to find killed files, but
+   shared code with the logic to find all the untracked files, which
+   made it unnecessarily inefficient.
+
+ * When there is no sufficient overlap between old and new history
+   during a "git fetch" into a shallow repository, objects that the
+   sending side knows the receiving end has were unnecessarily sent.
+
  * When running "fetch -q", a long silence while the sender side
    computes the set of objects to send can be mistaken by proxies as
    dropped connection.  The server side has been taught to send a
diff --git a/Documentation/RelNotes/1.8.4.3.txt b/Documentation/RelNotes/1.8.4.3.txt
new file mode 100644 (file)
index 0000000..afb3359
--- /dev/null
@@ -0,0 +1,36 @@
+Git v1.8.4.3 Release Notes
+========================
+
+Fixes since v1.8.4.2
+--------------------
+
+ * "git rev-list --objects ^v1.0^ v1.0" gave v1.0 tag itself in the
+   output, but "git rev-list --objects v1.0^..v1.0" did not.
+
+ * The fall-back parsing of commit objects with broken author or
+   committer lines were less robust than ideal in picking up the
+   timestamps.
+
+ * Bash prompting code to deal with an SVN remote as an upstream
+   were coded in a way not supported by older Bash versions (3.x).
+
+ * "git checkout topic", when there is not yet a local "topic" branch
+   but there is a unique remote-tracking branch for a remote "topic"
+   branch, pretended as if "git checkout -t -b topic remote/$r/topic"
+   (for that unique remote $r) was run. This hack however was not
+   implemented for "git checkout topic --".
+
+ * Coloring around octopus merges in "log --graph" output was screwy.
+
+ * We did not generate HTML version of documentation to "git subtree"
+   in contrib/.
+
+ * The synopsis section of "git unpack-objects" documentation has been
+   clarified a bit.
+
+ * An ancient How-To on serving Git repositories on an HTTP server
+   lacked a warning that it has been mostly superseded with more
+   modern way.
+
+Also contains a handful of trivial code clean-ups, documentation
+updates, updates to the test suite, etc.
index b7cb625b894d4ba83cb13914e1fd010e1bfaf0d3..311b33674eb2bda2c8e0bd5d2de4499a156b1719 100644 (file)
@@ -48,7 +48,8 @@ working tree to it; use "git checkout <newbranch>" to switch to the
 new branch.
 
 When a local branch is started off a remote-tracking branch, Git sets up the
-branch so that 'git pull' will appropriately merge from
+branch (specifically the `branch.<name>.remote` and `branch.<name>.merge`
+configuration entries) so that 'git pull' will appropriately merge from
 the remote-tracking branch. This behavior may be changed via the global
 `branch.autosetupmerge` configuration flag. That setting can be
 overridden by using the `--track` and `--no-track` options, and
@@ -156,7 +157,8 @@ This option is only applicable in non-verbose mode.
 
 -t::
 --track::
-       When creating a new branch, set up configuration to mark the
+       When creating a new branch, set up `branch.<name>.remote` and
+       `branch.<name>.merge` configuration entries to mark the
        start-point branch as "upstream" from the new branch. This
        configuration will tell git to show the relationship between the
        two branches in `git status` and `git branch -v`. Furthermore,
index ff23494e7013c5d491c7a7e2642ec30133906aac..12cb108b8561a6365192a65688d116901468decf 100644 (file)
@@ -9,7 +9,7 @@ git-unpack-objects - Unpack objects from a packed archive
 SYNOPSIS
 --------
 [verse]
-'git unpack-objects' [-n] [-q] [-r] [--strict] <pack-file
+'git unpack-objects' [-n] [-q] [-r] [--strict] < <pack-file>
 
 
 DESCRIPTION
index 5b83e0ab98b1da06c749b016f231584e644a73c0..90c5f37cebd043d2c8cc43a65d99f7b08f63383b 100644 (file)
@@ -43,9 +43,10 @@ unreleased) version of Git, that is available from 'master'
 branch of the `git.git` repository.
 Documentation for older releases are available here:
 
-* link:v1.8.4.1/git.html[documentation for release 1.8.4.1]
+* link:v1.8.4.2/git.html[documentation for release 1.8.4.2]
 
 * release notes for
+  link:RelNotes/1.8.4.2.txt[1.8.4.2],
   link:RelNotes/1.8.4.1.txt[1.8.4.1],
   link:RelNotes/1.8.4.txt[1.8.4].
 
index 54e334e3af1e790b132da5b6af761326d586ce8a..f9719605123003c97bdf2999f912712536fa927a 100644 (file)
@@ -113,12 +113,12 @@ full pathname may have special meaning:
 
  - A leading "`**`" followed by a slash means match in all
    directories. For example, "`**/foo`" matches file or directory
-   "`foo`" anywhere, the same as pattern "`foo`". "**/foo/bar"
+   "`foo`" anywhere, the same as pattern "`foo`". "`**/foo/bar`"
    matches file or directory "`bar`" anywhere that is directly
    under directory "`foo`".
 
- - A trailing "/**" matches everything inside. For example,
-   "abc/**" matches all files inside directory "abc", relative
+ - A trailing "`/**`" matches everything inside. For example,
+   "`abc/**`" matches all files inside directory "`abc`", relative
    to the location of the `.gitignore` file, with infinite depth.
 
  - A slash followed by two consecutive asterisks then a slash
index 7f4943e1025de7013715241145d2122ef16057e3..c8036492e6f559469e0e7a0f966565f0227d06cc 100644 (file)
@@ -6,6 +6,10 @@ Content-type: text/asciidoc
 How to setup Git server over http
 =================================
 
+NOTE: This document is from 2006.  A lot has happened since then, and this
+document is now relevant mainly if your web host is not CGI capable.
+Almost everyone else should instead look at linkgit:git-http-backend[1].
+
 Since Apache is one of those packages people like to compile
 themselves while others prefer the bureaucrat's dream Debian, it is
 impossible to give guidelines which will work for everyone. Just send
index b4147d6db58133bb88d9011d1153410cf9f419ca..d801493b23d389662b5f1fa11e0cde1b3468a614 100755 (executable)
@@ -1,7 +1,7 @@
 #!/bin/sh
 
 GVF=GIT-VERSION-FILE
-DEF_VER=v1.8.4.1
+DEF_VER=v1.8.4.2
 
 LF='
 '
index c3d6c6c277c370119d47a79e6119953efc68409f..13a018df3adc2e8f192700397eeea4588c0c29b3 120000 (symlink)
--- a/RelNotes
+++ b/RelNotes
@@ -1 +1 @@
-Documentation/RelNotes/1.8.4.2.txt
\ No newline at end of file
+Documentation/RelNotes/1.8.4.3.txt
\ No newline at end of file
index 71c19581daccffc87e5128c61d9adfae0be3e7de..1e46a4f50e16f059fa768cecc4c8331a88997021 100644 (file)
--- a/bisect.c
+++ b/bisect.c
@@ -624,7 +624,7 @@ static void bisect_common(struct rev_info *revs)
        if (prepare_revision_walk(revs))
                die("revision walk setup failed");
        if (revs->tree_objects)
-               mark_edges_uninteresting(revs->commits, revs, NULL);
+               mark_edges_uninteresting(revs, NULL);
 }
 
 static void exit_if_skipped_commits(struct commit_list *tried,
index c5c6984cb5266c27d3c13aa6e6aacaab56e112c0..2d15c19e3e8d5d2ae317d0cb0e06a35ea8f5745e 100644 (file)
--- a/branch.c
+++ b/branch.c
@@ -203,8 +203,7 @@ static int check_tracking_branch(struct remote *remote, void *cb_data)
        struct refspec query;
        memset(&query, 0, sizeof(struct refspec));
        query.dst = tracking_branch;
-       return !(remote_find_tracking(remote, &query) ||
-                prefixcmp(query.src, "refs/heads/"));
+       return !remote_find_tracking(remote, &query);
 }
 
 static int validate_remote_tracking_branch(char *ref)
index 7025938ae30f2ce3104b27f66bf6e1f294edb810..22cc7fcb59a508c861dd2b55b4c591c8792f8fdc 100644 (file)
@@ -880,7 +880,9 @@ static int parse_branchname_arg(int argc, const char **argv,
        int argcount = 0;
        unsigned char branch_rev[20];
        const char *arg;
-       int has_dash_dash;
+       int dash_dash_pos;
+       int has_dash_dash = 0;
+       int i;
 
        /*
         * case 1: git checkout <ref> -- [<paths>]
@@ -892,20 +894,30 @@ static int parse_branchname_arg(int argc, const char **argv,
         *
         *   everything after the '--' must be paths.
         *
-        * case 3: git checkout <something> [<paths>]
+        * case 3: git checkout <something> [--]
         *
-        *   With no paths, if <something> is a commit, that is to
-        *   switch to the branch or detach HEAD at it.  As a special case,
-        *   if <something> is A...B (missing A or B means HEAD but you can
-        *   omit at most one side), and if there is a unique merge base
-        *   between A and B, A...B names that merge base.
+        *   (a) If <something> is a commit, that is to
+        *       switch to the branch or detach HEAD at it.  As a special case,
+        *       if <something> is A...B (missing A or B means HEAD but you can
+        *       omit at most one side), and if there is a unique merge base
+        *       between A and B, A...B names that merge base.
         *
-        *   With no paths, if <something> is _not_ a commit, no -t nor -b
-        *   was given, and there is a tracking branch whose name is
-        *   <something> in one and only one remote, then this is a short-hand
-        *   to fork local <something> from that remote-tracking branch.
+        *   (b) If <something> is _not_ a commit, either "--" is present
+        *       or <something> is not a path, no -t nor -b was given, and
+        *       and there is a tracking branch whose name is <something>
+        *       in one and only one remote, then this is a short-hand to
+        *       fork local <something> from that remote-tracking branch.
         *
-        *   Otherwise <something> shall not be ambiguous.
+        *   (c) Otherwise, if "--" is present, treat it like case (1).
+        *
+        *   (d) Otherwise :
+        *       - if it's a reference, treat it like case (1)
+        *       - else if it's a path, treat it like case (2)
+        *       - else: fail.
+        *
+        * case 4: git checkout <something> <paths>
+        *
+        *   The first argument must not be ambiguous.
         *   - If it's *only* a reference, treat it like case (1).
         *   - If it's only a path, treat it like case (2).
         *   - else: fail.
@@ -914,28 +926,59 @@ static int parse_branchname_arg(int argc, const char **argv,
        if (!argc)
                return 0;
 
-       if (!strcmp(argv[0], "--"))     /* case (2) */
-               return 1;
-
        arg = argv[0];
-       has_dash_dash = (argc > 1) && !strcmp(argv[1], "--");
+       dash_dash_pos = -1;
+       for (i = 0; i < argc; i++) {
+               if (!strcmp(argv[i], "--")) {
+                       dash_dash_pos = i;
+                       break;
+               }
+       }
+       if (dash_dash_pos == 0)
+               return 1; /* case (2) */
+       else if (dash_dash_pos == 1)
+               has_dash_dash = 1; /* case (3) or (1) */
+       else if (dash_dash_pos >= 2)
+               die(_("only one reference expected, %d given."), dash_dash_pos);
 
        if (!strcmp(arg, "-"))
                arg = "@{-1}";
 
        if (get_sha1_mb(arg, rev)) {
-               if (has_dash_dash)          /* case (1) */
-                       die(_("invalid reference: %s"), arg);
-               if (dwim_new_local_branch_ok &&
-                   !check_filename(NULL, arg) &&
-                   argc == 1) {
+               /*
+                * Either case (3) or (4), with <something> not being
+                * a commit, or an attempt to use case (1) with an
+                * invalid ref.
+                *
+                * It's likely an error, but we need to find out if
+                * we should auto-create the branch, case (3).(b).
+                */
+               int recover_with_dwim = dwim_new_local_branch_ok;
+
+               if (check_filename(NULL, arg) && !has_dash_dash)
+                       recover_with_dwim = 0;
+               /*
+                * Accept "git checkout foo" and "git checkout foo --"
+                * as candidates for dwim.
+                */
+               if (!(argc == 1 && !has_dash_dash) &&
+                   !(argc == 2 && has_dash_dash))
+                       recover_with_dwim = 0;
+
+               if (recover_with_dwim) {
                        const char *remote = unique_tracking_name(arg, rev);
-                       if (!remote)
-                               return argcount;
-                       *new_branch = arg;
-                       arg = remote;
-                       /* DWIMmed to create local branch */
-               } else {
+                       if (remote) {
+                               *new_branch = arg;
+                               arg = remote;
+                               /* DWIMmed to create local branch, case (3).(b) */
+                       } else {
+                               recover_with_dwim = 0;
+                       }
+               }
+
+               if (!recover_with_dwim) {
+                       if (has_dash_dash)
+                               die(_("invalid reference: %s"), arg);
                        return argcount;
                }
        }
@@ -965,7 +1008,7 @@ static int parse_branchname_arg(int argc, const char **argv,
 
        if (!*source_tree)                   /* case (1): want a tree */
                die(_("reference is not a tree: %s"), arg);
-       if (!has_dash_dash) {/* case (3 -> 1) */
+       if (!has_dash_dash) {/* case (3).(d) -> (1) */
                /*
                 * Do not complain the most common case
                 *      git checkout branch
index 21ad9f945c019625ace5f5dba0d4e54a8b2bb70f..552f3409e3056846b45c6f300661dc5c14302b0b 100644 (file)
@@ -380,7 +380,7 @@ static void clone_local(const char *src_repo, const char *dest_repo)
        }
 
        if (0 <= option_verbosity)
-               printf(_("done.\n"));
+               fprintf(stderr, _("done.\n"));
 }
 
 static const char *junk_work_tree;
@@ -551,13 +551,13 @@ static void update_remote_refs(const struct ref *refs,
        const struct ref *rm = mapped_refs;
 
        if (check_connectivity) {
-               if (0 <= option_verbosity)
-                       printf(_("Checking connectivity... "));
+               if (transport->progress)
+                       fprintf(stderr, _("Checking connectivity... "));
                if (check_everything_connected_with_transport(iterate_ref_map,
                                                              0, &rm, transport))
                        die(_("remote did not send all necessary objects"));
-               if (0 <= option_verbosity)
-                       printf(_("done\n"));
+               if (transport->progress)
+                       fprintf(stderr, _("done\n"));
        }
 
        if (refs) {
@@ -850,9 +850,9 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
 
        if (0 <= option_verbosity) {
                if (option_bare)
-                       printf(_("Cloning into bare repository '%s'...\n"), dir);
+                       fprintf(stderr, _("Cloning into bare repository '%s'...\n"), dir);
                else
-                       printf(_("Cloning into '%s'...\n"), dir);
+                       fprintf(stderr, _("Cloning into '%s'...\n"), dir);
        }
        init_db(option_template, INIT_DB_QUIET);
        write_config(&option_config);
@@ -885,27 +885,25 @@ int cmd_clone(int argc, const char **argv, const char *prefix)
        remote = remote_get(option_origin);
        transport = transport_get(remote, remote->url[0]);
 
-       if (!is_local) {
-               if (!transport->get_refs_list || !transport->fetch)
-                       die(_("Don't know how to clone %s"), transport->url);
+       if (!transport->get_refs_list || (!is_local && !transport->fetch))
+               die(_("Don't know how to clone %s"), transport->url);
 
-               transport_set_option(transport, TRANS_OPT_KEEP, "yes");
+       transport_set_option(transport, TRANS_OPT_KEEP, "yes");
 
-               if (option_depth)
-                       transport_set_option(transport, TRANS_OPT_DEPTH,
-                                            option_depth);
-               if (option_single_branch)
-                       transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
+       if (option_depth)
+               transport_set_option(transport, TRANS_OPT_DEPTH,
+                                    option_depth);
+       if (option_single_branch)
+               transport_set_option(transport, TRANS_OPT_FOLLOWTAGS, "1");
 
-               transport_set_verbosity(transport, option_verbosity, option_progress);
+       transport_set_verbosity(transport, option_verbosity, option_progress);
 
-               if (option_upload_pack)
-                       transport_set_option(transport, TRANS_OPT_UPLOADPACK,
-                                            option_upload_pack);
+       if (option_upload_pack)
+               transport_set_option(transport, TRANS_OPT_UPLOADPACK,
+                                    option_upload_pack);
 
-               if (transport->smart_options && !option_depth)
-                       transport->smart_options->check_self_contained_and_connected = 1;
-       }
+       if (transport->smart_options && !option_depth)
+               transport->smart_options->check_self_contained_and_connected = 1;
 
        refs = transport_get_remote_refs(transport);
 
index 5cf3e313700e4cbcd26ae1a9f3f99f2819e40702..85004460bd383736490580735417208607dc3481 100644 (file)
@@ -219,6 +219,8 @@ static void show_files(struct dir_struct *dir)
 
        /* For cached/deleted files we don't need to even do the readdir */
        if (show_others || show_killed) {
+               if (!show_others)
+                       dir->flags |= DIR_COLLECT_KILLED_ONLY;
                fill_directory(dir, pathspec);
                if (show_others)
                        show_other_files(dir);
index f069462cb03bbae46dae8d6b97420b3726690c47..dd117b379ac9d7b58b439a691b90c6cb4f1dac90 100644 (file)
@@ -2378,7 +2378,7 @@ static void get_object_list(int ac, const char **av)
 
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
-       mark_edges_uninteresting(revs.commits, &revs, show_edge);
+       mark_edges_uninteresting(&revs, show_edge);
        traverse_commit_list(&revs, show_commit, show_object, NULL);
 
        if (keep_unreachable)
index a5ec30d74ee1945ddaf99c89307e59aec714714a..4fc16166374b86927e605ccbfe341aebe0e3240f 100644 (file)
@@ -336,7 +336,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        if (prepare_revision_walk(&revs))
                die("revision walk setup failed");
        if (revs.tree_objects)
-               mark_edges_uninteresting(revs.commits, &revs, show_edge);
+               mark_edges_uninteresting(&revs, show_edge);
 
        if (bisect_list) {
                int reaches = reaches, all = all;
index 1434f8fee487751abe069250c611bdaed2fa5857..84ffbb798e2e0ec8c1121790c04ccbb58b05e591 100644 (file)
@@ -127,9 +127,11 @@ void shortlog_add_commit(struct shortlog *log, struct commit *commit)
                        author = buffer + 7;
                buffer = eol;
        }
-       if (!author)
-               die(_("Missing author: %s"),
+       if (!author) {
+               warning(_("Missing author: %s"),
                    sha1_to_hex(commit->object.sha1));
+               return;
+       }
        if (log->user_format) {
                struct pretty_print_context ctx = {0};
                ctx.fmt = CMIT_FMT_USERFORMAT;
diff --git a/cache.h b/cache.h
index 85b544f38d934fe68d1e155c86337f1400eea14d..415d8830809cbb8a9651004184a01315ee8c7c3e 100644 (file)
--- a/cache.h
+++ b/cache.h
@@ -966,6 +966,15 @@ struct ident_split {
  */
 extern int split_ident_line(struct ident_split *, const char *, int);
 
+/*
+ * Compare split idents for equality or strict ordering. Note that we
+ * compare only the ident part of the line, ignoring any timestamp.
+ *
+ * Because there are two fields, we must choose one as the primary key; we
+ * currently arbitrarily pick the email.
+ */
+extern int ident_cmp(const struct ident_split *, const struct ident_split *);
+
 struct checkout {
        const char *base_dir;
        int base_dir_len;
@@ -1098,7 +1107,6 @@ extern struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
 extern int server_supports(const char *feature);
 extern int parse_feature_request(const char *features, const char *feature);
 extern const char *server_feature_value(const char *feature, int *len_ret);
-extern const char *parse_feature_value(const char *feature_list, const char *feature, int *len_ret);
 
 extern struct packed_git *parse_pack_index(unsigned char *sha1, const char *idx_path);
 
index f9504f70cc5b57de590099024556b9d2b6b2cdce..c308674120112f09ff8a9625e357c62e2029bbba 100644 (file)
--- a/commit.h
+++ b/commit.h
@@ -201,6 +201,10 @@ extern struct commit_list *get_shallow_commits(struct object_array *heads,
                int depth, int shallow_flag, int not_shallow_flag);
 extern void check_shallow_file_for_update(void);
 extern void set_alternate_shallow_file(const char *path);
+extern int write_shallow_commits(struct strbuf *out, int use_pack_protocol);
+extern void setup_alternate_shallow(struct lock_file *shallow_lock,
+                                   const char **alternate_shallow_file);
+extern char *setup_temporary_shallow(void);
 
 int is_descendant_of(struct commit *, struct commit_list *);
 int in_merge_bases(struct commit *, struct commit *);
index a0783d4867c5e6a9496e11ed043f666a402c5db9..553a80c734dc2a4420ee8c0db4491edead53b947 100644 (file)
--- a/connect.c
+++ b/connect.c
@@ -6,8 +6,10 @@
 #include "run-command.h"
 #include "remote.h"
 #include "url.h"
+#include "string-list.h"
 
 static char *server_capabilities;
+static const char *parse_feature_value(const char *, const char *, int *);
 
 static int check_ref(const char *name, int len, unsigned int flags)
 {
@@ -59,6 +61,61 @@ static void die_initial_contact(int got_at_least_one_head)
                    "and the repository exists.");
 }
 
+static void parse_one_symref_info(struct string_list *symref, const char *val, int len)
+{
+       char *sym, *target;
+       struct string_list_item *item;
+
+       if (!len)
+               return; /* just "symref" */
+       /* e.g. "symref=HEAD:refs/heads/master" */
+       sym = xmalloc(len + 1);
+       memcpy(sym, val, len);
+       sym[len] = '\0';
+       target = strchr(sym, ':');
+       if (!target)
+               /* just "symref=something" */
+               goto reject;
+       *(target++) = '\0';
+       if (check_refname_format(sym, REFNAME_ALLOW_ONELEVEL) ||
+           check_refname_format(target, REFNAME_ALLOW_ONELEVEL))
+               /* "symref=bogus:pair */
+               goto reject;
+       item = string_list_append(symref, sym);
+       item->util = target;
+       return;
+reject:
+       free(sym);
+       return;
+}
+
+static void annotate_refs_with_symref_info(struct ref *ref)
+{
+       struct string_list symref = STRING_LIST_INIT_DUP;
+       const char *feature_list = server_capabilities;
+
+       while (feature_list) {
+               int len;
+               const char *val;
+
+               val = parse_feature_value(feature_list, "symref", &len);
+               if (!val)
+                       break;
+               parse_one_symref_info(&symref, val, len);
+               feature_list = val + 1;
+       }
+       sort_string_list(&symref);
+
+       for (; ref; ref = ref->next) {
+               struct string_list_item *item;
+               item = string_list_lookup(&symref, ref->name);
+               if (!item)
+                       continue;
+               ref->symref = xstrdup((char *)item->util);
+       }
+       string_list_clear(&symref, 0);
+}
+
 /*
  * Read all the refs from the other end
  */
@@ -66,6 +123,7 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                              struct ref **list, unsigned int flags,
                              struct extra_have_objects *extra_have)
 {
+       struct ref **orig_list = list;
        int got_at_least_one_head = 0;
 
        *list = NULL;
@@ -113,10 +171,13 @@ struct ref **get_remote_heads(int in, char *src_buf, size_t src_len,
                list = &ref->next;
                got_at_least_one_head = 1;
        }
+
+       annotate_refs_with_symref_info(*orig_list);
+
        return list;
 }
 
-const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
+static const char *parse_feature_value(const char *feature_list, const char *feature, int *lenp)
 {
        int len;
 
index d6c61b2bdecc5ca89e21ece5df7c2e4bbec9373c..998722cf7f5d65639654f9f7e74f4c2b3d5452f3 100644 (file)
@@ -110,7 +110,7 @@ __git_ps1_show_upstream ()
                        ;;
                svn-remote.*.url)
                        svn_remote[$((${#svn_remote[@]} + 1))]="$value"
-                       svn_url_pattern+="\\|$value"
+                       svn_url_pattern="$svn_url_pattern\\|$value"
                        upstream=svn+git # default upstream is SVN if available, else git
                        ;;
                esac
index 435b2dea293b01daaad83e6905148be2256c02df..4030a168986d29bdbb2cd982a2e819ce009580f6 100644 (file)
@@ -21,13 +21,14 @@ GIT_SUBTREE    := git-subtree
 GIT_SUBTREE_DOC := git-subtree.1
 GIT_SUBTREE_XML := git-subtree.xml
 GIT_SUBTREE_TXT := git-subtree.txt
+GIT_SUBTREE_HTML := git-subtree.html
 
 all: $(GIT_SUBTREE)
 
 $(GIT_SUBTREE): $(GIT_SUBTREE_SH)
        cp $< $@ && chmod +x $@
 
-doc: $(GIT_SUBTREE_DOC)
+doc: $(GIT_SUBTREE_DOC) $(GIT_SUBTREE_HTML)
 
 install: $(GIT_SUBTREE)
        $(INSTALL) -d -m 755 $(DESTDIR)$(libexecdir)
@@ -46,6 +47,10 @@ $(GIT_SUBTREE_XML): $(GIT_SUBTREE_TXT)
        asciidoc -b docbook -d manpage -f $(ASCIIDOC_CONF) \
                -agit_version=$(gitver) $^
 
+$(GIT_SUBTREE_HTML): $(GIT_SUBTREE_TXT)
+       asciidoc -b xhtml11 -d manpage -f $(ASCIIDOC_CONF) \
+               -agit_version=$(gitver) $^
+
 test:
        $(MAKE) -C t/ test
 
diff --git a/dir.c b/dir.c
index 910bfcde4e432fae3bf57ec951258bc5a02bebb6..1d63e9898783e3d26dc8b4a23fc785810142d9a1 100644 (file)
--- a/dir.c
+++ b/dir.c
@@ -472,15 +472,14 @@ static void *read_skip_worktree_file_from_index(const char *path, size_t *size)
        unsigned long sz;
        enum object_type type;
        void *data;
-       struct index_state *istate = &the_index;
 
        len = strlen(path);
-       pos = index_name_pos(istate, path, len);
+       pos = cache_name_pos(path, len);
        if (pos < 0)
                return NULL;
-       if (!ce_skip_worktree(istate->cache[pos]))
+       if (!ce_skip_worktree(active_cache[pos]))
                return NULL;
-       data = read_sha1_file(istate->cache[pos]->sha1, &type, &sz);
+       data = read_sha1_file(active_cache[pos]->sha1, &type, &sz);
        if (!data || type != OBJ_BLOB) {
                free(data);
                return NULL;
@@ -927,13 +926,13 @@ enum exist_status {
 };
 
 /*
- * Do not use the alphabetically stored index to look up
+ * Do not use the alphabetically sorted index to look up
  * the directory name; instead, use the case insensitive
  * name hash.
  */
 static enum exist_status directory_exists_in_index_icase(const char *dirname, int len)
 {
-       const struct cache_entry *ce = index_name_exists(&the_index, dirname, len + 1, ignore_case);
+       const struct cache_entry *ce = cache_name_exists(dirname, len + 1, ignore_case);
        unsigned char endchar;
 
        if (!ce)
@@ -1175,14 +1174,51 @@ static enum path_treatment treat_one_path(struct dir_struct *dir,
                                          int dtype, struct dirent *de)
 {
        int exclude;
+       int has_path_in_index = !!cache_name_exists(path->buf, path->len, ignore_case);
+
        if (dtype == DT_UNKNOWN)
                dtype = get_dtype(de, path->buf, path->len);
 
        /* Always exclude indexed files */
-       if (dtype != DT_DIR &&
-           cache_name_exists(path->buf, path->len, ignore_case))
+       if (dtype != DT_DIR && has_path_in_index)
                return path_none;
 
+       /*
+        * When we are looking at a directory P in the working tree,
+        * there are three cases:
+        *
+        * (1) P exists in the index.  Everything inside the directory P in
+        * the working tree needs to go when P is checked out from the
+        * index.
+        *
+        * (2) P does not exist in the index, but there is P/Q in the index.
+        * We know P will stay a directory when we check out the contents
+        * of the index, but we do not know yet if there is a directory
+        * P/Q in the working tree to be killed, so we need to recurse.
+        *
+        * (3) P does not exist in the index, and there is no P/Q in the index
+        * to require P to be a directory, either.  Only in this case, we
+        * know that everything inside P will not be killed without
+        * recursing.
+        */
+       if ((dir->flags & DIR_COLLECT_KILLED_ONLY) &&
+           (dtype == DT_DIR) &&
+           !has_path_in_index) {
+               /*
+                * NEEDSWORK: directory_exists_in_index_icase()
+                * assumes that one byte past the given path is
+                * readable and has '/', which needs to be fixed, but
+                * until then, work it around in the caller.
+                */
+               strbuf_addch(path, '/');
+               if (directory_exists_in_index(path->buf, path->len - 1) ==
+                   index_nonexistent) {
+                       strbuf_setlen(path, path->len - 1);
+                       return path_none;
+               }
+               strbuf_setlen(path, path->len - 1);
+       }
+
        exclude = is_excluded(dir, path->buf, &dtype);
 
        /*
diff --git a/dir.h b/dir.h
index 3d6b80c933b395644c005f6d8df4697215ff000a..4677b861f0046492b2b2026789eaa69850afcc77 100644 (file)
--- a/dir.h
+++ b/dir.h
@@ -80,7 +80,8 @@ struct dir_struct {
                DIR_HIDE_EMPTY_DIRECTORIES = 1<<2,
                DIR_NO_GITLINKS = 1<<3,
                DIR_COLLECT_IGNORED = 1<<4,
-               DIR_SHOW_IGNORED_TOO = 1<<5
+               DIR_SHOW_IGNORED_TOO = 1<<5,
+               DIR_COLLECT_KILLED_ONLY = 1<<6
        } flags;
        struct dir_entry **entries;
        struct dir_entry **ignored;
index f5d99c11813b1ae2eee0bb7dfd94eab60c721b64..aff4f5abab667d054c9ab1b925e4877a4fd8d243 100644 (file)
@@ -184,36 +184,6 @@ static void consume_shallow_list(struct fetch_pack_args *args, int fd)
        }
 }
 
-struct write_shallow_data {
-       struct strbuf *out;
-       int use_pack_protocol;
-       int count;
-};
-
-static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
-{
-       struct write_shallow_data *data = cb_data;
-       const char *hex = sha1_to_hex(graft->sha1);
-       data->count++;
-       if (data->use_pack_protocol)
-               packet_buf_write(data->out, "shallow %s", hex);
-       else {
-               strbuf_addstr(data->out, hex);
-               strbuf_addch(data->out, '\n');
-       }
-       return 0;
-}
-
-static int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
-{
-       struct write_shallow_data data;
-       data.out = out;
-       data.use_pack_protocol = use_pack_protocol;
-       data.count = 0;
-       for_each_commit_graft(write_one_shallow, &data);
-       return data.count;
-}
-
 static enum ack_type get_ack(int fd, unsigned char *result_sha1)
 {
        int len;
@@ -795,27 +765,6 @@ static int cmp_ref_by_name(const void *a_, const void *b_)
        return strcmp(a->name, b->name);
 }
 
-static void setup_alternate_shallow(void)
-{
-       struct strbuf sb = STRBUF_INIT;
-       int fd;
-
-       check_shallow_file_for_update();
-       fd = hold_lock_file_for_update(&shallow_lock, git_path("shallow"),
-                                      LOCK_DIE_ON_ERROR);
-       if (write_shallow_commits(&sb, 0)) {
-               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
-                       die_errno("failed to write to %s", shallow_lock.filename);
-               alternate_shallow_file = shallow_lock.filename;
-       } else
-               /*
-                * is_repository_shallow() sees empty string as "no
-                * shallow file".
-                */
-               alternate_shallow_file = "";
-       strbuf_release(&sb);
-}
-
 static struct ref *do_fetch_pack(struct fetch_pack_args *args,
                                 int fd[2],
                                 const struct ref *orig_ref,
@@ -896,7 +845,7 @@ static struct ref *do_fetch_pack(struct fetch_pack_args *args,
        if (args->stateless_rpc)
                packet_flush(fd[1]);
        if (args->depth > 0)
-               setup_alternate_shallow();
+               setup_alternate_shallow(&shallow_lock, &alternate_shallow_file);
        else
                alternate_shallow_file = NULL;
        if (get_pack(args, fd, pack_lockfile))
diff --git a/graph.c b/graph.c
index b24d04c4066dd4d09200bb27d09c91d93c1bb5b0..640433166b39c515746d698566839326d9ce7bbe 100644 (file)
--- a/graph.c
+++ b/graph.c
@@ -801,10 +801,10 @@ static int graph_draw_octopus_merge(struct git_graph *graph,
        int num_dashes =
                ((graph->num_parents - dashless_commits) * 2) - 1;
        for (i = 0; i < num_dashes; i++) {
-               col_num = (i / 2) + dashless_commits;
+               col_num = (i / 2) + dashless_commits + graph->commit_index;
                strbuf_write_column(sb, &graph->new_columns[col_num], '-');
        }
-       col_num = (i / 2) + dashless_commits;
+       col_num = (i / 2) + dashless_commits + graph->commit_index;
        strbuf_write_column(sb, &graph->new_columns[col_num], '.');
        return num_dashes + 1;
 }
index 6dad188b5f31d47fdce86e5104aa3c6b899ebed7..b1e74d2086b8e0dee261358622016f6613249fcf 100644 (file)
@@ -1543,7 +1543,7 @@ static int remote_exists(const char *path)
 
        sprintf(url, "%s%s", repo->url, path);
 
-       switch (http_get_strbuf(url, NULL, NULL, 0)) {
+       switch (http_get_strbuf(url, NULL, NULL)) {
        case HTTP_OK:
                ret = 1;
                break;
@@ -1567,7 +1567,7 @@ static void fetch_symref(const char *path, char **symref, unsigned char *sha1)
        url = xmalloc(strlen(repo->url) + strlen(path) + 1);
        sprintf(url, "%s%s", repo->url, path);
 
-       if (http_get_strbuf(url, NULL, &buffer, 0) != HTTP_OK)
+       if (http_get_strbuf(url, &buffer, NULL) != HTTP_OK)
                die("Couldn't get %s for remote symref\n%s", url,
                    curl_errorstr);
        free(url);
@@ -1976,7 +1976,7 @@ int main(int argc, char **argv)
                pushing = 0;
                if (prepare_revision_walk(&revs))
                        die("revision walk setup failed");
-               mark_edges_uninteresting(revs.commits, &revs, NULL);
+               mark_edges_uninteresting(&revs, NULL);
                objects_to_send = get_delta(&revs, ref_lock);
                finish_all_active_slots();
 
diff --git a/http.c b/http.c
index 2d086aedfac9a8ea384c53f3b1963149b880ed31..3447945ee4f4e0e4b273d979ea1805b4c0f63b4c 100644 (file)
--- a/http.c
+++ b/http.c
@@ -45,7 +45,7 @@ static long curl_low_speed_time = -1;
 static int curl_ftp_no_epsv;
 static const char *curl_http_proxy;
 static const char *curl_cookie_file;
-static struct credential http_auth = CREDENTIAL_INIT;
+struct credential http_auth = CREDENTIAL_INIT;
 static int http_proactive_auth;
 static const char *user_agent;
 
@@ -806,7 +806,6 @@ int handle_curl_result(struct slot_results *results)
                        credential_reject(&http_auth);
                        return HTTP_NOAUTH;
                } else {
-                       credential_fill(&http_auth);
                        return HTTP_REAUTH;
                }
        } else {
@@ -820,12 +819,25 @@ int handle_curl_result(struct slot_results *results)
        }
 }
 
+static CURLcode curlinfo_strbuf(CURL *curl, CURLINFO info, struct strbuf *buf)
+{
+       char *ptr;
+       CURLcode ret;
+
+       strbuf_reset(buf);
+       ret = curl_easy_getinfo(curl, info, &ptr);
+       if (!ret && ptr)
+               strbuf_addstr(buf, ptr);
+       return ret;
+}
+
 /* http_request() targets */
 #define HTTP_REQUEST_STRBUF    0
 #define HTTP_REQUEST_FILE      1
 
-static int http_request(const char *url, struct strbuf *type,
-                       void *result, int target, int options)
+static int http_request(const char *url,
+                       void *result, int target,
+                       const struct http_get_options *options)
 {
        struct active_request_slot *slot;
        struct slot_results results;
@@ -858,9 +870,9 @@ static int http_request(const char *url, struct strbuf *type,
        }
 
        strbuf_addstr(&buf, "Pragma:");
-       if (options & HTTP_NO_CACHE)
+       if (options && options->no_cache)
                strbuf_addstr(&buf, " no-cache");
-       if (options & HTTP_KEEP_ERROR)
+       if (options && options->keep_error)
                curl_easy_setopt(slot->curl, CURLOPT_FAILONERROR, 0);
 
        headers = curl_slist_append(headers, buf.buf);
@@ -878,13 +890,13 @@ static int http_request(const char *url, struct strbuf *type,
                ret = HTTP_START_FAILED;
        }
 
-       if (type) {
-               char *t;
-               strbuf_reset(type);
-               curl_easy_getinfo(slot->curl, CURLINFO_CONTENT_TYPE, &t);
-               if (t)
-                       strbuf_addstr(type, t);
-       }
+       if (options && options->content_type)
+               curlinfo_strbuf(slot->curl, CURLINFO_CONTENT_TYPE,
+                               options->content_type);
+
+       if (options && options->effective_url)
+               curlinfo_strbuf(slot->curl, CURLINFO_EFFECTIVE_URL,
+                               options->effective_url);
 
        curl_slist_free_all(headers);
        strbuf_release(&buf);
@@ -892,12 +904,71 @@ static int http_request(const char *url, struct strbuf *type,
        return ret;
 }
 
+/*
+ * Update the "base" url to a more appropriate value, as deduced by
+ * redirects seen when requesting a URL starting with "url".
+ *
+ * The "asked" parameter is a URL that we asked curl to access, and must begin
+ * with "base".
+ *
+ * The "got" parameter is the URL that curl reported to us as where we ended
+ * up.
+ *
+ * Returns 1 if we updated the base url, 0 otherwise.
+ *
+ * Our basic strategy is to compare "base" and "asked" to find the bits
+ * specific to our request. We then strip those bits off of "got" to yield the
+ * new base. So for example, if our base is "http://example.com/foo.git",
+ * and we ask for "http://example.com/foo.git/info/refs", we might end up
+ * with "https://other.example.com/foo.git/info/refs". We would want the
+ * new URL to become "https://other.example.com/foo.git".
+ *
+ * Note that this assumes a sane redirect scheme. It's entirely possible
+ * in the example above to end up at a URL that does not even end in
+ * "info/refs".  In such a case we simply punt, as there is not much we can
+ * do (and such a scheme is unlikely to represent a real git repository,
+ * which means we are likely about to abort anyway).
+ */
+static int update_url_from_redirect(struct strbuf *base,
+                                   const char *asked,
+                                   const struct strbuf *got)
+{
+       const char *tail;
+       size_t tail_len;
+
+       if (!strcmp(asked, got->buf))
+               return 0;
+
+       if (prefixcmp(asked, base->buf))
+               die("BUG: update_url_from_redirect: %s is not a superset of %s",
+                   asked, base->buf);
+
+       tail = asked + base->len;
+       tail_len = strlen(tail);
+
+       if (got->len < tail_len ||
+           strcmp(tail, got->buf + got->len - tail_len))
+               return 0; /* insane redirect scheme */
+
+       strbuf_reset(base);
+       strbuf_add(base, got->buf, got->len - tail_len);
+       return 1;
+}
+
 static int http_request_reauth(const char *url,
-                              struct strbuf *type,
                               void *result, int target,
-                              int options)
+                              struct http_get_options *options)
 {
-       int ret = http_request(url, type, result, target, options);
+       int ret = http_request(url, result, target, options);
+
+       if (options && options->effective_url && options->base_url) {
+               if (update_url_from_redirect(options->base_url,
+                                            url, options->effective_url)) {
+                       credential_from_url(&http_auth, options->base_url->buf);
+                       url = options->effective_url->buf;
+               }
+       }
+
        if (ret != HTTP_REAUTH)
                return ret;
 
@@ -907,7 +978,7 @@ static int http_request_reauth(const char *url,
         * making our next request. We only know how to do this for
         * the strbuf case, but that is enough to satisfy current callers.
         */
-       if (options & HTTP_KEEP_ERROR) {
+       if (options && options->keep_error) {
                switch (target) {
                case HTTP_REQUEST_STRBUF:
                        strbuf_reset(result);
@@ -916,15 +987,17 @@ static int http_request_reauth(const char *url,
                        die("BUG: HTTP_KEEP_ERROR is only supported with strbufs");
                }
        }
-       return http_request(url, type, result, target, options);
+
+       credential_fill(&http_auth);
+
+       return http_request(url, result, target, options);
 }
 
 int http_get_strbuf(const char *url,
-                   struct strbuf *type,
-                   struct strbuf *result, int options)
+                   struct strbuf *result,
+                   struct http_get_options *options)
 {
-       return http_request_reauth(url, type, result,
-                                  HTTP_REQUEST_STRBUF, options);
+       return http_request_reauth(url, result, HTTP_REQUEST_STRBUF, options);
 }
 
 /*
@@ -933,7 +1006,8 @@ int http_get_strbuf(const char *url,
  * If a previous interrupted download is detected (i.e. a previous temporary
  * file is still around) the download is resumed.
  */
-static int http_get_file(const char *url, const char *filename, int options)
+static int http_get_file(const char *url, const char *filename,
+                        struct http_get_options *options)
 {
        int ret;
        struct strbuf tmpfile = STRBUF_INIT;
@@ -941,16 +1015,16 @@ static int http_get_file(const char *url, const char *filename, int options)
 
        strbuf_addf(&tmpfile, "%s.temp", filename);
        result = fopen(tmpfile.buf, "a");
-       if (! result) {
+       if (!result) {
                error("Unable to open local file %s", tmpfile.buf);
                ret = HTTP_ERROR;
                goto cleanup;
        }
 
-       ret = http_request_reauth(url, NULL, result, HTTP_REQUEST_FILE, options);
+       ret = http_request_reauth(url, result, HTTP_REQUEST_FILE, options);
        fclose(result);
 
-       if ((ret == HTTP_OK) && move_temp_to_file(tmpfile.buf, filename))
+       if (ret == HTTP_OK && move_temp_to_file(tmpfile.buf, filename))
                ret = HTTP_ERROR;
 cleanup:
        strbuf_release(&tmpfile);
@@ -959,12 +1033,15 @@ static int http_get_file(const char *url, const char *filename, int options)
 
 int http_fetch_ref(const char *base, struct ref *ref)
 {
+       struct http_get_options options = {0};
        char *url;
        struct strbuf buffer = STRBUF_INIT;
        int ret = -1;
 
+       options.no_cache = 1;
+
        url = quote_ref_url(base, ref->name);
-       if (http_get_strbuf(url, NULL, &buffer, HTTP_NO_CACHE) == HTTP_OK) {
+       if (http_get_strbuf(url, &buffer, &options) == HTTP_OK) {
                strbuf_rtrim(&buffer);
                if (buffer.len == 40)
                        ret = get_sha1_hex(buffer.buf, ref->old_sha1);
@@ -995,7 +1072,7 @@ static char *fetch_pack_index(unsigned char *sha1, const char *base_url)
        strbuf_addf(&buf, "%s.temp", sha1_pack_index_name(sha1));
        tmp = strbuf_detach(&buf, NULL);
 
-       if (http_get_file(url, tmp, 0) != HTTP_OK) {
+       if (http_get_file(url, tmp, NULL) != HTTP_OK) {
                error("Unable to get pack index %s", url);
                free(tmp);
                tmp = NULL;
@@ -1048,6 +1125,7 @@ static int fetch_and_setup_pack_index(struct packed_git **packs_head,
 
 int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
 {
+       struct http_get_options options = {0};
        int ret = 0, i = 0;
        char *url, *data;
        struct strbuf buf = STRBUF_INIT;
@@ -1057,7 +1135,8 @@ int http_get_info_packs(const char *base_url, struct packed_git **packs_head)
        strbuf_addstr(&buf, "objects/info/packs");
        url = strbuf_detach(&buf, NULL);
 
-       ret = http_get_strbuf(url, NULL, &buf, HTTP_NO_CACHE);
+       options.no_cache = 1;
+       ret = http_get_strbuf(url, &buf, &options);
        if (ret != HTTP_OK)
                goto cleanup;
 
diff --git a/http.h b/http.h
index d77c1b54f24d142461b9e1a0d11cf6de041bc59f..12ca5c892d61a063c856bcdef9154a103c0aa4bc 100644 (file)
--- a/http.h
+++ b/http.h
@@ -102,6 +102,7 @@ extern void http_cleanup(void);
 extern int active_requests;
 extern int http_is_verbose;
 extern size_t http_post_buffer;
+extern struct credential http_auth;
 
 extern char curl_errorstr[CURL_ERROR_SIZE];
 
@@ -125,11 +126,30 @@ extern void append_remote_object_url(struct strbuf *buf, const char *url,
 extern char *get_remote_object_url(const char *url, const char *hex,
                                   int only_two_digit_prefix);
 
-/* Options for http_request_*() */
-#define HTTP_NO_CACHE          1
-#define HTTP_KEEP_ERROR                2
+/* Options for http_get_*() */
+struct http_get_options {
+       unsigned no_cache:1,
+                keep_error:1;
+
+       /* If non-NULL, returns the content-type of the response. */
+       struct strbuf *content_type;
+
+       /*
+        * If non-NULL, returns the URL we ended up at, including any
+        * redirects we followed.
+        */
+       struct strbuf *effective_url;
+
+       /*
+        * If both base_url and effective_url are non-NULL, the base URL will
+        * be munged to reflect any redirections going from the requested url
+        * to effective_url. See the definition of update_url_from_redirect
+        * for details.
+        */
+       struct strbuf *base_url;
+};
 
-/* Return values for http_request_*() */
+/* Return values for http_get_*() */
 #define HTTP_OK                        0
 #define HTTP_MISSING_TARGET    1
 #define HTTP_ERROR             2
@@ -142,7 +162,7 @@ extern char *get_remote_object_url(const char *url, const char *hex,
  *
  * If the result pointer is NULL, a HTTP HEAD request is made instead of GET.
  */
-int http_get_strbuf(const char *url, struct strbuf *content_type, struct strbuf *result, int options);
+int http_get_strbuf(const char *url, struct strbuf *result, struct http_get_options *options);
 
 extern int http_fetch_ref(const char *base, struct ref *ref);
 
diff --git a/ident.c b/ident.c
index 1c123e685fbfbfcd5289958adc54a6cd48351628..1d9b6e770d02d12f77b417e05aca5134eccf1c40 100644 (file)
--- a/ident.c
+++ b/ident.c
@@ -233,7 +233,21 @@ int split_ident_line(struct ident_split *split, const char *line, int len)
        if (!split->mail_end)
                return status;
 
-       for (cp = split->mail_end + 1; cp < line + len && isspace(*cp); cp++)
+       /*
+        * Look from the end-of-line to find the trailing ">" of the mail
+        * address, even though we should already know it as split->mail_end.
+        * This can help in cases of broken idents with an extra ">" somewhere
+        * in the email address.  Note that we are assuming the timestamp will
+        * never have a ">" in it.
+        *
+        * Note that we will always find some ">" before going off the front of
+        * the string, because will always hit the split->mail_end closing
+        * bracket.
+        */
+       for (cp = line + len - 1; *cp != '>'; cp--)
+               ;
+
+       for (cp = cp + 1; cp < line + len && isspace(*cp); cp++)
                ;
        if (line + len <= cp)
                goto person_only;
@@ -402,3 +416,32 @@ int git_ident_config(const char *var, const char *value, void *data)
 
        return 0;
 }
+
+static int buf_cmp(const char *a_begin, const char *a_end,
+                  const char *b_begin, const char *b_end)
+{
+       int a_len = a_end - a_begin;
+       int b_len = b_end - b_begin;
+       int min = a_len < b_len ? a_len : b_len;
+       int cmp;
+
+       cmp = memcmp(a_begin, b_begin, min);
+       if (cmp)
+               return cmp;
+
+       return a_len - b_len;
+}
+
+int ident_cmp(const struct ident_split *a,
+             const struct ident_split *b)
+{
+       int cmp;
+
+       cmp = buf_cmp(a->mail_begin, a->mail_end,
+                     b->mail_begin, b->mail_end);
+       if (cmp)
+               return cmp;
+
+       return buf_cmp(a->name_begin, a->name_end,
+                      b->name_begin, b->name_end);
+}
index 3dd4a960190a1b0016b26dec9187692111b73e3f..05c8c5c61660fdf351246df4279b7642695f8c7e 100644 (file)
@@ -145,19 +145,35 @@ static void mark_edge_parents_uninteresting(struct commit *commit,
        }
 }
 
-void mark_edges_uninteresting(struct commit_list *list,
-                             struct rev_info *revs,
-                             show_edge_fn show_edge)
+void mark_edges_uninteresting(struct rev_info *revs, show_edge_fn show_edge)
 {
-       for ( ; list; list = list->next) {
+       struct commit_list *list;
+       int i;
+
+       for (list = revs->commits; list; list = list->next) {
                struct commit *commit = list->item;
 
                if (commit->object.flags & UNINTERESTING) {
                        mark_tree_uninteresting(commit->tree);
+                       if (revs->edge_hint && !(commit->object.flags & SHOWN)) {
+                               commit->object.flags |= SHOWN;
+                               show_edge(commit);
+                       }
                        continue;
                }
                mark_edge_parents_uninteresting(commit, revs, show_edge);
        }
+       for (i = 0; i < revs->cmdline.nr; i++) {
+               struct object *obj = revs->cmdline.rev[i].item;
+               struct commit *commit = (struct commit *)obj;
+               if (obj->type != OBJ_COMMIT || !(obj->flags & UNINTERESTING))
+                       continue;
+               mark_tree_uninteresting(commit->tree);
+               if (revs->edge_hint && !(obj->flags & SHOWN)) {
+                       obj->flags |= SHOWN;
+                       show_edge(commit);
+               }
+       }
 }
 
 static void add_pending_tree(struct rev_info *revs, struct tree *tree)
index 3db7bb6fa386df2ccb73b78ee72881f32074a1b8..136a1da5a6f048c0f4645112cdbf29c7b0982526 100644 (file)
@@ -6,6 +6,6 @@ typedef void (*show_object_fn)(struct object *, const struct name_path *, const
 void traverse_commit_list(struct rev_info *, show_commit_fn, show_object_fn, void *);
 
 typedef void (*show_edge_fn)(struct commit *);
-void mark_edges_uninteresting(struct commit_list *, struct rev_info *, show_edge_fn);
+void mark_edges_uninteresting(struct rev_info *, show_edge_fn);
 
 #endif
index f95933b0aacce84a8b911634e5c40d5e937a322b..fc2f00176c3082f2e1bb801e361b42c02fa3d5b8 100644 (file)
@@ -2069,8 +2069,8 @@ int parse_merge_opt(struct merge_options *o, const char *s)
                o->xdl_opts = DIFF_WITH_ALG(o, PATIENCE_DIFF);
        else if (!strcmp(s, "histogram"))
                o->xdl_opts = DIFF_WITH_ALG(o, HISTOGRAM_DIFF);
-       else if (!strcmp(s, "diff-algorithm=")) {
-               long value = parse_algorithm_value(s+15);
+       else if (!prefixcmp(s, "diff-algorithm=")) {
+               long value = parse_algorithm_value(s + strlen("diff-algorithm="));
                if (value < 0)
                        return -1;
                /* clear out previous settings */
index 74563c92b4cdce8e947a2816a8b56355b11439e6..b4e32b74d3622f92b2c5d3b9881e5cdf49d7f375 100644 (file)
--- a/pretty.c
+++ b/pretty.c
@@ -432,7 +432,7 @@ void pp_user_info(struct pretty_print_context *pp,
                map_user(pp->mailmap, &mailbuf, &maillen, &namebuf, &namelen);
 
        if (pp->fmt == CMIT_FMT_EMAIL) {
-               if (pp->from_ident) {
+               if (pp->from_ident && ident_cmp(pp->from_ident, &ident)) {
                        struct strbuf buf = STRBUF_INIT;
 
                        strbuf_addstr(&buf, "From: ");
index 5b3ce9eed299e312c132b590ea4926cfb019a271..ef1684b9df3ee5cff0d63ab5f7ed51e7f351a686 100644 (file)
@@ -8,9 +8,11 @@
 #include "pkt-line.h"
 #include "sideband.h"
 #include "argv-array.h"
+#include "credential.h"
 
 static struct remote *remote;
-static const char *url; /* always ends with a trailing slash */
+/* always ends with a trailing slash */
+static struct strbuf url = STRBUF_INIT;
 
 struct options {
        int verbosity;
@@ -111,7 +113,8 @@ static struct ref *parse_info_refs(struct discovery *heads)
                        mid = &data[i];
                if (data[i] == '\n') {
                        if (mid - start != 40)
-                               die("%sinfo/refs not valid: is this a git repository?", url);
+                               die("%sinfo/refs not valid: is this a git repository?",
+                                   url.buf);
                        data[i] = 0;
                        ref_name = mid + 1;
                        ref = xmalloc(sizeof(struct ref) +
@@ -130,7 +133,7 @@ static struct ref *parse_info_refs(struct discovery *heads)
        }
 
        ref = alloc_ref("HEAD");
-       if (!http_fetch_ref(url, ref) &&
+       if (!http_fetch_ref(url.buf, ref) &&
            !resolve_remote_symref(ref, refs)) {
                ref->next = refs;
                refs = ref;
@@ -184,40 +187,47 @@ static struct discovery* discover_refs(const char *service, int for_push)
        struct strbuf exp = STRBUF_INIT;
        struct strbuf type = STRBUF_INIT;
        struct strbuf buffer = STRBUF_INIT;
+       struct strbuf refs_url = STRBUF_INIT;
+       struct strbuf effective_url = STRBUF_INIT;
        struct discovery *last = last_discovery;
-       char *refs_url;
        int http_ret, maybe_smart = 0;
+       struct http_get_options options;
 
        if (last && !strcmp(service, last->service))
                return last;
        free_discovery(last);
 
-       strbuf_addf(&buffer, "%sinfo/refs", url);
-       if ((!prefixcmp(url, "http://") || !prefixcmp(url, "https://")) &&
+       strbuf_addf(&refs_url, "%sinfo/refs", url.buf);
+       if ((!prefixcmp(url.buf, "http://") || !prefixcmp(url.buf, "https://")) &&
             git_env_bool("GIT_SMART_HTTP", 1)) {
                maybe_smart = 1;
-               if (!strchr(url, '?'))
-                       strbuf_addch(&buffer, '?');
+               if (!strchr(url.buf, '?'))
+                       strbuf_addch(&refs_url, '?');
                else
-                       strbuf_addch(&buffer, '&');
-               strbuf_addf(&buffer, "service=%s", service);
+                       strbuf_addch(&refs_url, '&');
+               strbuf_addf(&refs_url, "service=%s", service);
        }
-       refs_url = strbuf_detach(&buffer, NULL);
 
-       http_ret = http_get_strbuf(refs_url, &type, &buffer,
-                                  HTTP_NO_CACHE | HTTP_KEEP_ERROR);
+       memset(&options, 0, sizeof(options));
+       options.content_type = &type;
+       options.effective_url = &effective_url;
+       options.base_url = &url;
+       options.no_cache = 1;
+       options.keep_error = 1;
+
+       http_ret = http_get_strbuf(refs_url.buf, &buffer, &options);
        switch (http_ret) {
        case HTTP_OK:
                break;
        case HTTP_MISSING_TARGET:
                show_http_message(&type, &buffer);
-               die("repository '%s' not found", url);
+               die("repository '%s' not found", url.buf);
        case HTTP_NOAUTH:
                show_http_message(&type, &buffer);
-               die("Authentication failed for '%s'", url);
+               die("Authentication failed for '%s'", url.buf);
        default:
                show_http_message(&type, &buffer);
-               die("unable to access '%s': %s", url, curl_errorstr);
+               die("unable to access '%s': %s", url.buf, curl_errorstr);
        }
 
        last= xcalloc(1, sizeof(*last_discovery));
@@ -258,9 +268,10 @@ static struct discovery* discover_refs(const char *service, int for_push)
        else
                last->refs = parse_info_refs(last);
 
-       free(refs_url);
+       strbuf_release(&refs_url);
        strbuf_release(&exp);
        strbuf_release(&type);
+       strbuf_release(&effective_url);
        strbuf_release(&buffer);
        last_discovery = last;
        return last;
@@ -444,6 +455,8 @@ static int post_rpc(struct rpc_state *rpc)
        if (large_request) {
                do {
                        err = probe_rpc(rpc);
+                       if (err == HTTP_REAUTH)
+                               credential_fill(&http_auth);
                } while (err == HTTP_REAUTH);
                if (err != HTTP_OK)
                        return -1;
@@ -543,8 +556,10 @@ static int post_rpc(struct rpc_state *rpc)
        curl_easy_setopt(slot->curl, CURLOPT_FILE, rpc);
 
        err = run_slot(slot);
-       if (err == HTTP_REAUTH && !large_request)
+       if (err == HTTP_REAUTH && !large_request) {
+               credential_fill(&http_auth);
                goto retry;
+       }
        if (err != HTTP_OK)
                err = -1;
 
@@ -579,7 +594,7 @@ static int rpc_service(struct rpc_state *rpc, struct discovery *heads)
        rpc->out = client.out;
        strbuf_init(&rpc->result, 0);
 
-       strbuf_addf(&buf, "%s%s", url, svc);
+       strbuf_addf(&buf, "%s%s", url.buf, svc);
        rpc->service_url = strbuf_detach(&buf, NULL);
 
        strbuf_addf(&buf, "Content-Type: application/x-%s-request", svc);
@@ -631,7 +646,7 @@ static int fetch_dumb(int nr_heads, struct ref **to_fetch)
        for (i = 0; i < nr_heads; i++)
                targets[i] = xstrdup(sha1_to_hex(to_fetch[i]->old_sha1));
 
-       walker = get_http_walker(url);
+       walker = get_http_walker(url.buf);
        walker->get_all = 1;
        walker->get_tree = 1;
        walker->get_history = 1;
@@ -676,7 +691,7 @@ static int fetch_git(struct discovery *heads,
                depth_arg = strbuf_detach(&buf, NULL);
                argv[argc++] = depth_arg;
        }
-       argv[argc++] = url;
+       argv[argc++] = url.buf;
        argv[argc++] = NULL;
 
        for (i = 0; i < nr_heads; i++) {
@@ -774,7 +789,7 @@ static int push_dav(int nr_spec, char **specs)
                argv[argc++] = "--dry-run";
        if (options.verbosity > 1)
                argv[argc++] = "--verbose";
-       argv[argc++] = url;
+       argv[argc++] = url.buf;
        for (i = 0; i < nr_spec; i++)
                argv[argc++] = specs[i];
        argv[argc++] = NULL;
@@ -804,7 +819,7 @@ static int push_git(struct discovery *heads, int nr_spec, char **specs)
        else if (options.verbosity > 1)
                argv_array_push(&args, "--verbose");
        argv_array_push(&args, options.progress ? "--progress" : "--no-progress");
-       argv_array_push(&args, url);
+       argv_array_push(&args, url.buf);
        for (i = 0; i < nr_spec; i++)
                argv_array_push(&args, specs[i]);
 
@@ -885,14 +900,12 @@ int main(int argc, const char **argv)
        remote = remote_get(argv[1]);
 
        if (argc > 2) {
-               end_url_with_slash(&buf, argv[2]);
+               end_url_with_slash(&url, argv[2]);
        } else {
-               end_url_with_slash(&buf, remote->url[0]);
+               end_url_with_slash(&url, remote->url[0]);
        }
 
-       url = strbuf_detach(&buf, NULL);
-
-       http_init(remote, url, 0);
+       http_init(remote, url.buf, 0);
 
        do {
                if (strbuf_getline(&buf, stdin, '\n') == EOF) {
index ac20d1aaed7b5e1a52682dfd196f87e2f94e87c7..f82b833fbceada4c4b32e669fd33966c751249e8 100644 (file)
@@ -1420,26 +1420,40 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                }
                if (!get_sha1_committish(this, from_sha1) &&
                    !get_sha1_committish(next, sha1)) {
-                       struct commit *a, *b;
-                       struct commit_list *exclude;
-
-                       a = lookup_commit_reference(from_sha1);
-                       b = lookup_commit_reference(sha1);
-                       if (!a || !b) {
-                               if (revs->ignore_missing)
-                                       return 0;
-                               die(symmetric ?
-                                   "Invalid symmetric difference expression %s...%s" :
-                                   "Invalid revision range %s..%s",
-                                   arg, next);
-                       }
+                       struct object *a_obj, *b_obj;
 
                        if (!cant_be_filename) {
                                *dotdot = '.';
                                verify_non_filename(revs->prefix, arg);
                        }
 
-                       if (symmetric) {
+                       a_obj = parse_object(from_sha1);
+                       b_obj = parse_object(sha1);
+                       if (!a_obj || !b_obj) {
+                       missing:
+                               if (revs->ignore_missing)
+                                       return 0;
+                               die(symmetric
+                                   ? "Invalid symmetric difference expression %s"
+                                   : "Invalid revision range %s", arg);
+                       }
+
+                       if (!symmetric) {
+                               /* just A..B */
+                               a_flags = flags_exclude;
+                       } else {
+                               /* A...B -- find merge bases between the two */
+                               struct commit *a, *b;
+                               struct commit_list *exclude;
+
+                               a = (a_obj->type == OBJ_COMMIT
+                                    ? (struct commit *)a_obj
+                                    : lookup_commit_reference(a_obj->sha1));
+                               b = (b_obj->type == OBJ_COMMIT
+                                    ? (struct commit *)b_obj
+                                    : lookup_commit_reference(b_obj->sha1));
+                               if (!a || !b)
+                                       goto missing;
                                exclude = get_merge_bases(a, b, 1);
                                add_rev_cmdline_list(revs, exclude,
                                                     REV_CMD_MERGE_BASE,
@@ -1447,17 +1461,18 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
                                add_pending_commit_list(revs, exclude,
                                                        flags_exclude);
                                free_commit_list(exclude);
+
                                a_flags = flags | SYMMETRIC_LEFT;
-                       } else
-                               a_flags = flags_exclude;
-                       a->object.flags |= a_flags;
-                       b->object.flags |= flags;
-                       add_rev_cmdline(revs, &a->object, this,
+                       }
+
+                       a_obj->flags |= a_flags;
+                       b_obj->flags |= flags;
+                       add_rev_cmdline(revs, a_obj, this,
                                        REV_CMD_LEFT, a_flags);
-                       add_rev_cmdline(revs, &b->object, next,
+                       add_rev_cmdline(revs, b_obj, next,
                                        REV_CMD_RIGHT, flags);
-                       add_pending_object(revs, &a->object, this);
-                       add_pending_object(revs, &b->object, next);
+                       add_pending_object(revs, a_obj, this);
+                       add_pending_object(revs, b_obj, next);
                        return 0;
                }
                *dotdot = '.';
index 06784fb95af2ddbdbfc6ed97e6ccf2ac920393b3..613839db544dc852ee49d026bb1b561e6e1ce4bf 100644 (file)
@@ -2444,7 +2444,6 @@ static int sha1_loose_object_info(const unsigned char *sha1,
        return 0;
 }
 
-/* returns enum object_type or negative */
 int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
 {
        struct cached_object *co;
@@ -2493,6 +2492,7 @@ int sha1_object_info_extended(const unsigned char *sha1, struct object_info *oi)
        return 0;
 }
 
+/* returns enum object_type or negative */
 int sha1_object_info(const unsigned char *sha1, unsigned long *sizep)
 {
        enum object_type type;
index 8a9c96d019a1b3df4942f370fe0d312467a36799..cdf37d694de175adcce10fa2e2a79c6579603b0b 100644 (file)
--- a/shallow.c
+++ b/shallow.c
@@ -1,6 +1,7 @@
 #include "cache.h"
 #include "commit.h"
 #include "tag.h"
+#include "pkt-line.h"
 
 static int is_shallow = -1;
 static struct stat shallow_stat;
@@ -141,3 +142,81 @@ void check_shallow_file_for_update(void)
                   )
                die("shallow file was changed during fetch");
 }
+
+struct write_shallow_data {
+       struct strbuf *out;
+       int use_pack_protocol;
+       int count;
+};
+
+static int write_one_shallow(const struct commit_graft *graft, void *cb_data)
+{
+       struct write_shallow_data *data = cb_data;
+       const char *hex = sha1_to_hex(graft->sha1);
+       if (graft->nr_parent != -1)
+               return 0;
+       data->count++;
+       if (data->use_pack_protocol)
+               packet_buf_write(data->out, "shallow %s", hex);
+       else {
+               strbuf_addstr(data->out, hex);
+               strbuf_addch(data->out, '\n');
+       }
+       return 0;
+}
+
+int write_shallow_commits(struct strbuf *out, int use_pack_protocol)
+{
+       struct write_shallow_data data;
+       data.out = out;
+       data.use_pack_protocol = use_pack_protocol;
+       data.count = 0;
+       for_each_commit_graft(write_one_shallow, &data);
+       return data.count;
+}
+
+char *setup_temporary_shallow(void)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int fd;
+
+       if (write_shallow_commits(&sb, 0)) {
+               struct strbuf path = STRBUF_INIT;
+               strbuf_addstr(&path, git_path("shallow_XXXXXX"));
+               fd = xmkstemp(path.buf);
+               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+                       die_errno("failed to write to %s",
+                                 path.buf);
+               close(fd);
+               strbuf_release(&sb);
+               return strbuf_detach(&path, NULL);
+       }
+       /*
+        * is_repository_shallow() sees empty string as "no shallow
+        * file".
+        */
+       return xstrdup("");
+}
+
+void setup_alternate_shallow(struct lock_file *shallow_lock,
+                            const char **alternate_shallow_file)
+{
+       struct strbuf sb = STRBUF_INIT;
+       int fd;
+
+       check_shallow_file_for_update();
+       fd = hold_lock_file_for_update(shallow_lock, git_path("shallow"),
+                                      LOCK_DIE_ON_ERROR);
+       if (write_shallow_commits(&sb, 0)) {
+               if (write_in_full(fd, sb.buf, sb.len) != sb.len)
+                       die_errno("failed to write to %s",
+                                 shallow_lock->filename);
+               *alternate_shallow_file = shallow_lock->filename;
+       } else
+               /*
+                * is_repository_shallow() sees empty string as "no
+                * shallow file".
+                */
+               *alternate_shallow_file = "";
+       strbuf_release(&sb);
+}
index 895b9258b07ca65b78ac376023e203f482d8ac16..7059cc6c21518ec43df28770ffc18d5e0ac1a660 100644 (file)
@@ -187,7 +187,8 @@ set_askpass() {
 }
 
 expect_askpass() {
-       dest=$HTTPD_DEST
+       dest=$HTTPD_DEST${3+/$3}
+
        {
                case "$1" in
                none)
index dd17e3a09d728a08c8038c5ce8187ea63112cdf3..4a261f13f533daaa3d687d2bb88481b560432378 100644 (file)
@@ -102,6 +102,8 @@ ScriptAlias /broken_smart/ broken-smart-http.sh/
 RewriteEngine on
 RewriteRule ^/smart-redir-perm/(.*)$ /smart/$1 [R=301]
 RewriteRule ^/smart-redir-temp/(.*)$ /smart/$1 [R=302]
+RewriteRule ^/smart-redir-auth/(.*)$ /auth/smart/$1 [R=301]
+RewriteRule ^/smart-redir-limited/(.*)/info/refs$ /smart/$1/info/refs [R=301]
 
 <IfDefine SSL>
 LoadModule ssl_module modules/mod_ssl.so
index 7cc0a3582ef1bb1598bc5dfc5334bfbe006c3f5a..87bdf9c96bfacf4ac921ad9b195db46d9c3b079a 100755 (executable)
@@ -47,4 +47,10 @@ test_expect_success 'disambiguate checking out from a tree-ish' '
        git diff --exit-code --quiet
 '
 
+test_expect_success 'accurate error message with more than one ref' '
+       test_must_fail git checkout HEAD master -- 2>actual &&
+       grep 2 actual &&
+       test_i18ngrep "one reference expected, 2 given" actual
+'
+
 test_done
index dee55e428f07eeac9c2d2c329ff2c1aca3f4ea98..6ecb559465b43e528a5cb1511b758cc48d940e92 100755 (executable)
@@ -104,7 +104,7 @@ test_expect_success 'setup more remotes with unconventional refspecs' '
                cd repo_c &&
                test_commit c_master &&
                git checkout -b bar &&
-               test_commit c_bar
+               test_commit c_bar &&
                git checkout -b spam &&
                test_commit c_spam
        ) &&
@@ -113,9 +113,9 @@ test_expect_success 'setup more remotes with unconventional refspecs' '
                cd repo_d &&
                test_commit d_master &&
                git checkout -b baz &&
-               test_commit f_baz
+               test_commit d_baz &&
                git checkout -b eggs &&
-               test_commit c_eggs
+               test_commit d_eggs
        ) &&
        git remote add repo_c repo_c &&
        git config remote.repo_c.fetch \
@@ -164,4 +164,25 @@ test_expect_success 'checkout of branch from a single remote succeeds #4' '
        test_branch_upstream eggs repo_d eggs
 '
 
+test_expect_success 'checkout of branch with a file having the same name fails' '
+       git checkout -B master &&
+       test_might_fail git branch -D spam &&
+
+       >spam &&
+       test_must_fail git checkout spam &&
+       test_must_fail git rev-parse --verify refs/heads/spam &&
+       test_branch master
+'
+
+test_expect_success 'checkout <branch> -- succeeds, even if a file with the same name exists' '
+       git checkout -B master &&
+       test_might_fail git branch -D spam &&
+
+       >spam &&
+       git checkout spam -- &&
+       test_branch spam &&
+       test_cmp_rev refs/remotes/extra_dir/repo_c/extra_dir/spam HEAD &&
+       test_branch_upstream spam repo_c spam
+'
+
 test_done
index f611d799b690457e306571adf16813b238a27dc9..6d3b828a951e4c03b886087393269dbe4d5c5c8f 100755 (executable)
@@ -11,6 +11,7 @@ This test prepares the following in the cache:
     path1       - a symlink
     path2/file2 - a file in a directory
     path3/file3 - a file in a directory
+    pathx/ju    - a file in a directory
     submod1/   - a submodule
     submod2/   - another submodule
 
@@ -23,6 +24,7 @@ and the following on the filesystem:
     path4      - a file
     path5      - a symlink
     path6/file6 - a file in a directory
+    pathx/ju/nk - a file in a directory to be killed
     submod1/   - a submodule (modified from the cache)
     submod2/   - a submodule (matches the cache)
 
@@ -44,14 +46,15 @@ modified without reporting path9 and path10.  submod1 is also modified.
 test_expect_success 'git update-index --add to add various paths.' '
        date >path0 &&
        test_ln_s_add xyzzy path1 &&
-       mkdir path2 path3 &&
+       mkdir path2 path3 pathx &&
        date >path2/file2 &&
        date >path3/file3 &&
+       >pathx/ju &&
        : >path7 &&
        date >path8 &&
        : >path9 &&
        date >path10 &&
-       git update-index --add -- path0 path?/file? path7 path8 path9 path10 &&
+       git update-index --add -- path0 path?/file? pathx/ju path7 path8 path9 path10 &&
        for i in 1 2
        do
                git init submod$i &&
@@ -77,7 +80,7 @@ test_expect_success 'git ls-files -k to show killed files.' '
                date >path3 &&
                date >path5
        fi &&
-       mkdir path0 path1 path6 &&
+       mkdir -p path0 path1 path6 pathx/ju &&
        date >path0/file0 &&
        date >path1/file1 &&
        date >path6/file6 &&
@@ -85,16 +88,23 @@ test_expect_success 'git ls-files -k to show killed files.' '
        : >path8 &&
        : >path9 &&
        touch path10 &&
-       git ls-files -k >.output
-'
-
-test_expect_success 'validate git ls-files -k output.' '
-       cat >.expected <<-\EOF &&
+       >pathx/ju/nk &&
+       cat >.expected <<-\EOF
        path0/file0
        path1/file1
        path2
        path3
+       pathx/ju/nk
        EOF
+'
+
+test_expect_success 'git ls-files -k output (w/o icase)' '
+       git ls-files -k >.output
+       test_cmp .expected .output
+'
+
+test_expect_success 'git ls-files -k output (w/ icase)' '
+       git -c core.ignorecase=true ls-files -k >.output
        test_cmp .expected .output
 '
 
@@ -110,6 +120,7 @@ test_expect_success 'validate git ls-files -m output.' '
        path3/file3
        path7
        path8
+       pathx/ju
        submod1
        EOF
        test_cmp .expected .output
index 44ec6a45f473ffe47aca6945c0e0aab445728f67..55c9ab0b6a5c65aacdb3ef6107c3417d7bdc4e00 100755 (executable)
@@ -319,8 +319,9 @@ test_expect_success 'test tracking setup (non-wildcard, matching)' '
 
 test_expect_success 'tracking setup fails on non-matching refspec' '
        git config remote.local.url . &&
-       git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
+       git config remote.local.fetch refs/heads/*:refs/remotes/local/* &&
        (git show-ref -q refs/remotes/local/master || git fetch local) &&
+       git config remote.local.fetch refs/heads/s:refs/remotes/local/s &&
        test_must_fail git branch --track my5 local/master &&
        test_must_fail git config branch.my5.remote &&
        test_must_fail git config branch.my5.merge
@@ -870,4 +871,39 @@ test_expect_success '--merged catches invalid object names' '
        test_must_fail git branch --merged 0000000000000000000000000000000000000000
 '
 
+test_expect_success 'tracking with unexpected .fetch refspec' '
+       rm -rf a b c d &&
+       git init a &&
+       (
+               cd a &&
+               test_commit a
+       ) &&
+       git init b &&
+       (
+               cd b &&
+               test_commit b
+       ) &&
+       git init c &&
+       (
+               cd c &&
+               test_commit c &&
+               git remote add a ../a &&
+               git remote add b ../b &&
+               git fetch --all
+       ) &&
+       git init d &&
+       (
+               cd d &&
+               git remote add c ../c &&
+               git config remote.c.fetch "+refs/remotes/*:refs/remotes/*" &&
+               git fetch c &&
+               git branch --track local/a/master remotes/a/master &&
+               test "$(git config branch.local/a/master.remote)" = "c" &&
+               test "$(git config branch.local/a/master.merge)" = "refs/remotes/a/master" &&
+               git rev-parse --verify a >expect &&
+               git rev-parse --verify local/a/master >actual &&
+               test_cmp expect actual
+       )
+'
+
 test_done
index 668933bfb2c906ce9f8c74ab498f1986b56aced0..8f272bce84ba605da82dacb04561649f10878df0 100755 (executable)
@@ -1000,6 +1000,16 @@ test_expect_success '--from uses committer ident' '
        test_cmp expect patch.head
 '
 
+test_expect_success '--from omits redundant in-body header' '
+       git format-patch -1 --stdout --from="A U Thor <author@example.com>" >patch &&
+       cat >expect <<-\EOF &&
+       From: A U Thor <author@example.com>
+
+       EOF
+       sed -ne "/^From:/p; /^$/p; /^---$/q" <patch >patch.head &&
+       test_cmp expect patch.head
+'
+
 test_expect_success 'in-body headers trigger content encoding' '
        GIT_AUTHOR_NAME="éxötìc" test_commit exotic &&
        test_when_finished "git reset --hard HEAD^" &&
index 5493500ef15d2f561bd14ca0bdf5f519ec6f49be..42866992cfe65657019dced508d3490b3851a6c7 100755 (executable)
@@ -172,4 +172,20 @@ test_expect_success 'shortlog encoding' '
        git shortlog HEAD~2.. > out &&
 test_cmp expect out'
 
+test_expect_success 'shortlog ignores commits with missing authors' '
+       git commit --allow-empty -m normal &&
+       git commit --allow-empty -m soon-to-be-broken &&
+       git cat-file commit HEAD >commit.tmp &&
+       sed "/^author/d" commit.tmp >broken.tmp &&
+       commit=$(git hash-object -w -t commit --stdin <broken.tmp) &&
+       git update-ref HEAD $commit &&
+       cat >expect <<-\EOF &&
+       A U Thor (1):
+             normal
+
+       EOF
+       git shortlog HEAD~2.. >actual &&
+       test_cmp expect actual
+'
+
 test_done
index ec5099b83d7490184a68691ff2ca0e875cfab209..93c7c366cfeffa46cc72665f6ce676bd37c3c2fe 100755 (executable)
@@ -13,11 +13,16 @@ test_expect_success 'setup' '
        git update-ref refs/heads/broken_email $(cat broken_email.hash)
 '
 
+test_expect_success 'fsck notices broken commit' '
+       git fsck 2>actual &&
+       test_i18ngrep invalid.author actual
+'
+
 test_expect_success 'git log with broken author email' '
        {
                echo commit $(cat broken_email.hash)
                echo "Author: A U Thor <author@example.com>"
-               echo "Date:   Thu Jan 1 00:00:00 1970 +0000"
+               echo "Date:   Thu Apr 7 15:13:13 2005 -0700"
                echo
                echo "    foo"
        } >expect.out &&
@@ -30,7 +35,7 @@ test_expect_success 'git log with broken author email' '
 '
 
 test_expect_success 'git log --format with broken author email' '
-       echo "A U Thor+author@example.com+" >expect.out &&
+       echo "A U Thor+author@example.com+Thu Apr 7 15:13:13 2005 -0700" >expect.out &&
        : >expect.err &&
 
        git log --format="%an+%ae+%ad" broken_email >actual.out 2>actual.err &&
index a80584ea0eaba854ff21318e3c9646f72325bee7..d87ddf73b7127bc624ca739653db9389831b817e 100755 (executable)
@@ -393,6 +393,17 @@ test_expect_success 'fetch in shallow repo unreachable shallow objects' '
                git fsck --no-dangling
        )
 '
+test_expect_success 'fetch creating new shallow root' '
+       (
+               git clone "file://$(pwd)/." shallow10 &&
+               git commit --allow-empty -m empty &&
+               cd shallow10 &&
+               git fetch --depth=1 --progress 2>actual &&
+               # This should fetch only the empty commit, no tree or
+               # blob objects
+               grep "remote: Total 1" actual
+       )
+'
 
 test_expect_success 'setup tests for the --stdin parameter' '
        for head in C D E F
index 8f6e3922dce2b676221610d83a87c5b36d3bc5b6..ac79dd915da4a520e90fca6a11a239aba6e688d7 100755 (executable)
@@ -160,9 +160,7 @@ cat >test/expect <<EOF
 * remote two
   Fetch URL: ../two
   Push  URL: ../three
-  HEAD branch (remote HEAD is ambiguous, may be one of the following):
-    another
-    master
+  HEAD branch: master
   Local refs configured for 'git push':
     ahead  forces to master  (fast-forwardable)
     master pushes to another (up to date)
@@ -262,16 +260,12 @@ test_expect_success 'set-head --auto' '
        )
 '
 
-cat >test/expect <<\EOF
-error: Multiple remote HEAD branches. Please choose one explicitly with:
-  git remote set-head two another
-  git remote set-head two master
-EOF
-
-test_expect_success 'set-head --auto fails w/multiple HEADs' '
+test_expect_success 'set-head --auto has no problem w/multiple HEADs' '
        (
                cd test &&
-               test_must_fail git remote set-head --auto two >output 2>&1 &&
+               git fetch two "refs/heads/*:refs/remotes/two/*" &&
+               git remote set-head --auto two >output 2>&1 &&
+               echo "two/HEAD set to master" >expect &&
                test_i18ncmp expect output
        )
 '
index c983d3694c74cf6de6b7bd9a74c28d41462171f0..3932e797f7f60545e6e8c140824a5ca5ee16832a 100755 (executable)
@@ -54,9 +54,6 @@ test_expect_success 'upload-pack fails due to error in rev-list' '
        printf "0032want %s\n0034shallow %s00000009done\n0000" \
                $(git rev-parse HEAD) $(git rev-parse HEAD^) >input &&
        test_must_fail git upload-pack . <input >/dev/null 2>output.err &&
-       # pack-objects survived
-       grep "Total.*, reused" output.err &&
-       # but there was an error, which must have been in rev-list
        grep "bad tree object" output.err
 '
 
index 55a866af803452e164e2c11c421b1e63f62d2e1e..1b71bb515646e47181da33f83f97f04b6e0e8617 100755 (executable)
@@ -113,6 +113,10 @@ test_expect_success 'follow redirects (302)' '
        git clone $HTTPD_URL/smart-redir-temp/repo.git --quiet repo-t
 '
 
+test_expect_success 'redirects re-root further requests' '
+       git clone $HTTPD_URL/smart-redir-limited/repo.git repo-redir-limited
+'
+
 test_expect_success 'clone from password-protected repository' '
        echo two >expect &&
        set_askpass user@host &&
@@ -146,6 +150,13 @@ test_expect_success 'no-op half-auth fetch does not require a password' '
        expect_askpass none
 '
 
+test_expect_success 'redirects send auth to new location' '
+       set_askpass user@host &&
+       git -c credential.useHttpPath=true \
+         clone $HTTPD_URL/smart-redir-auth/repo.git repo-redir-auth &&
+       expect_askpass both user@host auth/smart/repo.git
+'
+
 test_expect_success 'disable dumb http on server' '
        git --git-dir="$HTTPD_DOCUMENT_ROOT_PATH/repo.git" \
                config http.getanyfile false
index f01edffa3c0babf76593fbd211f6606ff2036c74..e06146835cdda5b7264a34a24405aedd9f950006 100755 (executable)
@@ -37,7 +37,7 @@ test_expect_success 'fetch changes via git protocol' '
        test_cmp file clone/file
 '
 
-test_expect_failure 'remote detects correct HEAD' '
+test_expect_success 'remote detects correct HEAD' '
        git push public master:other &&
        (cd clone &&
         git remote set-head -d origin &&
@@ -122,8 +122,7 @@ test_remote_error()
        fi
 
        test_must_fail git "$cmd" "$GIT_DAEMON_URL/$repo" "$@" 2>output &&
-       echo "fatal: remote error: $msg: /$repo" >expect &&
-       test_cmp expect output
+       test_i18ngrep "fatal: remote error: $msg: /$repo" output &&
        ret=$?
        chmod +x "$GIT_DAEMON_DOCUMENT_ROOT_PATH/repo.git"
        (exit $ret)
index 0629149eddcff4e6e724ab82479504deda141141..f8e5a9a4ae1051216e4ef55c8a46c99686e45b5c 100755 (executable)
@@ -36,7 +36,7 @@ test_expect_success 'clone with excess parameters (2)' '
 
 test_expect_success C_LOCALE_OUTPUT 'output from clone' '
        rm -fr dst &&
-       git clone -n "file://$(pwd)/src" dst >output &&
+       git clone -n "file://$(pwd)/src" dst >output 2>&1 &&
        test $(grep Clon output | wc -l) = 1
 '
 
@@ -285,4 +285,15 @@ test_expect_success NOT_MINGW,NOT_CYGWIN 'clone local path foo:bar' '
        git clone "./foo:bar" foobar
 '
 
+test_expect_success 'clone from a repository with two identical branches' '
+
+       (
+               cd src &&
+               git checkout -b another master
+       ) &&
+       git clone src target-11 &&
+       test "z$( cd target-11 && git symbolic-ref HEAD )" = zrefs/heads/another
+
+'
+
 test_done
index 7ff6e0e16cbeb014c1ea551392a63beca2dbc386..c4903687fbc801ffd1db9e619065996f97986ccb 100755 (executable)
@@ -134,4 +134,8 @@ test_expect_success 'cloning a local path with --no-local does not hardlink' '
        ! repo_is_hardlinked force-nonlocal
 '
 
+test_expect_success 'cloning locally respects "-u" for fetching refs' '
+       test_must_fail git clone --bare -u false a should_not_work.git
+'
+
 test_done
index 85cadfad6d8aeee79e0f05530376ca7dc6f085f0..9e24ec88e67f0db8be401f2a60f69c408f39fc02 100755 (executable)
@@ -19,17 +19,19 @@ test_expect_success 'clone -o' '
 
 '
 
-test_expect_success 'redirected clone' '
+test_expect_success 'redirected clone does not show progress' '
 
        git clone "file://$(pwd)/parent" clone-redirected >out 2>err &&
-       test_must_be_empty err
+       ! grep % err &&
+       test_i18ngrep ! "Checking connectivity" err
 
 '
-test_expect_success 'redirected clone -v' '
+
+test_expect_success 'redirected clone -v does show progress' '
 
        git clone --progress "file://$(pwd)/parent" clone-redirected-progress \
                >out 2>err &&
-       test -s err
+       grep % err
 
 '
 
index b10685af4e3e19c79e1e656aadc9aae3430777f0..15e3d6476c78ba3447504aa133e9e1d7ffbfd87d 100755 (executable)
@@ -48,4 +48,12 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' '
        ! grep one output
 '
 
+test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
+       git commit --allow-empty -m another &&
+       git tag -a -m "annotated" v1.0 &&
+       git rev-list --objects ^v1.0^ v1.0 >expect &&
+       git rev-list --objects v1.0^..v1.0 >actual &&
+       test_cmp expect actual
+'
+
 test_done
index 1aa27bdbbf39bd937a6bb66a7eafeda16bb76246..c5e914a781e7aff82f712cecf2b2320320a0dc44 100644 (file)
@@ -325,7 +325,7 @@ trap 'die' EXIT
 . "$TEST_DIRECTORY/test-lib-functions.sh"
 
 # You are not expected to call test_ok_ and test_failure_ directly, use
-# the text_expect_* functions instead.
+# the test_expect_* functions instead.
 
 test_ok_ () {
        test_success=$(($test_success + 1))
index 04a8707bb58e7411a2c272a2716e6b8b340a2c7e..fd96f3be116fa3655d8a0f297bcd6c6c4fec5cb7 100644 (file)
@@ -69,87 +69,28 @@ static ssize_t send_client_data(int fd, const char *data, ssize_t sz)
        return sz;
 }
 
-static FILE *pack_pipe = NULL;
-static void show_commit(struct commit *commit, void *data)
-{
-       if (commit->object.flags & BOUNDARY)
-               fputc('-', pack_pipe);
-       if (fputs(sha1_to_hex(commit->object.sha1), pack_pipe) < 0)
-               die("broken output pipe");
-       fputc('\n', pack_pipe);
-       fflush(pack_pipe);
-       free(commit->buffer);
-       commit->buffer = NULL;
-}
-
-static void show_object(struct object *obj,
-                       const struct name_path *path, const char *component,
-                       void *cb_data)
-{
-       show_object_with_name(pack_pipe, obj, path, component);
-}
-
-static void show_edge(struct commit *commit)
-{
-       fprintf(pack_pipe, "-%s\n", sha1_to_hex(commit->object.sha1));
-}
-
-static int do_rev_list(int in, int out, void *user_data)
-{
-       int i;
-       struct rev_info revs;
-
-       pack_pipe = xfdopen(out, "w");
-       init_revisions(&revs, NULL);
-       revs.tag_objects = 1;
-       revs.tree_objects = 1;
-       revs.blob_objects = 1;
-       if (use_thin_pack)
-               revs.edge_hint = 1;
-
-       for (i = 0; i < want_obj.nr; i++) {
-               struct object *o = want_obj.objects[i].item;
-               /* why??? */
-               o->flags &= ~UNINTERESTING;
-               add_pending_object(&revs, o, NULL);
-       }
-       for (i = 0; i < have_obj.nr; i++) {
-               struct object *o = have_obj.objects[i].item;
-               o->flags |= UNINTERESTING;
-               add_pending_object(&revs, o, NULL);
-       }
-       setup_revisions(0, NULL, &revs, NULL);
-       if (prepare_revision_walk(&revs))
-               die("revision walk setup failed");
-       mark_edges_uninteresting(revs.commits, &revs, show_edge);
-       if (use_thin_pack)
-               for (i = 0; i < extra_edge_obj.nr; i++)
-                       fprintf(pack_pipe, "-%s\n", sha1_to_hex(
-                                       extra_edge_obj.objects[i].item->sha1));
-       traverse_commit_list(&revs, show_commit, show_object, NULL);
-       fflush(pack_pipe);
-       fclose(pack_pipe);
-       return 0;
-}
-
 static void create_pack_file(void)
 {
-       struct async rev_list;
        struct child_process pack_objects;
        char data[8193], progress[128];
        char abort_msg[] = "aborting due to possible repository "
                "corruption on the remote side.";
        int buffered = -1;
        ssize_t sz;
-       const char *argv[10];
-       int arg = 0;
+       const char *argv[12];
+       int i, arg = 0;
+       FILE *pipe_fd;
+       char *shallow_file = NULL;
 
-       argv[arg++] = "pack-objects";
-       if (!shallow_nr) {
-               argv[arg++] = "--revs";
-               if (use_thin_pack)
-                       argv[arg++] = "--thin";
+       if (shallow_nr) {
+               shallow_file = setup_temporary_shallow();
+               argv[arg++] = "--shallow-file";
+               argv[arg++] = shallow_file;
        }
+       argv[arg++] = "pack-objects";
+       argv[arg++] = "--revs";
+       if (use_thin_pack)
+               argv[arg++] = "--thin";
 
        argv[arg++] = "--stdout";
        if (!no_progress)
@@ -170,29 +111,21 @@ static void create_pack_file(void)
        if (start_command(&pack_objects))
                die("git upload-pack: unable to fork git-pack-objects");
 
-       if (shallow_nr) {
-               memset(&rev_list, 0, sizeof(rev_list));
-               rev_list.proc = do_rev_list;
-               rev_list.out = pack_objects.in;
-               if (start_async(&rev_list))
-                       die("git upload-pack: unable to fork git-rev-list");
-       }
-       else {
-               FILE *pipe_fd = xfdopen(pack_objects.in, "w");
-               int i;
-
-               for (i = 0; i < want_obj.nr; i++)
-                       fprintf(pipe_fd, "%s\n",
-                               sha1_to_hex(want_obj.objects[i].item->sha1));
-               fprintf(pipe_fd, "--not\n");
-               for (i = 0; i < have_obj.nr; i++)
-                       fprintf(pipe_fd, "%s\n",
-                               sha1_to_hex(have_obj.objects[i].item->sha1));
-               fprintf(pipe_fd, "\n");
-               fflush(pipe_fd);
-               fclose(pipe_fd);
-       }
-
+       pipe_fd = xfdopen(pack_objects.in, "w");
+
+       for (i = 0; i < want_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(want_obj.objects[i].item->sha1));
+       fprintf(pipe_fd, "--not\n");
+       for (i = 0; i < have_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(have_obj.objects[i].item->sha1));
+       for (i = 0; i < extra_edge_obj.nr; i++)
+               fprintf(pipe_fd, "%s\n",
+                       sha1_to_hex(extra_edge_obj.objects[i].item->sha1));
+       fprintf(pipe_fd, "\n");
+       fflush(pipe_fd);
+       fclose(pipe_fd);
 
        /* We read from pack_objects.err to capture stderr output for
         * progress bar, and pack_objects.out to capture the pack data.
@@ -308,8 +241,11 @@ static void create_pack_file(void)
                error("git upload-pack: git-pack-objects died with error.");
                goto fail;
        }
-       if (shallow_nr && finish_async(&rev_list))
-               goto fail;      /* error was already reported */
+       if (shallow_file) {
+               if (*shallow_file)
+                       unlink(shallow_file);
+               free(shallow_file);
+       }
 
        /* flush the data */
        if (0 <= buffered) {
@@ -752,6 +688,16 @@ static int mark_our_ref(const char *refname, const unsigned char *sha1, int flag
        return 0;
 }
 
+static void format_symref_info(struct strbuf *buf, struct string_list *symref)
+{
+       struct string_list_item *item;
+
+       if (!symref->nr)
+               return;
+       for_each_string_list_item(item, symref)
+               strbuf_addf(buf, " symref=%s:%s", item->string, (char *)item->util);
+}
+
 static int send_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
 {
        static const char *capabilities = "multi_ack thin-pack side-band"
@@ -760,35 +706,64 @@ static int send_ref(const char *refname, const unsigned char *sha1, int flag, vo
        const char *refname_nons = strip_namespace(refname);
        unsigned char peeled[20];
 
-       if (mark_our_ref(refname, sha1, flag, cb_data))
+       if (mark_our_ref(refname, sha1, flag, NULL))
                return 0;
 
-       if (capabilities)
-               packet_write(1, "%s %s%c%s%s%s agent=%s\n",
+       if (capabilities) {
+               struct strbuf symref_info = STRBUF_INIT;
+
+               format_symref_info(&symref_info, cb_data);
+               packet_write(1, "%s %s%c%s%s%s%s agent=%s\n",
                             sha1_to_hex(sha1), refname_nons,
                             0, capabilities,
                             allow_tip_sha1_in_want ? " allow-tip-sha1-in-want" : "",
                             stateless_rpc ? " no-done" : "",
+                            symref_info.buf,
                             git_user_agent_sanitized());
-       else
+               strbuf_release(&symref_info);
+       } else {
                packet_write(1, "%s %s\n", sha1_to_hex(sha1), refname_nons);
+       }
        capabilities = NULL;
        if (!peel_ref(refname, peeled))
                packet_write(1, "%s %s^{}\n", sha1_to_hex(peeled), refname_nons);
        return 0;
 }
 
+static int find_symref(const char *refname, const unsigned char *sha1, int flag,
+                      void *cb_data)
+{
+       const char *symref_target;
+       struct string_list_item *item;
+       unsigned char unused[20];
+
+       if ((flag & REF_ISSYMREF) == 0)
+               return 0;
+       symref_target = resolve_ref_unsafe(refname, unused, 0, &flag);
+       if (!symref_target || (flag & REF_ISSYMREF) == 0)
+               die("'%s' is a symref but it is not?", refname);
+       item = string_list_append(cb_data, refname);
+       item->util = xstrdup(symref_target);
+       return 0;
+}
+
 static void upload_pack(void)
 {
+       struct string_list symref = STRING_LIST_INIT_DUP;
+
+       head_ref_namespaced(find_symref, &symref);
+       for_each_namespaced_ref(find_symref, &symref);
+
        if (advertise_refs || !stateless_rpc) {
                reset_timeout();
-               head_ref_namespaced(send_ref, NULL);
-               for_each_namespaced_ref(send_ref, NULL);
+               head_ref_namespaced(send_ref, &symref);
+               for_each_namespaced_ref(send_ref, &symref);
                packet_flush(1);
        } else {
                head_ref_namespaced(mark_our_ref, NULL);
                for_each_namespaced_ref(mark_our_ref, NULL);
        }
+       string_list_clear(&symref, 1);
        if (advertise_refs)
                return;