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