fetch: allow explicit --refmap to override configuration

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> ---

Junio C Hamano committed May 29, 2014 at 15:21 UTC c5558f80c3a34f411022f1bc67f8364556ca3922
4 files changed +81 -3
Documentation/fetch-options.txt
+8
@@ -72,6 +72,14 @@ endif::git-pull[]
72 setting. See linkgit:git-config[1].
73
74 ifndef::git-pull[]
75 +--refmap=<refspec>::
76 + When fetching refs listed on the command line, use the
77 + specified refspec (can be given more than once) to map the
78 + refs to remote-tracking branches, instead of the values of
79 + `remote.*.fetch` configuration variables for the remote
80 + repository. See section on "Configured Remote-tracking
81 + Branches" for details.
82 +
83 -t::
84 --tags::
85 Fetch all tags from the remote (i.e., fetch remote tags
Documentation/git-fetch.txt
+4
@@ -95,6 +95,10 @@ This configuration is used in two ways:
95 only used to decide _where_ the refs that are fetched are stored
96 by acting as a mapping.
97
98 +The latter use of the `remote.<repository>.fetch` values can be
99 +overridden by giving the `--refmap=<refspec>` parameter(s) on the
100 +command line.
101 +
102
103 EXAMPLES
104 --------
builtin/fetch.c
+32 -3
@@ -45,6 +45,8 @@ static struct transport *gsecondary;
45 static const char *submodule_prefix = "";
46 static const char *recurse_submodules_default;
47 static int shown_url = 0;
48 +static int refmap_alloc, refmap_nr;
49 +static const char **refmap_array;
50
51 static int option_parse_recurse_submodules(const struct option *opt,
52 const char *arg, int unset)
@@ -69,6 +71,19 @@ static int git_fetch_config(const char *k, const char *v, void *cb)
71 return 0;
72 }
73
74 +static int parse_refmap_arg(const struct option *opt, const char *arg, int unset)
75 +{
76 + ALLOC_GROW(refmap_array, refmap_nr + 1, refmap_alloc);
77 +
78 + /*
79 + * "git fetch --refmap='' origin foo"
80 + * can be used to tell the command not to store anywhere
81 + */
82 + if (*arg)
83 + refmap_array[refmap_nr++] = arg;
84 + return 0;
85 +}
86 +
87 static struct option builtin_fetch_options[] = {
88 OPT__VERBOSITY(&verbosity),
89 OPT_BOOL(0, "all", &all,
@@ -107,6 +122,8 @@ static struct option builtin_fetch_options[] = {
122 N_("default mode for recursion"), PARSE_OPT_HIDDEN },
123 OPT_BOOL(0, "update-shallow", &update_shallow,
124 N_("accept refs that update .git/shallow")),
125 + { OPTION_CALLBACK, 0, "refmap", NULL, N_("refmap"),
126 + N_("specify fetch refmap"), PARSE_OPT_NONEG, parse_refmap_arg },
127 OPT_END()
128 };
129
@@ -278,6 +295,9 @@ static struct ref *get_ref_map(struct transport *transport,
295 const struct ref *remote_refs = transport_get_remote_refs(transport);
296
297 if (refspec_count) {
298 + struct refspec *fetch_refspec;
299 + int fetch_refspec_nr;
300 +
301 for (i = 0; i < refspec_count; i++) {
302 get_fetch_map(remote_refs, &refspecs[i], &tail, 0);
303 if (refspecs[i].dst && refspecs[i].dst[0])
@@ -307,12 +327,21 @@ static struct ref *get_ref_map(struct transport *transport,
327 * by ref_remove_duplicates() in favor of one of these
328 * opportunistic entries with FETCH_HEAD_IGNORE.
329 */
310 - for (i = 0; i < transport->remote->fetch_refspec_nr; i++)
311 - get_fetch_map(ref_map, &transport->remote->fetch[i],
312 - &oref_tail, 1);
330 + if (refmap_array) {
331 + fetch_refspec = parse_fetch_refspec(refmap_nr, refmap_array);
332 + fetch_refspec_nr = refmap_nr;
333 + } else {
334 + fetch_refspec = transport->remote->fetch;
335 + fetch_refspec_nr = transport->remote->fetch_refspec_nr;
336 + }
337 +
338 + for (i = 0; i < fetch_refspec_nr; i++)
339 + get_fetch_map(ref_map, &fetch_refspec[i], &oref_tail, 1);
340
341 if (tags == TAGS_SET)
342 get_fetch_map(remote_refs, tag_refspec, &tail, 0);
343 + } else if (refmap_array) {
344 + die("--refmap option is only meaningful with command-line refspec(s).");
345 } else {
346 /* Use the defaults */
347 struct remote *remote = transport->remote;
t/t5510-fetch.sh
+37
@@ -447,6 +447,43 @@ test_expect_success 'explicit pull should update tracking' '
447 )
448 '
449
450 +test_expect_success 'explicit --refmap is allowed only with command-line refspec' '
451 + cd "$D" &&
452 + (
453 + cd three &&
454 + test_must_fail git fetch --refmap="*:refs/remotes/none/*"
455 + )
456 +'
457 +
458 +test_expect_success 'explicit --refmap option overrides remote.*.fetch' '
459 + cd "$D" &&
460 + git branch -f side &&
461 + (
462 + cd three &&
463 + git update-ref refs/remotes/origin/master base-origin-master &&
464 + o=$(git rev-parse --verify refs/remotes/origin/master) &&
465 + git fetch --refmap="refs/heads/*:refs/remotes/other/*" origin master &&
466 + n=$(git rev-parse --verify refs/remotes/origin/master) &&
467 + test "$o" = "$n" &&
468 + test_must_fail git rev-parse --verify refs/remotes/origin/side &&
469 + git rev-parse --verify refs/remotes/other/master
470 + )
471 +'
472 +
473 +test_expect_success 'explicitly empty --refmap option disables remote.*.fetch' '
474 + cd "$D" &&
475 + git branch -f side &&
476 + (
477 + cd three &&
478 + git update-ref refs/remotes/origin/master base-origin-master &&
479 + o=$(git rev-parse --verify refs/remotes/origin/master) &&
480 + git fetch --refmap="" origin master &&
481 + n=$(git rev-parse --verify refs/remotes/origin/master) &&
482 + test "$o" = "$n" &&
483 + test_must_fail git rev-parse --verify refs/remotes/origin/side
484 + )
485 +'
486 +
487 test_expect_success 'configured fetch updates tracking' '
488
489 cd "$D" &&