fetch: allow explicit --refmap to override configuration
authorJunio C Hamano <gitster@pobox.com>
Thu, 29 May 2014 22:21:31 +0000 (15:21 -0700)
committerJunio C Hamano <gitster@pobox.com>
Thu, 5 Jun 2014 22:13:12 +0000 (15:13 -0700)
Since the introduction of opportunisitic updates of remote-tracking
branches, started at around f2690487 (fetch: opportunistically
update tracking refs, 2013-05-11) with a few updates in v1.8.4 era,
the remote.*.fetch configuration always kicks in even when a refspec
to specify what to fetch is given on the command line, and there is
no way to disable or override it per-invocation.

Teach the command to pay attention to the --refmap=<lhs>:<rhs>
command-line options that can be used to override the use of
configured remote.*.fetch as the refmap.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
---

Documentation/fetch-options.txt
Documentation/git-fetch.txt
builtin/fetch.c
t/t5510-fetch.sh
index 92c68c3fdabafd9ac5afe74256dbd2d56218c247..b09a783ee313bf67cb07db03bb3127d252ab70c3 100644 (file)
@@ -72,6 +72,14 @@ endif::git-pull[]
        setting. See linkgit:git-config[1].
 
 ifndef::git-pull[]
+--refmap=<refspec>::
+       When fetching refs listed on the command line, use the
+       specified refspec (can be given more than once) to map the
+       refs to remote-tracking branches, instead of the values of
+       `remote.*.fetch` configuration variables for the remote
+       repository.  See section on "Configured Remote-tracking
+       Branches" for details.
+
 -t::
 --tags::
        Fetch all tags from the remote (i.e., fetch remote tags
index 7f818c348b35b342e1892196bac0dc22f268f5d6..629a1cfe004efe52854390351999ecd84b12ce29 100644 (file)
@@ -95,6 +95,10 @@ This configuration is used in two ways:
   only used to decide _where_ the refs that are fetched are stored
   by acting as a mapping.
 
+The latter use of the `remote.<repository>.fetch` values can be
+overridden by giving the `--refmap=<refspec>` parameter(s) on the
+command line.
+
 
 EXAMPLES
 --------
index 55f457c04f5c72ca31ab4fbc28e62ea48b6cb471..dd46b61d9a31475e3d5289e41e6d7f57a0264b6f 100644 (file)
@@ -45,6 +45,8 @@ static struct transport *gsecondary;
 static const char *submodule_prefix = "";
 static const char *recurse_submodules_default;
 static int shown_url = 0;
+static int refmap_alloc, refmap_nr;
+static const char **refmap_array;
 
 static int option_parse_recurse_submodules(const struct option *opt,
                                   const char *arg, int unset)
@@ -69,6 +71,19 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
        return 0;
 }
 
+static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
+{
+       ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
+
+       /*
+        * "git fetch --refmap='' origin foo"
+        * can be used to tell the command not to store anywhere
+        */
+       if (*arg)
+               refmap_array[refmap_nr++] = arg;
+       return 0;
+}
+
 static struct option builtin_fetch_options[] = {
        OPT__VERBOSITY(&verbosity),
        OPT_BOOL(0, "all", &all,
@@ -107,6 +122,8 @@ static struct option builtin_fetch_options[] = {
                   N_("default mode for recursion"), PARSE_OPT_HIDDEN },
        OPT_BOOL(0, "update-shallow", &update_shallow,
                 N_("accept refs that update .git/shallow")),
+       { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
+         N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
        OPT_END()
 };
 
@@ -278,6 +295,9 @@ static struct ref *get_ref_map(struct transport *transport,
        const struct ref *remote_refs = transport_get_remote_refs(transport);
 
        if (refspec_count) {
+               struct refspec *fetch_refspec;
+               int fetch_refspec_nr;
+
                for (i = 0; i < refspec_count; i++) {
                        get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
                        if (refspecs[i].dst && refspecs[i].dst[0])
@@ -307,12 +327,21 @@ static struct ref *get_ref_map(struct transport *transport,
                 * by ref_remove_duplicates() in favor of one of these
                 * opportunistic entries with FETCH_HEAD_IGNORE.
                 */
-               for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
-                       get_fetch_map(ref_map, &transport->remote->fetch[i],
-                                     &oref_tail, 1);
+               if (refmap_array) {
+                       fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
+                       fetch_refspec_nr = refmap_nr;
+               } else {
+                       fetch_refspec = transport->remote->fetch;
+                       fetch_refspec_nr = transport->remote->fetch_refspec_nr;
+               }
+
+               for (i = 0; i < fetch_refspec_nr; i++)
+                       get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
 
                if (tags == TAGS_SET)
                        get_fetch_map(remote_refs, tag_refspec, &tail, 0);
+       } else if (refmap_array) {
+               die("--refmap option is only meaningful with command-line refspec(s).");
        } else {
                /* Use the defaults */
                struct remote *remote = transport->remote;
index 29d59ef9fa2bf2bffb0075f07e03103387ab718e..d78f3201f423504bf5d53029492cc1bc056aab06 100755 (executable)
@@ -447,6 +447,43 @@ test_expect_success 'explicit pull should update tracking' '
        )
 '
 
+test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
+       cd "$D" &&
+       (
+               cd three &&
+               test_must_fail git fetch --refmap="*:refs/remotes/none/*"
+       )
+'
+
+test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
+       cd "$D" &&
+       git branch -f side &&
+       (
+               cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
+               o=$(git rev-parse --verify refs/remotes/origin/master) &&
+               git fetch --refmap="refs/heads/*:refs/remotes/other/*" origin master &&
+               n=$(git rev-parse --verify refs/remotes/origin/master) &&
+               test "$o" = "$n" &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side &&
+               git rev-parse --verify refs/remotes/other/master
+       )
+'
+
+test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
+       cd "$D" &&
+       git branch -f side &&
+       (
+               cd three &&
+               git update-ref refs/remotes/origin/master base-origin-master &&
+               o=$(git rev-parse --verify refs/remotes/origin/master) &&
+               git fetch --refmap="" origin master &&
+               n=$(git rev-parse --verify refs/remotes/origin/master) &&
+               test "$o" = "$n" &&
+               test_must_fail git rev-parse --verify refs/remotes/origin/side
+       )
+'
+
 test_expect_success 'configured fetch updates tracking' '
 
        cd "$D" &&