tracking branches: add advice to ambiguous refspec error
The error "not tracking: ambiguous information for ref" is raised when we are evaluating what tracking information to set on a branch, and find that the ref to be added as tracking branch is mapped under multiple remotes' fetch refspecs. This can easily happen when a user copy-pastes a remote definition in their git config, and forgets to change the tracking path. Add advice in this situation, explicitly highlighting which remotes are involved and suggesting how to correct the situation. Also update a test to explicitly expect that advice. Signed-off-by: Tao Klerks <tao@klerks.biz> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Tao Klerks committed
Apr 1, 2022 at 06:05 UTC
e4921d877ab3487fbc0bde8b3e59b757d274783c
5 files changed
+78
-9
Documentation/config/advice.txt
+4
@@ -4,6 +4,10 @@ advice.*::
4
can tell Git that you do not need help by setting these to 'false':
5
+
6
--
7
+ ambiguousFetchRefspec::
8
+ Advice shown when fetch refspec for multiple remotes map to
9
+ the same remote-tracking branch namespace and causes branch
10
+ tracking set-up to fail.
11
fetchShowForcedUpdates::
12
Advice shown when linkgit:git-fetch[1] takes a long time
13
to calculate forced updates after ref updates, or to warn
advice.c
+1
@@ -39,6 +39,7 @@ static struct {
39
[ADVICE_ADD_EMPTY_PATHSPEC] = { "addEmptyPathspec", 1 },
40
[ADVICE_ADD_IGNORED_FILE] = { "addIgnoredFile", 1 },
41
[ADVICE_AM_WORK_DIR] = { "amWorkDir", 1 },
42
+ [ADVICE_AMBIGUOUS_FETCH_REFSPEC] = { "ambiguousFetchRefspec", 1 },
43
[ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME] = { "checkoutAmbiguousRemoteBranchName", 1 },
44
[ADVICE_COMMIT_BEFORE_MERGE] = { "commitBeforeMerge", 1 },
45
[ADVICE_DETACHED_HEAD] = { "detachedHead", 1 },
advice.h
+1
@@ -17,6 +17,7 @@ struct string_list;
17
ADVICE_ADD_EMPTY_PATHSPEC,
18
ADVICE_ADD_IGNORED_FILE,
19
ADVICE_AM_WORK_DIR,
20
+ ADVICE_AMBIGUOUS_FETCH_REFSPEC,
21
ADVICE_CHECKOUT_AMBIGUOUS_REMOTE_BRANCH_NAME,
22
ADVICE_COMMIT_BEFORE_MERGE,
23
ADVICE_DETACHED_HEAD,
branch.c
+56
-7
@@ -18,17 +18,31 @@ struct tracking {
18
int matches;
19
};
20
21
+struct find_tracked_branch_cb {
22
+ struct tracking *tracking;
23
+ struct string_list ambiguous_remotes;
24
+};
25
+
26
static int find_tracked_branch(struct remote *remote, void *priv)
27
{
23
- struct tracking *tracking = priv;
28
+ struct find_tracked_branch_cb *ftb = priv;
29
+ struct tracking *tracking = ftb->tracking;
30
31
if (!remote_find_tracking(remote, &tracking->spec)) {
26
- if (++tracking->matches == 1) {
32
+ switch (++tracking->matches) {
33
+ case 1:
34
string_list_append(tracking->srcs, tracking->spec.src);
35
tracking->remote = remote->name;
29
- } else {
36
+ break;
37
+ case 2:
38
+ /* there are at least two remotes; backfill the first one */
39
+ string_list_append(&ftb->ambiguous_remotes, tracking->remote);
40
+ /* fall through */
41
+ default:
42
+ string_list_append(&ftb->ambiguous_remotes, remote->name);
43
free(tracking->spec.src);
44
string_list_clear(tracking->srcs, 0);
45
+ break;
46
}
47
tracking->spec.src = NULL;
48
}
@@ -232,12 +246,16 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
246
struct tracking tracking;
247
struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
248
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
249
+ struct find_tracked_branch_cb ftb_cb = {
250
+ .tracking = &tracking,
251
+ .ambiguous_remotes = STRING_LIST_INIT_DUP,
252
+ };
253
254
memset(&tracking, 0, sizeof(tracking));
255
tracking.spec.dst = (char *)orig_ref;
256
tracking.srcs = &tracking_srcs;
257
if (track != BRANCH_TRACK_INHERIT)
240
- for_each_remote(find_tracked_branch, &tracking);
258
+ for_each_remote(find_tracked_branch, &ftb_cb);
259
else if (inherit_tracking(&tracking, orig_ref))
260
goto cleanup;
261
@@ -252,9 +270,39 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
270
goto cleanup;
271
}
272
255
- if (tracking.matches > 1)
256
- die(_("not tracking: ambiguous information for ref %s"),
257
- orig_ref);
273
+ if (tracking.matches > 1) {
274
+ int status = die_message(_("not tracking: ambiguous information for ref '%s'"),
275
+ orig_ref);
276
+ if (advice_enabled(ADVICE_AMBIGUOUS_FETCH_REFSPEC)) {
277
+ struct strbuf remotes_advice = STRBUF_INIT;
278
+ struct string_list_item *item;
279
+
280
+ for_each_string_list_item(item, &ftb_cb.ambiguous_remotes)
281
+ /*
282
+ * TRANSLATORS: This is a line listing a remote with duplicate
283
+ * refspecs in the advice message below. For RTL languages you'll
284
+ * probably want to swap the "%s" and leading " " space around.
285
+ */
286
+ strbuf_addf(&remotes_advice, _(" %s\n"), item->string);
287
+
288
+ /*
289
+ * TRANSLATORS: The second argument is a \n-delimited list of
290
+ * duplicate refspecs, composed above.
291
+ */
292
+ advise(_("There are multiple remotes whose fetch refspecs map to the remote\n"
293
+ "tracking ref '%s':\n"
294
+ "%s"
295
+ "\n"
296
+ "This is typically a configuration error.\n"
297
+ "\n"
298
+ "To support setting up tracking branches, ensure that\n"
299
+ "different remotes' fetch refspecs map into different\n"
300
+ "tracking namespaces."), orig_ref,
301
+ remotes_advice.buf);
302
+ strbuf_release(&remotes_advice);
303
+ }
304
+ exit(status);
305
+ }
306
307
if (tracking.srcs->nr < 1)
308
string_list_append(tracking.srcs, orig_ref);
@@ -264,6 +312,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
312
313
cleanup:
314
string_list_clear(&tracking_srcs, 0);
315
+ string_list_clear(&ftb_cb.ambiguous_remotes, 0);
316
}
317
318
int read_branch_desc(struct strbuf *buf, const char *branch_name)
t/t3200-branch.sh
+16
-2
@@ -1039,13 +1039,27 @@ test_expect_success 'checkout -b with -l makes reflog when core.logAllRefUpdates
1039
git rev-parse --verify gamma@{0}
1040
'
1041
1042
-test_expect_success 'avoid ambiguous track' '
1042
+test_expect_success 'avoid ambiguous track and advise' '
1043
git config branch.autosetupmerge true &&
1044
git config remote.ambi1.url lalala &&
1045
git config remote.ambi1.fetch refs/heads/lalala:refs/heads/main &&
1046
git config remote.ambi2.url lilili &&
1047
git config remote.ambi2.fetch refs/heads/lilili:refs/heads/main &&
1048
- test_must_fail git branch all1 main &&
1048
+ cat <<-EOF >expected &&
1049
+ fatal: not tracking: ambiguous information for ref '\''refs/heads/main'\''
1050
+ hint: There are multiple remotes whose fetch refspecs map to the remote
1051
+ hint: tracking ref '\''refs/heads/main'\'':
1052
+ hint: ambi1
1053
+ hint: ambi2
1054
+ hint: ''
1055
+ hint: This is typically a configuration error.
1056
+ hint: ''
1057
+ hint: To support setting up tracking branches, ensure that
1058
+ hint: different remotes'\'' fetch refspecs map into different
1059
+ hint: tracking namespaces.
1060
+ EOF
1061
+ test_must_fail git branch all1 main 2>actual &&
1062
+ test_cmp expected actual &&
1063
test -z "$(git config branch.all1.merge)"
1064
'
1065