Skip to content

Commit 09e0be1

Browse files
committed
Merge branch 'js/branch-track-inherit' into gc/branch-recurse-submodules
* js/branch-track-inherit: branch,checkout: fix --track documentation branch,checkout: fix --track usage strings config: require lowercase for branch.*.autosetupmerge branch: add flags and config to inherit tracking branch: accept multiple upstream branches for tracking
2 parents abe6bb3 + 6327f0e commit 09e0be1

16 files changed

+312
-67
lines changed

Documentation/config/branch.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ branch.autoSetupMerge::
77
automatic setup is done; `true` -- automatic setup is done when the
88
starting point is a remote-tracking branch; `always` --
99
automatic setup is done when the starting point is either a
10-
local branch or remote-tracking
10+
local branch or remote-tracking branch; `inherit` -- if the starting point
11+
has a tracking configuration, it is copied to the new
1112
branch. This option defaults to true.
1213

1314
branch.autoSetupRebase::

Documentation/git-branch.txt

Lines changed: 17 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ SYNOPSIS
1616
[--points-at <object>] [--format=<format>]
1717
[(-r | --remotes) | (-a | --all)]
1818
[--list] [<pattern>...]
19-
'git branch' [--track | --no-track] [-f] <branchname> [<start-point>]
19+
'git branch' [--track[=(direct|inherit)] | --no-track] [-f] <branchname> [<start-point>]
2020
'git branch' (--set-upstream-to=<upstream> | -u <upstream>) [<branchname>]
2121
'git branch' --unset-upstream [<branchname>]
2222
'git branch' (-m | -M) [<oldbranch>] <newbranch>
@@ -206,24 +206,34 @@ This option is only applicable in non-verbose mode.
206206
Display the full sha1s in the output listing rather than abbreviating them.
207207

208208
-t::
209-
--track::
209+
--track[=(direct|inherit)]::
210210
When creating a new branch, set up `branch.<name>.remote` and
211-
`branch.<name>.merge` configuration entries to mark the
212-
start-point branch as "upstream" from the new branch. This
211+
`branch.<name>.merge` configuration entries to set "upstream" tracking
212+
configuration for the new branch. This
213213
configuration will tell git to show the relationship between the
214214
two branches in `git status` and `git branch -v`. Furthermore,
215215
it directs `git pull` without arguments to pull from the
216216
upstream when the new branch is checked out.
217217
+
218-
This behavior is the default when the start point is a remote-tracking branch.
218+
The exact upstream branch is chosen depending on the optional argument:
219+
`-t`, `--track`, or `--track=direct` means to use the start-point branch
220+
itself as the upstream; `--track=inherit` means to copy the upstream
221+
configuration of the start-point branch.
222+
+
223+
`--track=direct` is the default when the start point is a remote-tracking branch.
219224
Set the branch.autoSetupMerge configuration variable to `false` if you
220225
want `git switch`, `git checkout` and `git branch` to always behave as if `--no-track`
221226
were given. Set it to `always` if you want this behavior when the
222-
start-point is either a local or remote-tracking branch.
227+
start-point is either a local or remote-tracking branch. Set it to
228+
`inherit` if you want to copy the tracking configuration from the
229+
branch point.
230+
+
231+
See linkgit:git-pull[1] and linkgit:git-config[1] for additional discussion on
232+
how the `branch.<name>.remote` and `branch.<name>.merge` options are used.
223233

224234
--no-track::
225235
Do not set up "upstream" configuration, even if the
226-
branch.autoSetupMerge configuration variable is true.
236+
branch.autoSetupMerge configuration variable is set.
227237

228238
--set-upstream::
229239
As this option had confusing syntax, it is no longer supported.

Documentation/git-checkout.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ of it").
156156
linkgit:git-branch[1] for details.
157157

158158
-t::
159-
--track::
159+
--track[=(direct|inherit)]::
160160
When creating a new branch, set up "upstream" configuration. See
161161
"--track" in linkgit:git-branch[1] for details.
162162
+

Documentation/git-switch.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,7 +152,7 @@ should result in deletion of the path).
152152
attached to a terminal, regardless of `--quiet`.
153153

154154
-t::
155-
--track::
155+
--track [direct|inherit]::
156156
When creating a new branch, set up "upstream" configuration.
157157
`-c` is implied. See `--track` in linkgit:git-branch[1] for
158158
details.

branch.c

Lines changed: 145 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
struct tracking {
1313
struct refspec_item spec;
14-
char *src;
14+
struct string_list *srcs;
1515
const char *remote;
1616
int matches;
1717
};
@@ -22,11 +22,11 @@ static int find_tracked_branch(struct remote *remote, void *priv)
2222

2323
if (!remote_find_tracking(remote, &tracking->spec)) {
2424
if (++tracking->matches == 1) {
25-
tracking->src = tracking->spec.src;
25+
string_list_append(tracking->srcs, tracking->spec.src);
2626
tracking->remote = remote->name;
2727
} else {
2828
free(tracking->spec.src);
29-
FREE_AND_NULL(tracking->src);
29+
string_list_clear(tracking->srcs, 0);
3030
}
3131
tracking->spec.src = NULL;
3232
}
@@ -49,34 +49,64 @@ static int should_setup_rebase(const char *origin)
4949
return 0;
5050
}
5151

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)
5968
{
6069
const char *shortname = NULL;
6170
struct strbuf key = STRBUF_INIT;
71+
struct string_list_item *item;
6272
int rebasing = should_setup_rebase(origin);
6373

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+
}
7192

7293
strbuf_addf(&key, "branch.%s.remote", local);
7394
if (git_config_set_gently(key.buf, origin ? origin : ".") < 0)
7495
goto out_err;
7596

7697
strbuf_reset(&key);
7798
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)
79106
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;
80110

81111
if (rebasing) {
82112
strbuf_reset(&key);
@@ -87,29 +117,40 @@ int install_branch_config(int flag, const char *local, const char *origin, const
87117
strbuf_release(&key);
88118

89119
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);
101147
} 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);
112151
}
152+
153+
string_list_clear(&friendly_ref_names, 0);
113154
}
114155

115156
return 0;
@@ -118,14 +159,64 @@ int install_branch_config(int flag, const char *local, const char *origin, const
118159
strbuf_release(&key);
119160
error(_("Unable to write upstream branch configuration"));
120161

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+
}
125176

126177
return -1;
127178
}
128179

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+
static int inherit_tracking(struct tracking *tracking, const char *orig_ref)
193+
{
194+
const char *bare_ref;
195+
struct branch *branch;
196+
int i;
197+
198+
bare_ref = orig_ref;
199+
skip_prefix(orig_ref, "refs/heads/", &bare_ref);
200+
201+
branch = branch_get(bare_ref);
202+
if (!branch->remote_name) {
203+
warning(_("asked to inherit tracking from '%s', but no remote is set"),
204+
bare_ref);
205+
return -1;
206+
}
207+
208+
if (branch->merge_nr < 1 || !branch->merge_name || !branch->merge_name[0]) {
209+
warning(_("asked to inherit tracking from '%s', but no merge configuration is set"),
210+
bare_ref);
211+
return -1;
212+
}
213+
214+
tracking->remote = xstrdup(branch->remote_name);
215+
for (i = 0; i < branch->merge_nr; i++)
216+
string_list_append(tracking->srcs, branch->merge_name[i]);
217+
return 0;
218+
}
219+
129220
/*
130221
* This is called when new_ref is branched off of orig_ref, and tries
131222
* to infer the settings for branch.<new_ref>.{remote,merge} from the
@@ -135,18 +226,23 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
135226
enum branch_track track, int quiet)
136227
{
137228
struct tracking tracking;
229+
struct string_list tracking_srcs = STRING_LIST_INIT_DUP;
138230
int config_flags = quiet ? 0 : BRANCH_CONFIG_VERBOSE;
139231

140232
memset(&tracking, 0, sizeof(tracking));
141233
tracking.spec.dst = (char *)orig_ref;
142-
if (for_each_remote(find_tracked_branch, &tracking))
234+
tracking.srcs = &tracking_srcs;
235+
if (track != BRANCH_TRACK_INHERIT)
236+
for_each_remote(find_tracked_branch, &tracking);
237+
else if (inherit_tracking(&tracking, orig_ref))
143238
return;
144239

145240
if (!tracking.matches)
146241
switch (track) {
147242
case BRANCH_TRACK_ALWAYS:
148243
case BRANCH_TRACK_EXPLICIT:
149244
case BRANCH_TRACK_OVERRIDE:
245+
case BRANCH_TRACK_INHERIT:
150246
break;
151247
default:
152248
return;
@@ -156,11 +252,13 @@ static void setup_tracking(const char *new_ref, const char *orig_ref,
156252
die(_("Not tracking: ambiguous information for ref %s"),
157253
orig_ref);
158254

159-
if (install_branch_config(config_flags, new_ref, tracking.remote,
160-
tracking.src ? tracking.src : orig_ref) < 0)
255+
if (tracking.srcs->nr < 1)
256+
string_list_append(tracking.srcs, orig_ref);
257+
if (install_branch_config_multiple_remotes(config_flags, new_ref,
258+
tracking.remote, tracking.srcs) < 0)
161259
exit(-1);
162260

163-
free(tracking.src);
261+
string_list_clear(tracking.srcs, 0);
164262
}
165263

166264
int read_branch_desc(struct strbuf *buf, const char *branch_name)

branch.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ enum branch_track {
1010
BRANCH_TRACK_REMOTE,
1111
BRANCH_TRACK_ALWAYS,
1212
BRANCH_TRACK_EXPLICIT,
13-
BRANCH_TRACK_OVERRIDE
13+
BRANCH_TRACK_OVERRIDE,
14+
BRANCH_TRACK_INHERIT,
1415
};
1516

1617
extern enum branch_track git_branch_track;

builtin/branch.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -633,8 +633,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
633633
OPT__VERBOSE(&filter.verbose,
634634
N_("show hash and subject, give twice for upstream branch")),
635635
OPT__QUIET(&quiet, N_("suppress informational messages")),
636-
OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"),
637-
BRANCH_TRACK_EXPLICIT),
636+
OPT_CALLBACK_F('t', "track", &track, "(direct|inherit)",
637+
N_("set branch tracking configuration"),
638+
PARSE_OPT_OPTARG,
639+
parse_opt_tracking_mode),
638640
OPT_SET_INT_F(0, "set-upstream", &track, N_("do not use"),
639641
BRANCH_TRACK_OVERRIDE, PARSE_OPT_HIDDEN),
640642
OPT_STRING('u', "set-upstream-to", &new_upstream, N_("upstream"), N_("change the upstream info")),

builtin/checkout.c

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1530,8 +1530,10 @@ static struct option *add_common_switch_branch_options(
15301530
{
15311531
struct option options[] = {
15321532
OPT_BOOL('d', "detach", &opts->force_detach, N_("detach HEAD at named commit")),
1533-
OPT_SET_INT('t', "track", &opts->track, N_("set upstream info for new branch"),
1534-
BRANCH_TRACK_EXPLICIT),
1533+
OPT_CALLBACK_F('t', "track", &opts->track, "(direct|inherit)",
1534+
N_("set branch tracking configuration"),
1535+
PARSE_OPT_OPTARG,
1536+
parse_opt_tracking_mode),
15351537
OPT__FORCE(&opts->force, N_("force checkout (throw away local modifications)"),
15361538
PARSE_OPT_NOCOMPLETE),
15371539
OPT_STRING(0, "orphan", &opts->new_orphan_branch, N_("new-branch"), N_("new unparented branch")),

0 commit comments

Comments
 (0)