Skip to content

Commit 9ed36cf

Browse files
jaysoffiangitster
authored andcommitted
branch: optionally setup branch.*.merge from upstream local branches
"git branch" and "git checkout -b" now honor --track option even when the upstream branch is local. Previously --track was silently ignored when forking from a local branch. Also the command did not error out when --track was explicitly asked for but the forked point specified was not an existing branch (i.e. when there is no way to set up the tracking configuration), but now it correctly does. The configuration setting branch.autosetupmerge can now be set to "always", which is equivalent to using --track from the command line. Setting branch.autosetupmerge to "true" will retain the former behavior of only setting up branch.*.merge for remote upstream branches. Includes test cases for the new functionality. Signed-off-by: Jay Soffian <[email protected]> Signed-off-by: Junio C Hamano <[email protected]>
1 parent 569012b commit 9ed36cf

File tree

9 files changed

+98
-39
lines changed

9 files changed

+98
-39
lines changed

branch.c

Lines changed: 24 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,8 @@ static int find_tracked_branch(struct remote *remote, void *priv)
3737
* to infer the settings for branch.<new_ref>.{remote,merge} from the
3838
* config.
3939
*/
40-
static int setup_tracking(const char *new_ref, const char *orig_ref)
40+
static int setup_tracking(const char *new_ref, const char *orig_ref,
41+
enum branch_track track)
4142
{
4243
char key[1024];
4344
struct tracking tracking;
@@ -48,30 +49,36 @@ static int setup_tracking(const char *new_ref, const char *orig_ref)
4849

4950
memset(&tracking, 0, sizeof(tracking));
5051
tracking.spec.dst = (char *)orig_ref;
51-
if (for_each_remote(find_tracked_branch, &tracking) ||
52-
!tracking.matches)
52+
if (for_each_remote(find_tracked_branch, &tracking))
5353
return 1;
5454

55+
if (!tracking.matches)
56+
switch (track) {
57+
case BRANCH_TRACK_ALWAYS:
58+
case BRANCH_TRACK_EXPLICIT:
59+
break;
60+
default:
61+
return 1;
62+
}
63+
5564
if (tracking.matches > 1)
5665
return error("Not tracking: ambiguous information for ref %s",
5766
orig_ref);
5867

59-
if (tracking.matches == 1) {
60-
sprintf(key, "branch.%s.remote", new_ref);
61-
git_config_set(key, tracking.remote ? tracking.remote : ".");
62-
sprintf(key, "branch.%s.merge", new_ref);
63-
git_config_set(key, tracking.src);
64-
free(tracking.src);
65-
printf("Branch %s set up to track remote branch %s.\n",
66-
new_ref, orig_ref);
67-
}
68+
sprintf(key, "branch.%s.remote", new_ref);
69+
git_config_set(key, tracking.remote ? tracking.remote : ".");
70+
sprintf(key, "branch.%s.merge", new_ref);
71+
git_config_set(key, tracking.src ? tracking.src : orig_ref);
72+
free(tracking.src);
73+
printf("Branch %s set up to track %s branch %s.\n", new_ref,
74+
tracking.remote ? "remote" : "local", orig_ref);
6875

6976
return 0;
7077
}
7178

7279
void create_branch(const char *head,
7380
const char *name, const char *start_name,
74-
int force, int reflog, int track)
81+
int force, int reflog, enum branch_track track)
7582
{
7683
struct ref_lock *lock;
7784
struct commit *commit;
@@ -98,7 +105,8 @@ void create_branch(const char *head,
98105
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
99106
case 0:
100107
/* Not branching from any existing branch */
101-
real_ref = NULL;
108+
if (track == BRANCH_TRACK_EXPLICIT)
109+
die("Cannot setup tracking information; starting point is not a branch.");
102110
break;
103111
case 1:
104112
/* Unique completion -- good */
@@ -126,17 +134,13 @@ void create_branch(const char *head,
126134
snprintf(msg, sizeof msg, "branch: Created from %s",
127135
start_name);
128136

129-
/* When branching off a remote branch, set up so that git-pull
130-
automatically merges from there. So far, this is only done for
131-
remotes registered via .git/config. */
132137
if (real_ref && track)
133-
setup_tracking(name, real_ref);
138+
setup_tracking(name, real_ref, track);
134139

135140
if (write_ref_sha1(lock, sha1, msg) < 0)
136141
die("Failed to write ref: %s.", strerror(errno));
137142

138-
if (real_ref)
139-
free(real_ref);
143+
free(real_ref);
140144
}
141145

142146
void remove_branch_state(void)

branch.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313
* branch for (if any).
1414
*/
1515
void create_branch(const char *head, const char *name, const char *start_name,
16-
int force, int reflog, int track);
16+
int force, int reflog, enum branch_track track);
1717

1818
/*
1919
* Remove information about the state of working on the current

builtin-branch.c

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
3030
static const char *head;
3131
static unsigned char head_sha1[20];
3232

33-
static int branch_track = 1;
34-
3533
static int branch_use_color;
3634
static char branch_colors[][COLOR_MAXLEN] = {
3735
"\033[m", /* reset */
@@ -74,9 +72,6 @@ static int git_branch_config(const char *var, const char *value)
7472
color_parse(value, var, branch_colors[slot]);
7573
return 0;
7674
}
77-
if (!strcmp(var, "branch.autosetupmerge"))
78-
branch_track = git_config_bool(var, value);
79-
8075
return git_default_config(var, value);
8176
}
8277

@@ -417,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
417412
{
418413
int delete = 0, rename = 0, force_create = 0;
419414
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
420-
int reflog = 0, track;
415+
int reflog = 0;
416+
enum branch_track track;
421417
int kinds = REF_LOCAL_BRANCH;
422418
struct commit_list *with_commit = NULL;
423419

424420
struct option options[] = {
425421
OPT_GROUP("Generic options"),
426422
OPT__VERBOSE(&verbose),
427-
OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
423+
OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))",
424+
BRANCH_TRACK_EXPLICIT),
428425
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
429426
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
430427
REF_REMOTE_BRANCH),
@@ -451,7 +448,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
451448
};
452449

453450
git_config(git_branch_config);
454-
track = branch_track;
451+
track = git_branch_track;
455452
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
456453
if (!!delete + !!rename + !!force_create > 1)
457454
usage_with_options(builtin_branch_usage, options);

builtin-checkout.c

Lines changed: 5 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -186,7 +186,7 @@ struct checkout_opts {
186186

187187
char *new_branch;
188188
int new_branch_log;
189-
int track;
189+
enum branch_track track;
190190
};
191191

192192
struct branch_info {
@@ -457,13 +457,8 @@ static int switch_branches(struct checkout_opts *opts,
457457
return post_checkout_hook(old.commit, new->commit, 1);
458458
}
459459

460-
static int branch_track = 0;
461-
462460
static int git_checkout_config(const char *var, const char *value)
463461
{
464-
if (!strcmp(var, "branch.autosetupmerge"))
465-
branch_track = git_config_bool(var, value);
466-
467462
return git_default_config(var, value);
468463
}
469464

@@ -478,7 +473,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
478473
OPT__QUIET(&opts.quiet),
479474
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
480475
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"),
481-
OPT_BOOLEAN( 0 , "track", &opts.track, "track"),
476+
OPT_SET_INT( 0 , "track", &opts.track, "track",
477+
BRANCH_TRACK_EXPLICIT),
482478
OPT_BOOLEAN('f', NULL, &opts.force, "force"),
483479
OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
484480
OPT_END(),
@@ -489,7 +485,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
489485

490486
git_config(git_checkout_config);
491487

492-
opts.track = branch_track;
488+
opts.track = git_branch_track;
493489

494490
argc = parse_options(argc, argv, options, checkout_usage, 0);
495491
if (argc) {
@@ -518,7 +514,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
518514
argc--;
519515
}
520516

521-
if (!opts.new_branch && (opts.track != branch_track))
517+
if (!opts.new_branch && (opts.track != git_branch_track))
522518
die("git checkout: --track and --no-track require -b");
523519

524520
if (opts.force && opts.merge)

cache.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -373,6 +373,15 @@ extern size_t packed_git_limit;
373373
extern size_t delta_base_cache_limit;
374374
extern int auto_crlf;
375375

376+
enum branch_track {
377+
BRANCH_TRACK_NEVER = 0,
378+
BRANCH_TRACK_REMOTE,
379+
BRANCH_TRACK_ALWAYS,
380+
BRANCH_TRACK_EXPLICIT,
381+
};
382+
383+
extern enum branch_track git_branch_track;
384+
376385
#define GIT_REPO_VERSION 0
377386
extern int repository_format_version;
378387
extern int check_repository_format(void);

config.c

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -454,6 +454,14 @@ int git_default_config(const char *var, const char *value)
454454
whitespace_rule_cfg = parse_whitespace_rule(value);
455455
return 0;
456456
}
457+
if (!strcmp(var, "branch.autosetupmerge")) {
458+
if (value && !strcasecmp(value, "always")) {
459+
git_branch_track = BRANCH_TRACK_ALWAYS;
460+
return 0;
461+
}
462+
git_branch_track = git_config_bool(var, value);
463+
return 0;
464+
}
457465

458466
/* Add other config variables here and to Documentation/config.txt. */
459467
return 0;

environment.c

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ char *editor_program;
3636
char *excludes_file;
3737
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
3838
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
39+
enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
3940

4041
/* This is set by setup_git_dir_gently() and/or git_default_config() */
4142
char *git_work_tree_cfg;

t/t3200-branch.sh

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,9 @@ test_expect_success \
1515
'echo Hello > A &&
1616
git update-index --add A &&
1717
git-commit -m "Initial commit." &&
18+
echo World >> A &&
19+
git update-index --add A &&
20+
git-commit -m "Second commit." &&
1821
HEAD=$(git rev-parse --verify HEAD)'
1922

2023
test_expect_failure \
@@ -169,7 +172,9 @@ test_expect_success 'test overriding tracking setup via --no-track' \
169172
! test "$(git config branch.my2.merge)" = refs/heads/master'
170173

171174
test_expect_success 'no tracking without .fetch entries' \
172-
'git branch --track my6 s &&
175+
'git config branch.autosetupmerge true &&
176+
git branch my6 s &&
177+
git config branch.automsetupmerge false &&
173178
test -z "$(git config branch.my6.remote)" &&
174179
test -z "$(git config branch.my6.merge)"'
175180

@@ -190,6 +195,21 @@ test_expect_success 'test deleting branch without config' \
190195
'git branch my7 s &&
191196
test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
192197

198+
test_expect_success 'test --track without .fetch entries' \
199+
'git branch --track my8 &&
200+
test "$(git config branch.my8.remote)" &&
201+
test "$(git config branch.my8.merge)"'
202+
203+
test_expect_success \
204+
'branch from non-branch HEAD w/autosetupmerge=always' \
205+
'git config branch.autosetupmerge always &&
206+
git branch my9 HEAD^ &&
207+
git config branch.autosetupmerge false'
208+
209+
test_expect_success \
210+
'branch from non-branch HEAD w/--track causes failure' \
211+
'!(git branch --track my10 HEAD^)'
212+
193213
# Keep this test last, as it changes the current branch
194214
cat >expect <<EOF
195215
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master

t/t7201-co.sh

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,4 +263,28 @@ test_expect_success 'checkout with ambiguous tag/branch names' '
263263
264264
'
265265

266+
test_expect_success \
267+
'checkout w/--track sets up tracking' '
268+
git config branch.autosetupmerge false &&
269+
git checkout master &&
270+
git checkout --track -b track1 &&
271+
test "$(git config branch.track1.remote)" &&
272+
test "$(git config branch.track1.merge)"'
273+
274+
test_expect_success \
275+
'checkout w/autosetupmerge=always sets up tracking' '
276+
git config branch.autosetupmerge always &&
277+
git checkout master &&
278+
git checkout -b track2 &&
279+
test "$(git config branch.track2.remote)" &&
280+
test "$(git config branch.track2.merge)"
281+
git config branch.autosetupmerge false'
282+
283+
test_expect_success \
284+
'checkout w/--track from non-branch HEAD fails' '
285+
git checkout -b delete-me master &&
286+
rm .git/refs/heads/delete-me &&
287+
test refs/heads/delete-me = "$(git symbolic-ref HEAD)" &&
288+
!(git checkout --track -b track)'
289+
266290
test_done

0 commit comments

Comments
 (0)