Merge branch 'sp/fetch-fix'
authorJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:26:04 +0000 (14:26 -0800)
committerJunio C Hamano <gitster@pobox.com>
Wed, 14 Nov 2007 22:26:04 +0000 (14:26 -0800)
* sp/fetch-fix:
  git-fetch: avoid local fetching from alternate (again)
  rev-list: Introduce --quiet to avoid /dev/null redirects
  run-command: Support sending stderr to /dev/null
  git-fetch: Always fetch tags if the object they reference exists

Documentation/git-rev-list.txt
builtin-fetch.c
builtin-rev-list.c
run-command.c
run-command.h
t/t5502-quickfetch.sh

index 485280423e90bac43b4c372bbc8d350c34cf4fa8..989fbf3562977866d7d2558022587847f538ae4b 100644 (file)
@@ -20,6 +20,7 @@ SYNOPSIS
             [ \--not ]
             [ \--all ]
             [ \--stdin ]
+            [ \--quiet ]
             [ \--topo-order ]
             [ \--parents ]
             [ \--timestamp ]
@@ -270,6 +271,14 @@ limiting may be applied.
        In addition to the '<commit>' listed on the command
        line, read them from the standard input.
 
+--quiet::
+
+       Don't print anything to standard output.  This form of
+       git-rev-list is primarly meant to allow the caller to
+       test the exit status to see if a range of objects is fully
+       connected (or not).  It is faster than redirecting stdout
+       to /dev/null as the output does not have to be formatted.
+
 --cherry-pick::
 
        Omit any commit that introduces the same change as
index e65690f25cdadb7b75aad40718510b1b9bdb8800..be9e3ea2bcea0d0ab88a51d396aff5714822b77a 100644 (file)
@@ -8,10 +8,12 @@
 #include "path-list.h"
 #include "remote.h"
 #include "transport.h"
+#include "run-command.h"
 
 static const char fetch_usage[] = "git-fetch [-a | --append] [--upload-pack <upload-pack>] [-f | --force] [--no-tags] [-t | --tags] [-k | --keep] [-u | --update-head-ok] [--depth <depth>] [-v | --verbose] [<repository> <refspec>...]";
 
 static int append, force, tags, no_tags, update_head_ok, verbose, quiet;
+static const char *depth;
 static char *default_rla = NULL;
 static struct transport *transport;
 
@@ -335,9 +337,72 @@ static void store_updated_refs(const char *url, struct ref *ref_map)
        fclose(fp);
 }
 
+/*
+ * We would want to bypass the object transfer altogether if
+ * everything we are going to fetch already exists and connected
+ * locally.
+ *
+ * The refs we are going to fetch are in to_fetch (nr_heads in
+ * total).  If running
+ *
+ *  $ git-rev-list --objects to_fetch[0] to_fetch[1] ... --not --all
+ *
+ * does not error out, that means everything reachable from the
+ * refs we are going to fetch exists and is connected to some of
+ * our existing refs.
+ */
+static int quickfetch(struct ref *ref_map)
+{
+       struct child_process revlist;
+       struct ref *ref;
+       char **argv;
+       int i, err;
+
+       /*
+        * If we are deepening a shallow clone we already have these
+        * objects reachable.  Running rev-list here will return with
+        * a good (0) exit status and we'll bypass the fetch that we
+        * really need to perform.  Claiming failure now will ensure
+        * we perform the network exchange to deepen our history.
+        */
+       if (depth)
+               return -1;
+
+       for (i = 0, ref = ref_map; ref; ref = ref->next)
+               i++;
+       if (!i)
+               return 0;
+
+       argv = xmalloc(sizeof(*argv) * (i + 6));
+       i = 0;
+       argv[i++] = xstrdup("rev-list");
+       argv[i++] = xstrdup("--quiet");
+       argv[i++] = xstrdup("--objects");
+       for (ref = ref_map; ref; ref = ref->next)
+               argv[i++] = xstrdup(sha1_to_hex(ref->old_sha1));
+       argv[i++] = xstrdup("--not");
+       argv[i++] = xstrdup("--all");
+       argv[i++] = NULL;
+
+       memset(&revlist, 0, sizeof(revlist));
+       revlist.argv = (const char**)argv;
+       revlist.git_cmd = 1;
+       revlist.no_stdin = 1;
+       revlist.no_stdout = 1;
+       revlist.no_stderr = 1;
+       err = run_command(&revlist);
+
+       for (i = 0; argv[i]; i++)
+               free(argv[i]);
+       free(argv);
+       return err;
+}
+
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
 {
-       int ret = transport_fetch_refs(transport, ref_map);
+       int ret = quickfetch(ref_map);
+       if (ret)
+               ret = transport_fetch_refs(transport, ref_map);
        if (!ret)
                store_updated_refs(transport->url, ref_map);
        transport_unlock_pack(transport);
@@ -389,7 +454,7 @@ static struct ref *find_non_local_tags(struct transport *transport,
 
                if (!path_list_has_path(&existing_refs, ref_name) &&
                    !path_list_has_path(&new_refs, ref_name) &&
-                   lookup_object(ref->old_sha1)) {
+                   has_sha1_file(ref->old_sha1)) {
                        path_list_insert(ref_name, &new_refs);
 
                        rm = alloc_ref(strlen(ref_name) + 1);
@@ -473,7 +538,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        static const char **refs = NULL;
        int ref_nr = 0;
        int cmd_len = 0;
-       const char *depth = NULL, *upload_pack = NULL;
+       const char *upload_pack = NULL;
        int keep = 0;
 
        for (i = 1; i < argc; i++) {
index 697046723fc64f38ab4fb589c84f1c5d5934894a..f5149e59b7c04d943f3b90f706a66d185f8a396c 100644 (file)
@@ -26,6 +26,7 @@ static const char rev_list_usage[] =
 "    --remove-empty\n"
 "    --all\n"
 "    --stdin\n"
+"    --quiet\n"
 "  ordering output:\n"
 "    --topo-order\n"
 "    --date-order\n"
@@ -50,6 +51,7 @@ static int show_timestamp;
 static int hdr_termination;
 static const char *header_prefix;
 
+static void finish_commit(struct commit *commit);
 static void show_commit(struct commit *commit)
 {
        if (show_timestamp)
@@ -93,6 +95,11 @@ static void show_commit(struct commit *commit)
                strbuf_release(&buf);
        }
        maybe_flush_or_die(stdout, "stdout");
+       finish_commit(commit);
+}
+
+static void finish_commit(struct commit *commit)
+{
        if (commit->parents) {
                free_commit_list(commit->parents);
                commit->parents = NULL;
@@ -101,6 +108,12 @@ static void show_commit(struct commit *commit)
        commit->buffer = NULL;
 }
 
+static void finish_object(struct object_array_entry *p)
+{
+       if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
+               die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
+}
+
 static void show_object(struct object_array_entry *p)
 {
        /* An object with name "foo\n0000000..." can be used to
@@ -108,9 +121,7 @@ static void show_object(struct object_array_entry *p)
         */
        const char *ep = strchr(p->name, '\n');
 
-       if (p->item->type == OBJ_BLOB && !has_sha1_file(p->item->sha1))
-               die("missing blob object '%s'", sha1_to_hex(p->item->sha1));
-
+       finish_object(p);
        if (ep) {
                printf("%s %.*s\n", sha1_to_hex(p->item->sha1),
                       (int) (ep - p->name),
@@ -527,6 +538,7 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
        int read_from_stdin = 0;
        int bisect_show_vars = 0;
        int bisect_find_all = 0;
+       int quiet = 0;
 
        git_config(git_default_config);
        init_revisions(&revs, prefix);
@@ -565,6 +577,10 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                        read_revisions_from_stdin(&revs);
                        continue;
                }
+               if (!strcmp(arg, "--quiet")) {
+                       quiet = 1;
+                       continue;
+               }
                usage(rev_list_usage);
 
        }
@@ -640,7 +656,9 @@ int cmd_rev_list(int argc, const char **argv, const char *prefix)
                }
        }
 
-       traverse_commit_list(&revs, show_commit, show_object);
+       traverse_commit_list(&revs,
+               quiet ? finish_commit : show_commit,
+               quiet ? finish_object : show_object);
 
        return 0;
 }
index d99a6c4ea7c776bec717c9952f77db7fa9a69c21..476d00c2182e3af82a0cfe495c61c9df1eb44d26 100644 (file)
@@ -41,7 +41,7 @@ int start_command(struct child_process *cmd)
                cmd->close_out = 1;
        }
 
-       need_err = cmd->err < 0;
+       need_err = !cmd->no_stderr && cmd->err < 0;
        if (need_err) {
                if (pipe(fderr) < 0) {
                        if (need_in)
@@ -87,7 +87,9 @@ int start_command(struct child_process *cmd)
                        close(cmd->out);
                }
 
-               if (need_err) {
+               if (cmd->no_stderr)
+                       dup_devnull(2);
+               else if (need_err) {
                        dup2(fderr[1], 2);
                        close_pair(fderr);
                }
index 94e1e9d516887d818f99f8f6d6b3ded3f3be6d6f..1fc781d7668468f9e74bd430b7569dc040440ba8 100644 (file)
@@ -23,6 +23,7 @@ struct child_process {
        unsigned close_out:1;
        unsigned no_stdin:1;
        unsigned no_stdout:1;
+       unsigned no_stderr:1;
        unsigned git_cmd:1; /* if this is to be git sub-command */
        unsigned stdout_to_stderr:1;
 };
index b4760f2dc0bb690429b358cefde911db1fb26e9a..16eadd6b68664884836976aafb6dcbb582603c09 100755 (executable)
@@ -86,4 +86,37 @@ test_expect_success 'quickfetch should not leave a corrupted repository' '
 
 '
 
+test_expect_success 'quickfetch should not copy from alternate' '
+
+       (
+               mkdir quickclone &&
+               cd quickclone &&
+               git init-db &&
+               (cd ../.git/objects && pwd) >.git/objects/info/alternates &&
+               git remote add origin .. &&
+               git fetch -k -k
+       ) &&
+       obj_cnt=$( (
+               cd quickclone &&
+               git count-objects | sed -e "s/ *objects,.*//"
+       ) ) &&
+       pck_cnt=$( (
+               cd quickclone &&
+               git count-objects -v | sed -n -e "/packs:/{
+                               s/packs://
+                               p
+                               q
+                       }"
+       ) ) &&
+       origin_master=$( (
+               cd quickclone &&
+               git rev-parse origin/master
+       ) ) &&
+       echo "loose objects: $obj_cnt, packfiles: $pck_cnt" &&
+       test $obj_cnt -eq 0 &&
+       test $pck_cnt -eq 0 &&
+       test z$origin_master = z$(git rev-parse master)
+
+'
+
 test_done