Merge branch 'ds/commit-graph-on-fetch'
[git/git.git] / builtin / fetch.c
index d36a403..67c0eb8 100644 (file)
@@ -23,6 +23,8 @@
 #include "packfile.h"
 #include "list-objects-filter-options.h"
 #include "commit-reach.h"
+#include "branch.h"
+#include "promisor-remote.h"
 #include "commit-graph.h"
 
 #define FORCED_UPDATES_DELAY_WARNING_IN_MS (10 * 1000)
@@ -51,7 +53,8 @@ static int fetch_prune_tags_config = -1; /* unspecified */
 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, verbosity, deepen_relative;
+static int all, append, dry_run, force, keep, multiple, update_head_ok;
+static int verbosity, deepen_relative, set_upstream;
 static int progress = -1;
 static int enable_auto_gc = 1;
 static int tags = TAGS_DEFAULT, unshallow, update_shallow, deepen;
@@ -124,6 +127,8 @@ static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
                 N_("fetch from all remotes")),
+       OPT_BOOL(0, "set-upstream", &set_upstream,
+                N_("set upstream for git pull/fetch")),
        OPT_BOOL('a', "append", &append,
                 N_("append to .git/FETCH_HEAD instead of overwriting")),
        OPT_STRING(0, "upload-pack", &upload_pack, N_("path"),
@@ -851,6 +856,15 @@ static int iterate_ref_map(void *cb_data, struct object_id *oid)
        return 0;
 }
 
+static const char warn_show_forced_updates[] =
+N_("Fetch normally indicates which branches had a forced update,\n"
+   "but that check has been disabled. To re-enable, use '--show-forced-updates'\n"
+   "flag or run 'git config fetch.showForcedUpdates true'.");
+static const char warn_time_show_forced_updates[] =
+N_("It took %.2f seconds to check forced updates. You can use\n"
+   "'--no-show-forced-updates' or run 'git config fetch.showForcedUpdates false'\n"
+   " to avoid this check.\n");
+
 static int store_updated_refs(const char *raw_url, const char *remote_name,
                              int connectivity_checked, struct ref *ref_map)
 {
@@ -1006,12 +1020,10 @@ static int store_updated_refs(const char *raw_url, const char *remote_name,
 
        if (advice_fetch_show_forced_updates) {
                if (!fetch_show_forced_updates) {
-                       warning(_("Fetch normally indicates which branches had a forced update, but that check has been disabled."));
-                       warning(_("To re-enable, use '--show-forced-updates' flag or run 'git config fetch.showForcedUpdates true'."));
+                       warning(_(warn_show_forced_updates));
                } else if (forced_updates_ms > FORCED_UPDATES_DELAY_WARNING_IN_MS) {
-                       warning(_("It took %.2f seconds to check forced updates. You can use '--no-show-forced-updates'\n"),
+                       warning(_(warn_time_show_forced_updates),
                                forced_updates_ms / 1000.0);
-                       warning(_("or run 'git config fetch.showForcedUpdates false' to avoid this check.\n"));
                }
        }
 
@@ -1232,13 +1244,10 @@ static struct transport *prepare_transport(struct remote *remote, int deepen)
        if (update_shallow)
                set_option(transport, TRANS_OPT_UPDATE_SHALLOW, "yes");
        if (filter_options.choice) {
-               struct strbuf expanded_filter_spec = STRBUF_INIT;
-               expand_list_objects_filter_spec(&filter_options,
-                                               &expanded_filter_spec);
-               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER,
-                          expanded_filter_spec.buf);
+               const char *spec =
+                       expand_list_objects_filter_spec(&filter_options);
+               set_option(transport, TRANS_OPT_LIST_OBJECTS_FILTER, spec);
                set_option(transport, TRANS_OPT_FROM_PROMISOR, "1");
-               strbuf_release(&expanded_filter_spec);
        }
        if (negotiation_tip.nr) {
                if (transport->smart_options)
@@ -1361,6 +1370,51 @@ static int do_fetch(struct transport *transport,
                retcode = 1;
                goto cleanup;
        }
+
+       if (set_upstream) {
+               struct branch *branch = branch_get("HEAD");
+               struct ref *rm;
+               struct ref *source_ref = NULL;
+
+               /*
+                * We're setting the upstream configuration for the
+                * current branch. The relevent upstream is the
+                * fetched branch that is meant to be merged with the
+                * current one, i.e. the one fetched to FETCH_HEAD.
+                *
+                * When there are several such branches, consider the
+                * request ambiguous and err on the safe side by doing
+                * nothing and just emit a warning.
+                */
+               for (rm = ref_map; rm; rm = rm->next) {
+                       if (!rm->peer_ref) {
+                               if (source_ref) {
+                                       warning(_("multiple branch detected, incompatible with --set-upstream"));
+                                       goto skip;
+                               } else {
+                                       source_ref = rm;
+                               }
+                       }
+               }
+               if (source_ref) {
+                       if (!strcmp(source_ref->name, "HEAD") ||
+                           starts_with(source_ref->name, "refs/heads/"))
+                               install_branch_config(0,
+                                                     branch->name,
+                                                     transport->remote->name,
+                                                     source_ref->name);
+                       else if (starts_with(source_ref->name, "refs/remotes/"))
+                               warning(_("not setting upstream for a remote remote-tracking branch"));
+                       else if (starts_with(source_ref->name, "refs/tags/"))
+                               warning(_("not setting upstream for a remote tag"));
+                       else
+                               warning(_("unknown branch type"));
+               } else {
+                       warning(_("no source branch found.\n"
+                               "you need to specify exactly one branch with the --set-upstream option."));
+               }
+       }
+ skip:
        free_refs(ref_map);
 
        /* if neither --no-tags nor --tags was specified, do automated tag
@@ -1504,37 +1558,27 @@ static inline void fetch_one_setup_partial(struct remote *remote)
         * If no prior partial clone/fetch and the current fetch DID NOT
         * request a partial-fetch, do a normal fetch.
         */
-       if (!repository_format_partial_clone && !filter_options.choice)
+       if (!has_promisor_remote() && !filter_options.choice)
                return;
 
        /*
-        * If this is the FIRST partial-fetch request, we enable partial
-        * on this repo and remember the given filter-spec as the default
-        * for subsequent fetches to this remote.
+        * If this is a partial-fetch request, we enable partial on
+        * this repo if not already enabled and remember the given
+        * filter-spec as the default for subsequent fetches to this
+        * remote.
         */
-       if (!repository_format_partial_clone && filter_options.choice) {
+       if (filter_options.choice) {
                partial_clone_register(remote->name, &filter_options);
                return;
        }
 
-       /*
-        * We are currently limited to only ONE promisor remote and only
-        * allow partial-fetches from the promisor remote.
-        */
-       if (strcmp(remote->name, repository_format_partial_clone)) {
-               if (filter_options.choice)
-                       die(_("--filter can only be used with the remote "
-                             "configured in extensions.partialClone"));
-               return;
-       }
-
        /*
         * Do a partial-fetch from the promisor remote using either the
         * explicitly given filter-spec or inherit the filter-spec from
         * the config.
         */
        if (!filter_options.choice)
-               partial_clone_get_default_filter_spec(&filter_options);
+               partial_clone_get_default_filter_spec(&filter_options, remote->name);
        return;
 }
 
@@ -1655,7 +1699,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        if (depth || deepen_since || deepen_not.nr)
                deepen = 1;
 
-       if (filter_options.choice && !repository_format_partial_clone)
+       if (filter_options.choice && !has_promisor_remote())
                die("--filter can only be used when extensions.partialClone is set");
 
        if (all) {
@@ -1689,7 +1733,7 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
        }
 
        if (remote) {
-               if (filter_options.choice || repository_format_partial_clone)
+               if (filter_options.choice || has_promisor_remote())
                        fetch_one_setup_partial(remote);
                result = fetch_one(remote, argc, argv, prune_tags_ok);
        } else {
@@ -1718,12 +1762,12 @@ int cmd_fetch(int argc, const char **argv, const char *prefix)
 
        prepare_repo_settings(the_repository);
        if (the_repository->settings.fetch_write_commit_graph) {
-               int commit_graph_flags = COMMIT_GRAPH_SPLIT;
+               int commit_graph_flags = COMMIT_GRAPH_WRITE_SPLIT;
                struct split_commit_graph_opts split_opts;
                memset(&split_opts, 0, sizeof(struct split_commit_graph_opts));
 
                if (progress)
-                       commit_graph_flags |= COMMIT_GRAPH_PROGRESS;
+                       commit_graph_flags |= COMMIT_GRAPH_WRITE_PROGRESS;
 
                write_commit_graph_reachable(get_object_directory(),
                                             commit_graph_flags,