Code

Merge branch 'maint'
[git.git] / builtin / fetch.c
index 6cbb5f69ecf0946f4028b88563347332d54f65bc..91731b909aeb22bf8d4e366b8b92281ac0f9ac0c 100644 (file)
@@ -13,6 +13,7 @@
 #include "sigchain.h"
 #include "transport.h"
 #include "submodule.h"
+#include "connected.h"
 
 static const char * const builtin_fetch_usage[] = {
        "git fetch [<options>] [<repository> [<refspec>...]]",
@@ -28,12 +29,6 @@ enum {
        TAGS_SET = 2
 };
 
-enum {
-       RECURSE_SUBMODULES_OFF = 0,
-       RECURSE_SUBMODULES_DEFAULT = 1,
-       RECURSE_SUBMODULES_ON = 2
-};
-
 static int all, append, dry_run, force, keep, multiple, prune, update_head_ok, verbosity;
 static int progress, recurse_submodules = RECURSE_SUBMODULES_DEFAULT;
 static int tags = TAGS_DEFAULT;
@@ -42,6 +37,21 @@ static const char *upload_pack;
 static struct strbuf default_rla = STRBUF_INIT;
 static struct transport *transport;
 static const char *submodule_prefix = "";
+static const char *recurse_submodules_default;
+
+static int option_parse_recurse_submodules(const struct option *opt,
+                                  const char *arg, int unset)
+{
+       if (unset) {
+               recurse_submodules = RECURSE_SUBMODULES_OFF;
+       } else {
+               if (arg)
+                       recurse_submodules = parse_fetch_recurse_submodules_arg(opt->long_name, arg);
+               else
+                       recurse_submodules = RECURSE_SUBMODULES_ON;
+       }
+       return 0;
+}
 
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
@@ -60,9 +70,9 @@ static struct option builtin_fetch_options[] = {
                    "do not fetch all tags (--no-tags)", TAGS_UNSET),
        OPT_BOOLEAN('p', "prune", &prune,
                    "prune remote-tracking branches no longer on remote"),
-       OPT_SET_INT(0, "recurse-submodules", &recurse_submodules,
+       { OPTION_CALLBACK, 0, "recurse-submodules", NULL, "on-demand",
                    "control recursive fetching of submodules",
-                   RECURSE_SUBMODULES_ON),
+                   PARSE_OPT_OPTARG, option_parse_recurse_submodules },
        OPT_BOOLEAN(0, "dry-run", &dry_run,
                    "dry run"),
        OPT_BOOLEAN('k', "keep", &keep, "keep downloaded pack"),
@@ -73,6 +83,9 @@ static struct option builtin_fetch_options[] = {
                   "deepen history of shallow clone"),
        { OPTION_STRING, 0, "submodule-prefix", &submodule_prefix, "dir",
                   "prepend this to submodule path output", PARSE_OPT_HIDDEN },
+       { OPTION_STRING, 0, "recurse-submodules-default",
+                  &recurse_submodules_default, NULL,
+                  "default mode for recursion", PARSE_OPT_HIDDEN },
        OPT_END()
 };
 
@@ -284,6 +297,9 @@ static int update_local_ref(struct ref *ref,
                else {
                        msg = "storing head";
                        what = _("[new branch]");
+                       if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                           (recurse_submodules != RECURSE_SUBMODULES_ON))
+                               check_for_new_submodule_commits(ref->new_sha1);
                }
 
                r = s_update_ref(msg, ref, 0);
@@ -299,6 +315,9 @@ static int update_local_ref(struct ref *ref,
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "..");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                   (recurse_submodules != RECURSE_SUBMODULES_ON))
+                       check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("fast-forward", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s%s", r ? '!' : ' ',
                        TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
@@ -310,6 +329,9 @@ static int update_local_ref(struct ref *ref,
                strcpy(quickref, find_unique_abbrev(current->object.sha1, DEFAULT_ABBREV));
                strcat(quickref, "...");
                strcat(quickref, find_unique_abbrev(ref->new_sha1, DEFAULT_ABBREV));
+               if ((recurse_submodules != RECURSE_SUBMODULES_OFF) &&
+                   (recurse_submodules != RECURSE_SUBMODULES_ON))
+                       check_for_new_submodule_commits(ref->new_sha1);
                r = s_update_ref("forced-update", ref, 1);
                sprintf(display, "%c %-*s %-*s -> %s  (%s)", r ? '!' : '+',
                        TRANSPORT_SUMMARY_WIDTH, quickref, REFCOL_WIDTH, remote,
@@ -324,6 +346,18 @@ static int update_local_ref(struct ref *ref,
        }
 }
 
+static int iterate_ref_map(void *cb_data, unsigned char sha1[20])
+{
+       struct ref **rm = cb_data;
+       struct ref *ref = *rm;
+
+       if (!ref)
+               return -1; /* end of the list */
+       *rm = ref->next;
+       hashcpy(sha1, ref->old_sha1);
+       return 0;
+}
+
 static int store_updated_refs(const char *raw_url, const char *remote_name,
                struct ref *ref_map)
 {
@@ -343,6 +377,13 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                url = transport_anonymize_url(raw_url);
        else
                url = xstrdup("foreign");
+
+       rm = ref_map;
+       if (check_everything_connected(iterate_ref_map, 0, &rm)) {
+               rc = error(_("%s did not send all necessary objects\n"), url);
+               goto abort;
+       }
+
        for (rm = ref_map; rm; rm = rm->next) {
                struct ref *ref = NULL;
 
@@ -423,12 +464,15 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                fprintf(stderr, " %s\n", note);
                }
        }
-       free(url);
-       fclose(fp);
+
        if (rc & STORE_REF_ERROR_DF_CONFLICT)
                error(_("some local refs could not be updated; try running\n"
                      " 'git remote prune %s' to remove any old, conflicting "
                      "branches"), remote_name);
+
+ abort:
+       free(url);
+       fclose(fp);
        return rc;
 }
 
@@ -436,23 +480,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
  * We would want to bypass the object transfer altogether if
  * everything we are going to fetch already exists and is connected
  * locally.
- *
- * The refs we are going to fetch are in ref_map.  If running
- *
- *  $ git rev-list --objects --stdin --not --all
- *
- * (feeding all the refs in ref_map on its standard input)
- * 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;
-       int err;
-       const char *argv[] = {"rev-list",
-               "--quiet", "--objects", "--stdin", "--not", "--all", NULL};
+       struct ref *rm = ref_map;
 
        /*
         * If we are deepening a shallow clone we already have these
@@ -463,47 +494,7 @@ static int quickfetch(struct ref *ref_map)
         */
        if (depth)
                return -1;
-
-       if (!ref_map)
-               return 0;
-
-       memset(&revlist, 0, sizeof(revlist));
-       revlist.argv = argv;
-       revlist.git_cmd = 1;
-       revlist.no_stdout = 1;
-       revlist.no_stderr = 1;
-       revlist.in = -1;
-
-       err = start_command(&revlist);
-       if (err) {
-               error(_("could not run rev-list"));
-               return err;
-       }
-
-       /*
-        * If rev-list --stdin encounters an unknown commit, it terminates,
-        * which will cause SIGPIPE in the write loop below.
-        */
-       sigchain_push(SIGPIPE, SIG_IGN);
-
-       for (ref = ref_map; ref; ref = ref->next) {
-               if (write_in_full(revlist.in, sha1_to_hex(ref->old_sha1), 40) < 0 ||
-                   write_str_in_full(revlist.in, "\n") < 0) {
-                       if (errno != EPIPE && errno != EINVAL)
-                               error(_("failed write to rev-list: %s"), strerror(errno));
-                       err = -1;
-                       break;
-               }
-       }
-
-       if (close(revlist.in)) {
-               error(_("failed to close rev-list's stdin: %s"), strerror(errno));
-               err = -1;
-       }
-
-       sigchain_pop(SIGPIPE);
-
-       return finish_command(&revlist) || err;
+       return check_everything_connected(iterate_ref_map, 1, &rm);
 }
 
 static int fetch_refs(struct transport *transport, struct ref *ref_map)
@@ -519,10 +510,10 @@ static int fetch_refs(struct transport *transport, struct ref *ref_map)
        return ret;
 }
 
-static int prune_refs(struct transport *transport, struct ref *ref_map)
+static int prune_refs(struct refspec *refs, int ref_count, struct ref *ref_map)
 {
        int result = 0;
-       struct ref *ref, *stale_refs = get_stale_heads(transport->remote, ref_map);
+       struct ref *ref, *stale_refs = get_stale_heads(refs, ref_count, ref_map);
        const char *dangling_msg = dry_run
                ? _("   (%s will become dangling)\n")
                : _("   (%s has become dangling)\n");
@@ -713,8 +704,31 @@ static int do_fetch(struct transport *transport,
                free_refs(ref_map);
                return 1;
        }
-       if (prune)
-               prune_refs(transport, ref_map);
+       if (prune) {
+               /* If --tags was specified, pretend the user gave us the canonical tags refspec */
+               if (tags == TAGS_SET) {
+                       const char *tags_str = "refs/tags/*:refs/tags/*";
+                       struct refspec *tags_refspec, *refspec;
+
+                       /* Copy the refspec and add the tags to it */
+                       refspec = xcalloc(ref_count + 1, sizeof(struct refspec));
+                       tags_refspec = parse_fetch_refspec(1, &tags_str);
+                       memcpy(refspec, refs, ref_count * sizeof(struct refspec));
+                       memcpy(&refspec[ref_count], tags_refspec, sizeof(struct refspec));
+                       ref_count++;
+
+                       prune_refs(refspec, ref_count, ref_map);
+
+                       ref_count--;
+                       /* The rest of the strings belong to fetch_one */
+                       free_refspec(1, tags_refspec);
+                       free(refspec);
+               } else if (ref_count) {
+                       prune_refs(refs, ref_count, ref_map);
+               } else {
+                       prune_refs(transport->remote->fetch, transport->remote->fetch_refspec_nr, ref_map);
+               }
+       }
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -810,6 +824,8 @@ static void add_options_to_argv(int *argc, const char **argv)
                argv[(*argc)++] = "--keep";
        if (recurse_submodules == RECURSE_SUBMODULES_ON)
                argv[(*argc)++] = "--recurse-submodules";
+       else if (recurse_submodules == RECURSE_SUBMODULES_ON_DEMAND)
+               argv[(*argc)++] = "--recurse-submodules=on-demand";
        if (verbosity >= 2)
                argv[(*argc)++] = "-v";
        if (verbosity >= 1)
@@ -852,6 +868,7 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 {
        int i;
        static const char **refs = NULL;
+       struct refspec *refspec;
        int ref_nr = 0;
        int exit_code;
 
@@ -892,8 +909,9 @@ static int fetch_one(struct remote *remote, int argc, const char **argv)
 
        sigchain_push_common(unlock_pack_on_signal);
        atexit(unlock_pack);
-       exit_code = do_fetch(transport,
-                       parse_fetch_refspec(ref_nr, refs), ref_nr);
+       refspec = parse_fetch_refspec(ref_nr, refs);
+       exit_code = do_fetch(transport, refspec, ref_nr);
+       free_refspec(ref_nr, refspec);
        transport_disconnect(transport);
        transport = NULL;
        return exit_code;
@@ -916,6 +934,15 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
 
+       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+               if (recurse_submodules_default) {
+                       int arg = parse_fetch_recurse_submodules_arg("--recurse-submodules-default", recurse_submodules_default);
+                       set_config_fetch_recurse_submodules(arg);
+               }
+               gitmodules_config();
+               git_config(submodule_config, NULL);
+       }
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
@@ -951,15 +978,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (!result && (recurse_submodules != RECURSE_SUBMODULES_OFF)) {
                const char *options[10];
                int num_options = 0;
-               /* Set recursion as default when we already are recursing */
-               if (submodule_prefix[0])
-                       set_config_fetch_recurse_submodules(1);
-               gitmodules_config();
-               git_config(submodule_config, NULL);
                add_options_to_argv(&num_options, options);
                result = fetch_populated_submodules(num_options, options,
                                                    submodule_prefix,
-                                                   recurse_submodules == RECURSE_SUBMODULES_ON,
+                                                   recurse_submodules,
                                                    verbosity < 0);
        }