]> git.ipfire.org Git - thirdparty/git.git/blobdiff - builtin/fetch.c
Merge branch 'so/separate-field-for-m-and-diff-merges'
[thirdparty/git.git] / builtin / fetch.c
index c49f0e975203b039cbcf2ccab9860904f3f67e47..a6d326866112758f62b50d7b36eab1314bda5e6f 100644 (file)
@@ -56,6 +56,7 @@ static int prune_tags = -1; /* unspecified */
 #define PRUNE_TAGS_BY_DEFAULT 0 /* do we prune tags by default? */
 
 static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int write_fetch_head = 1;
 static int verbosity, deepen_relative, set_upstream;
 static int progress = -1;
 static int enable_auto_gc = 1;
@@ -79,6 +80,7 @@ static struct list_objects_filter_options filter_options;
 static struct string_list server_options = STRING_LIST_INIT_DUP;
 static struct string_list negotiation_tip = STRING_LIST_INIT_NODUP;
 static int fetch_write_commit_graph = -1;
+static int stdin_refspecs = 0;
 
 static int git_fetch_config(const char *k, const char *v, void *cb)
 {
@@ -162,6 +164,8 @@ static struct option builtin_fetch_options[] = {
                    PARSE_OPT_OPTARG, option_fetch_parse_recurse_submodules),
        OPT_BOOL(0, "dry-run", &dry_run,
                 N_("dry run")),
+       OPT_BOOL(0, "write-fetch-head", &write_fetch_head,
+                N_("write fetched references to the FETCH_HEAD file")),
        OPT_BOOL('k', "keep", &keep, N_("keep downloaded pack")),
        OPT_BOOL('u', "update-head-ok", &update_head_ok,
                    N_("allow updating of HEAD ref")),
@@ -202,6 +206,8 @@ static struct option builtin_fetch_options[] = {
                 N_("check for forced-updates on all updated branches")),
        OPT_BOOL(0, "write-commit-graph", &fetch_write_commit_graph,
                 N_("write the commit-graph after fetching")),
+       OPT_BOOL(0, "stdin", &stdin_refspecs,
+                N_("accept refspecs from stdin")),
        OPT_END()
 };
 
@@ -439,6 +445,7 @@ static struct ref *get_ref_map(struct remote *remote,
        struct ref *orefs = NULL, **oref_tail = &orefs;
 
        struct hashmap existing_refs;
+       int existing_refs_populated = 0;
 
        if (rs->nr) {
                struct refspec *fetch_refspec;
@@ -532,15 +539,18 @@ static struct ref *get_ref_map(struct remote *remote,
 
        ref_map = ref_remove_duplicates(ref_map);
 
-       refname_hash_init(&existing_refs);
-       for_each_ref(add_one_refname, &existing_refs);
-
        for (rm = ref_map; rm; rm = rm->next) {
                if (rm->peer_ref) {
                        const char *refname = rm->peer_ref->name;
                        struct refname_hash_entry *peer_item;
                        unsigned int hash = strhash(refname);
 
+                       if (!existing_refs_populated) {
+                               refname_hash_init(&existing_refs);
+                               for_each_ref(add_one_refname, &existing_refs);
+                               existing_refs_populated = 1;
+                       }
+
                        peer_item = hashmap_get_entry_from_hash(&existing_refs,
                                                hash, refname,
                                                struct refname_hash_entry, ent);
@@ -550,7 +560,8 @@ static struct ref *get_ref_map(struct remote *remote,
                        }
                }
        }
-       hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
+       if (existing_refs_populated)
+               hashmap_free_entries(&existing_refs, struct refname_hash_entry, ent);
 
        return ref_map;
 }
@@ -645,7 +656,7 @@ static void prepare_format_display(struct ref *ref_map)
        struct ref *rm;
        const char *format = "full";
 
-       git_config_get_string_const("fetch.output", &format);
+       git_config_get_string_tmp("fetch.output", &format);
        if (!strcasecmp(format, "full"))
                compact_format = 0;
        else if (!strcasecmp(format, "compact"))
@@ -893,7 +904,9 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
        const char *what, *kind;
        struct ref *rm;
        char *url;
-       const char *filename = dry_run ? "/dev/null" : git_path_fetch_head(the_repository);
+       const char *filename = (!write_fetch_head
+                               ? "/dev/null"
+                               : git_path_fetch_head(the_repository));
        int want_status;
        int summary_width = transport_summary_width(ref_map);
 
@@ -1010,11 +1023,17 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
                                rc |= update_local_ref(ref, what, rm, &note,
                                                       summary_width);
                                free(ref);
-                       } else
+                       } else if (write_fetch_head || dry_run) {
+                               /*
+                                * Display fetches written to FETCH_HEAD (or
+                                * would be written to FETCH_HEAD, if --dry-run
+                                * is set).
+                                */
                                format_display(&note, '*',
                                               *kind ? kind : "branch", NULL,
                                               *what ? what : "HEAD",
                                               "FETCH_HEAD", summary_width);
+                       }
                        if (note.len) {
                                if (verbosity >= 0 && !shown_url) {
                                        fprintf(stderr, _("From %.*s\n"),
@@ -1327,7 +1346,7 @@ static int do_fetch(struct transport *transport,
        }
 
        /* if not appending, truncate FETCH_HEAD */
-       if (!append && !dry_run) {
+       if (!append && write_fetch_head) {
                retcode = truncate_fetch_head();
                if (retcode)
                        goto cleanup;
@@ -1594,7 +1613,7 @@ static int fetch_multiple(struct string_list *list, int max_children)
        int i, result = 0;
        struct strvec argv = STRVEC_INIT;
 
-       if (!append && !dry_run) {
+       if (!append && write_fetch_head) {
                int errcode = truncate_fetch_head();
                if (errcode)
                        return errcode;
@@ -1675,7 +1694,8 @@ static inline void fetch_one_setup_partial(struct remote *remote)
        return;
 }
 
-static int fetch_one(struct remote *remote, int argc, const char **argv, int prune_tags_ok)
+static int fetch_one(struct remote *remote, int argc, const char **argv,
+                    int prune_tags_ok, int use_stdin_refspecs)
 {
        struct refspec rs = REFSPEC_INIT_FETCH;
        int i;
@@ -1732,6 +1752,13 @@ static int fetch_one(struct remote *remote, int argc, const char **argv, int pru
                }
        }
 
+       if (use_stdin_refspecs) {
+               struct strbuf line = STRBUF_INIT;
+               while (strbuf_getline_lf(&line, stdin) != EOF)
+                       refspec_append(&rs, line.buf);
+               strbuf_release(&line);
+       }
+
        if (server_options.nr)
                gtransport->server_options = &server_options;
 
@@ -1766,12 +1793,18 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                free(anon);
        }
 
-       fetch_config_from_gitmodules(&submodule_fetch_jobs_config,
-                                    &recurse_submodules);
        git_config(git_fetch_config, NULL);
 
        argc = parse_options(argc, argv, prefix,
                             builtin_fetch_options, builtin_fetch_usage, 0);
+       if (recurse_submodules != RECURSE_SUBMODULES_OFF) {
+               int *sfjc = submodule_fetch_jobs_config == -1
+                           ? &submodule_fetch_jobs_config : NULL;
+               int *rs = recurse_submodules == RECURSE_SUBMODULES_DEFAULT
+                         ? &recurse_submodules : NULL;
+
+               fetch_config_from_gitmodules(sfjc, rs);
+       }
 
        if (deepen_relative) {
                if (deepen_relative < 0)
@@ -1795,6 +1828,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
+       /* FETCH_HEAD never gets updated in --dry-run mode */
+       if (dry_run)
+               write_fetch_head = 0;
+
        if (all) {
                if (argc == 1)
                        die(_("fetch --all does not take a repository argument"));
@@ -1828,7 +1865,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (remote) {
                if (filter_options.choice || has_promisor_remote())
                        fetch_one_setup_partial(remote);
-               result = fetch_one(remote, argc, argv, prune_tags_ok);
+               result = fetch_one(remote, argc, argv, prune_tags_ok, stdin_refspecs);
        } else {
                int max_children = max_jobs;
 
@@ -1836,6 +1873,10 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
                        die(_("--filter can only be used with the remote "
                              "configured in extensions.partialclone"));
 
+               if (stdin_refspecs)
+                       die(_("--stdin can only be used when fetching "
+                             "from one remote"));
+
                if (max_children < 0)
                        max_children = fetch_parallel_config;