branch: expose helpers for finding the remote owning a tracking ref

The remote-lookup that setup_tracking() does is useful outside branch.c too; for example, deciding which remote to "git fetch" from given a remote-tracking ref. Move 'struct tracking' to branch.h and add two helpers backed by the existing for_each_remote walk: find_tracking_remote_for_ref() and advise_ambiguous_fetch_refspec(). setup_tracking() uses both. No behavior change. Signed-off-by: Harald Nordgren <haraldnordgren@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Harald Nordgren committed Jun 24, 2026 at 21:54 UTC f4914a86e11be463819175ce769c3b689bbb9994
2 files changed +68 -44
branch.c
+52 -44
@@ -20,16 +20,9 @@
20 #include "run-command.h"
21 #include "strmap.h"
22
23 -struct tracking {
24 - struct refspec_item spec;
25 - struct string_list *srcs;
26 - const char *remote;
27 - int matches;
28 -};
29 -
23 struct find_tracked_branch_cb {
24 struct tracking *tracking;
32 - struct string_list ambiguous_remotes;
25 + struct string_list *ambiguous_remotes;
26 };
27
28 static int find_tracked_branch(struct remote *remote, void *priv)
@@ -45,10 +38,10 @@ static int find_tracked_branch(struct remote *remote, void *priv)
38 break;
39 case 2:
40 /* there are at least two remotes; backfill the first one */
48 - string_list_append(&ftb->ambiguous_remotes, tracking->remote);
41 + string_list_append(ftb->ambiguous_remotes, tracking->remote);
42 /* fall through */
43 default:
51 - string_list_append(&ftb->ambiguous_remotes, remote->name);
44 + string_list_append(ftb->ambiguous_remotes, remote->name);
45 free(tracking->spec.src);
46 string_list_clear(tracking->srcs, 0);
47 break;
@@ -59,6 +52,51 @@ static int find_tracked_branch(struct remote *remote, void *priv)
52 return 0;
53 }
54
55 +void find_tracking_remote_for_ref(struct tracking *tracking,
56 + struct string_list *ambiguous_remotes)
57 +{
58 + struct find_tracked_branch_cb ftb_cb = {
59 + .tracking = tracking,
60 + .ambiguous_remotes = ambiguous_remotes,
61 + };
62 +
63 + for_each_remote(find_tracked_branch, &ftb_cb);
64 +}
65 +
66 +void advise_ambiguous_fetch_refspec(const char *dst,
67 + const struct string_list *ambiguous_remotes)
68 +{
69 + struct strbuf remotes_advice = STRBUF_INIT;
70 + struct string_list_item *item;
71 +
72 + if (!advice_enabled(ADVICE_AMBIGUOUS_FETCH_REFSPEC))
73 + return;
74 +
75 + for_each_string_list_item(item, ambiguous_remotes)
76 + /*
77 + * TRANSLATORS: This is a line listing a remote with duplicate
78 + * refspecs in the advice message below. For RTL languages you'll
79 + * probably want to swap the "%s" and leading " " space around.
80 + */
81 + strbuf_addf(&remotes_advice, _(" %s\n"), item->string);
82 +
83 + /*
84 + * TRANSLATORS: The second argument is a \n-delimited list of
85 + * duplicate refspecs, composed above.
86 + */
87 + advise(_("There are multiple remotes whose fetch refspecs map to the remote\n"
88 + "tracking ref '%s':\n"
89 + "%s"
90 + "\n"
91 + "This is typically a configuration error.\n"
92 + "\n"
93 + "To support setting up tracking branches, ensure that\n"
94 + "different remotes' fetch refspecs map into different\n"
95 + "tracking namespaces."), dst,
96 + remotes_advice.buf);
97 + strbuf_release(&remotes_advice);
98 +}
99 +
100 static int should_setup_rebase(const char *origin)
101 {
102 switch (autorebase) {
@@ -254,11 +292,8 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
292 {
293 struct tracking tracking;
294 struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
295 + struct string_list ambiguous_remotes = STRING_LIST_INIT_DUP;
296 int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
258 - struct find_tracked_branch_cb ftb_cb = {
259 - .tracking = &tracking,
260 - .ambiguous_remotes = STRING_LIST_INIT_DUP,
261 - };
297
298 if (!track)
299 BUG("asked to set up tracking, but tracking is disallowed");
@@ -267,7 +302,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
302 tracking.spec.dst = (char *)orig_ref;
303 tracking.srcs = &tracking_srcs;
304 if (track != BRANCH_TRACK_INHERIT)
270 - for_each_remote(find_tracked_branch, &ftb_cb);
305 + find_tracking_remote_for_ref(&tracking, &ambiguous_remotes);
306 else if (inherit_tracking(&tracking, orig_ref))
307 goto cleanup;
308
@@ -293,34 +328,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
328 if (tracking.matches > 1) {
329 int status = die_message(_("not tracking: ambiguous information for ref '%s'"),
330 orig_ref);
296 - if (advice_enabled(ADVICE_AMBIGUOUS_FETCH_REFSPEC)) {
297 - struct strbuf remotes_advice = STRBUF_INIT;
298 - struct string_list_item *item;
299 -
300 - for_each_string_list_item(item, &ftb_cb.ambiguous_remotes)
301 - /*
302 - * TRANSLATORS: This is a line listing a remote with duplicate
303 - * refspecs in the advice message below. For RTL languages you'll
304 - * probably want to swap the "%s" and leading " " space around.
305 - */
306 - strbuf_addf(&remotes_advice, _(" %s\n"), item->string);
307 -
308 - /*
309 - * TRANSLATORS: The second argument is a \n-delimited list of
310 - * duplicate refspecs, composed above.
311 - */
312 - advise(_("There are multiple remotes whose fetch refspecs map to the remote\n"
313 - "tracking ref '%s':\n"
314 - "%s"
315 - "\n"
316 - "This is typically a configuration error.\n"
317 - "\n"
318 - "To support setting up tracking branches, ensure that\n"
319 - "different remotes' fetch refspecs map into different\n"
320 - "tracking namespaces."), orig_ref,
321 - remotes_advice.buf);
322 - strbuf_release(&remotes_advice);
323 - }
331 + advise_ambiguous_fetch_refspec(orig_ref, &ambiguous_remotes);
332 exit(status);
333 }
334
@@ -347,7 +355,7 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
355
356 cleanup:
357 string_list_clear(&tracking_srcs, 0);
350 - string_list_clear(&ftb_cb.ambiguous_remotes, 0);
358 + string_list_clear(&ambiguous_remotes, 0);
359 }
360
361 int read_branch_desc(struct strbuf *buf, const char *branch_name)
branch.h
+16
@@ -1,9 +1,25 @@
1 #ifndef BRANCH_H
2 #define BRANCH_H
3
4 +#include "refspec.h"
5 +
6 +struct string_list;
7 struct repository;
8 struct strbuf;
9
10 +struct tracking {
11 + struct refspec_item spec;
12 + struct string_list *srcs;
13 + const char *remote;
14 + int matches;
15 +};
16 +
17 +void find_tracking_remote_for_ref(struct tracking *tracking,
18 + struct string_list *ambiguous_remotes);
19 +
20 +void advise_ambiguous_fetch_refspec(const char *dst,
21 + const struct string_list *ambiguous_remotes);
22 +
23 enum branch_track {
24 BRANCH_TRACK_UNSPECIFIED = -1,
25 BRANCH_TRACK_NEVER = 0,