branch: accept multiple upstream branches for tracking
Add a new static variant of install_branch_config() that accepts multiple remote branch names for tracking. This will be used in an upcoming commit that enables inheriting the tracking configuration from a parent branch. Currently, all callers of install_branch_config() pass only a single remote. Make install_branch_config() a small wrapper around install_branch_config_multiple_remotes() so that existing callers do not need to be changed. Signed-off-by: Josh Steadmon <steadmon@google.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
Josh Steadmon committed
Dec 20, 2021 at 19:30 UTC
a3f40ec4b0dc56a54e6a6866d0a44a04d4a6edf0
2 files changed
+106
-43
branch.c
+103
-40
@@ -49,25 +49,46 @@ static int should_setup_rebase(const char *origin)
49
return 0;
50
}
51
52
-static const char tracking_advice[] =
53
-N_("\n"
54
-"After fixing the error cause you may try to fix up\n"
55
-"the remote tracking information by invoking\n"
56
-"\"git branch --set-upstream-to=%s%s%s\".");
57
-
58
-int install_branch_config(int flag, const char *local, const char *origin, const char *remote)
52
+/**
53
+ * Install upstream tracking configuration for a branch; specifically, add
54
+ * `branch.<name>.remote` and `branch.<name>.merge` entries.
55
+ *
56
+ * `flag` contains integer flags for options; currently only
57
+ * BRANCH_CONFIG_VERBOSE is checked.
58
+ *
59
+ * `local` is the name of the branch whose configuration we're installing.
60
+ *
61
+ * `origin` is the name of the remote owning the upstream branches. NULL means
62
+ * the upstream branches are local to this repo.
63
+ *
64
+ * `remotes` is a list of refs that are upstream of local
65
+ */
66
+static int install_branch_config_multiple_remotes(int flag, const char *local,
67
+ const char *origin, struct string_list *remotes)
68
{
69
const char *shortname = NULL;
70
struct strbuf key = STRBUF_INIT;
71
+ struct string_list_item *item;
72
int rebasing = should_setup_rebase(origin);
73
64
- if (skip_prefix(remote, "refs/heads/", &shortname)
65
- && !strcmp(local, shortname)
66
- && !origin) {
67
- warning(_("Not setting branch %s as its own upstream."),
68
- local);
69
- return 0;
70
- }
74
+ if (!remotes->nr)
75
+ BUG("must provide at least one remote for branch config");
76
+ if (rebasing && remotes->nr > 1)
77
+ die(_("cannot inherit upstream tracking configuration of "
78
+ "multiple refs when rebasing is requested"));
79
+
80
+ /*
81
+ * If the new branch is trying to track itself, something has gone
82
+ * wrong. Warn the user and don't proceed any further.
83
+ */
84
+ if (!origin)
85
+ for_each_string_list_item(item, remotes)
86
+ if (skip_prefix(item->string, "refs/heads/", &shortname)
87
+ && !strcmp(local, shortname)) {
88
+ warning(_("not setting branch '%s' as its own upstream."),
89
+ local);
90
+ return 0;
91
+ }
92
93
strbuf_addf(&key, "branch.%s.remote", local);
94
if (git_config_set_gently(key.buf, origin ? origin : ".") < 0)
@@ -75,8 +96,17 @@ int install_branch_config(int flag, const char *local, const char *origin, const
96
97
strbuf_reset(&key);
98
strbuf_addf(&key, "branch.%s.merge", local);
78
- if (git_config_set_gently(key.buf, remote) < 0)
99
+ /*
100
+ * We want to overwrite any existing config with all the branches in
101
+ * "remotes". Override any existing config, then write our branches. If
102
+ * more than one is provided, use CONFIG_REGEX_NONE to preserve what
103
+ * we've written so far.
104
+ */
105
+ if (git_config_set_gently(key.buf, NULL) < 0)
106
goto out_err;
107
+ for_each_string_list_item(item, remotes)
108
+ if (git_config_set_multivar_gently(key.buf, item->string, CONFIG_REGEX_NONE, 0) < 0)
109
+ goto out_err;
110
111
if (rebasing) {
112
strbuf_reset(&key);
@@ -87,29 +117,40 @@ int install_branch_config(int flag, const char *local, const char *origin, const
117
strbuf_release(&key);
118
119
if (flag & BRANCH_CONFIG_VERBOSE) {
90
- if (shortname) {
91
- if (origin)
92
- printf_ln(rebasing ?
93
- _("Branch '%s' set up to track remote branch '%s' from '%s' by rebasing.") :
94
- _("Branch '%s' set up to track remote branch '%s' from '%s'."),
95
- local, shortname, origin);
96
- else
97
- printf_ln(rebasing ?
98
- _("Branch '%s' set up to track local branch '%s' by rebasing.") :
99
- _("Branch '%s' set up to track local branch '%s'."),
100
- local, shortname);
120
+ struct strbuf tmp_ref_name = STRBUF_INIT;
121
+ struct string_list friendly_ref_names = STRING_LIST_INIT_DUP;
122
+
123
+ for_each_string_list_item(item, remotes) {
124
+ shortname = item->string;
125
+ skip_prefix(shortname, "refs/heads/", &shortname);
126
+ if (origin) {
127
+ strbuf_addf(&tmp_ref_name, "%s/%s",
128
+ origin, shortname);
129
+ string_list_append_nodup(
130
+ &friendly_ref_names,
131
+ strbuf_detach(&tmp_ref_name, NULL));
132
+ } else {
133
+ string_list_append(
134
+ &friendly_ref_names, shortname);
135
+ }
136
+ }
137
+
138
+ if (remotes->nr == 1) {
139
+ /*
140
+ * Rebasing is only allowed in the case of a single
141
+ * upstream branch.
142
+ */
143
+ printf_ln(rebasing ?
144
+ _("branch '%s' set up to track '%s' by rebasing.") :
145
+ _("branch '%s' set up to track '%s'."),
146
+ local, friendly_ref_names.items[0].string);
147
} else {
102
- if (origin)
103
- printf_ln(rebasing ?
104
- _("Branch '%s' set up to track remote ref '%s' by rebasing.") :
105
- _("Branch '%s' set up to track remote ref '%s'."),
106
- local, remote);
107
- else
108
- printf_ln(rebasing ?
109
- _("Branch '%s' set up to track local ref '%s' by rebasing.") :
110
- _("Branch '%s' set up to track local ref '%s'."),
111
- local, remote);
148
+ printf_ln(_("branch '%s' set up to track:"), local);
149
+ for_each_string_list_item(item, &friendly_ref_names)
150
+ printf_ln(" %s", item->string);
151
}
152
+
153
+ string_list_clear(&friendly_ref_names, 0);
154
}
155
156
return 0;
@@ -118,14 +159,36 @@ out_err:
159
strbuf_release(&key);
160
error(_("Unable to write upstream branch configuration"));
161
121
- advise(_(tracking_advice),
122
- origin ? origin : "",
123
- origin ? "/" : "",
124
- shortname ? shortname : remote);
162
+ advise(_("\nAfter fixing the error cause you may try to fix up\n"
163
+ "the remote tracking information by invoking:"));
164
+ if (remotes->nr == 1)
165
+ advise(" git branch --set-upstream-to=%s%s%s",
166
+ origin ? origin : "",
167
+ origin ? "/" : "",
168
+ remotes->items[0].string);
169
+ else {
170
+ advise(" git config --add branch.\"%s\".remote %s",
171
+ local, origin ? origin : ".");
172
+ for_each_string_list_item(item, remotes)
173
+ advise(" git config --add branch.\"%s\".merge %s",
174
+ local, item->string);
175
+ }
176
177
return -1;
178
}
179
180
+int install_branch_config(int flag, const char *local, const char *origin,
181
+ const char *remote)
182
+{
183
+ int ret;
184
+ struct string_list remotes = STRING_LIST_INIT_DUP;
185
+
186
+ string_list_append(&remotes, remote);
187
+ ret = install_branch_config_multiple_remotes(flag, local, origin, &remotes);
188
+ string_list_clear(&remotes, 0);
189
+ return ret;
190
+}
191
+
192
/*
193
* This is called when new_ref is branched off of orig_ref, and tries
194
* to infer the settings for branch.<new_ref>.{remote,merge} from the
t/t3200-branch.sh
+3
-3
@@ -950,15 +950,15 @@ test_expect_success 'disabled option --set-upstream fails' '
950
test_must_fail git branch --set-upstream origin/main
951
'
952
953
-test_expect_success '--set-upstream-to notices an error to set branch as own upstream' '
953
+test_expect_success '--set-upstream-to notices an error to set branch as own upstream' "
954
git branch --set-upstream-to refs/heads/my13 my13 2>actual &&
955
cat >expect <<-\EOF &&
956
- warning: Not setting branch my13 as its own upstream.
956
+ warning: not setting branch 'my13' as its own upstream.
957
EOF
958
test_expect_code 1 git config branch.my13.remote &&
959
test_expect_code 1 git config branch.my13.merge &&
960
test_cmp expect actual
961
-'
961
+"
962
963
# Keep this test last, as it changes the current branch
964
cat >expect <<EOF