@matheusbsbs / matheus-bagliano / commits / 9ed36cfa35
Fork 0

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 <jaysoffian@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jay Soffian committed Feb 19, 2008 at 11:24 UTC 9ed36cfa35cfbd09c454f12194a91cd50ba284d1
9 files changed +98 -39
branch.c
+24 -20
@@ -37,7 +37,8 @@ static int find_tracked_branch(struct remote *remote, void *priv)
37 * to infer the settings for branch.<new_ref>.{remote,merge} from the
38 * config.
39 */
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)
42 {
43 char key[1024];
44 struct tracking tracking;
@@ -48,30 +49,36 @@ static int setup_tracking(const char *new_ref, const char *orig_ref)
49
50 memset(&tracking, 0, sizeof(tracking));
51 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))
53 return 1;
54
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 +
64 if (tracking.matches > 1)
65 return error("Not tracking: ambiguous information for ref %s",
66 orig_ref);
67
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);
75
76 return 0;
77 }
78
79 void create_branch(const char *head,
80 const char *name, const char *start_name,
74 - int force, int reflog, int track)
81 + int force, int reflog, enum branch_track track)
82 {
83 struct ref_lock *lock;
84 struct commit *commit;
@@ -98,7 +105,8 @@ void create_branch(const char *head,
105 switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
106 case 0:
107 /* 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.");
110 break;
111 case 1:
112 /* Unique completion -- good */
@@ -126,17 +134,13 @@ void create_branch(const char *head,
134 snprintf(msg, sizeof msg, "branch: Created from %s",
135 start_name);
136
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. */
137 if (real_ref && track)
133 - setup_tracking(name, real_ref);
138 + setup_tracking(name, real_ref, track);
139
140 if (write_ref_sha1(lock, sha1, msg) < 0)
141 die("Failed to write ref: %s.", strerror(errno));
142
138 - if (real_ref)
139 - free(real_ref);
143 + free(real_ref);
144 }
145
146 void remove_branch_state(void)
branch.h
+1 -1
@@ -13,7 +13,7 @@
13 * branch for (if any).
14 */
15 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);
17
18 /*
19 * Remove information about the state of working on the current
builtin-branch.c
+5 -8
@@ -30,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
30 static const char *head;
31 static unsigned char head_sha1[20];
32
33 -static int branch_track = 1;
34 -
33 static int branch_use_color;
34 static char branch_colors[][COLOR_MAXLEN] = {
35 "\033[m", /* reset */
@@ -74,9 +72,6 @@ static int git_branch_config(const char *var, const char *value)
72 color_parse(value, var, branch_colors[slot]);
73 return 0;
74 }
77 - if (!strcmp(var, "branch.autosetupmerge"))
78 - branch_track = git_config_bool(var, value);
79 -
75 return git_default_config(var, value);
76 }
77
@@ -417,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
412 {
413 int delete = 0, rename = 0, force_create = 0;
414 int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
420 - int reflog = 0, track;
415 + int reflog = 0;
416 + enum branch_track track;
417 int kinds = REF_LOCAL_BRANCH;
418 struct commit_list *with_commit = NULL;
419
420 struct option options[] = {
421 OPT_GROUP("Generic options"),
422 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),
425 OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
426 OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
427 REF_REMOTE_BRANCH),
@@ -451,7 +448,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
448 };
449
450 git_config(git_branch_config);
454 - track = branch_track;
451 + track = git_branch_track;
452 argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
453 if (!!delete + !!rename + !!force_create > 1)
454 usage_with_options(builtin_branch_usage, options);
builtin-checkout.c
+5 -9
@@ -186,7 +186,7 @@ struct checkout_opts {
186
187 char *new_branch;
188 int new_branch_log;
189 - int track;
189 + enum branch_track track;
190 };
191
192 struct branch_info {
@@ -457,13 +457,8 @@ static int switch_branches(struct checkout_opts *opts,
457 return post_checkout_hook(old.commit, new->commit, 1);
458 }
459
460 -static int branch_track = 0;
461 -
460 static int git_checkout_config(const char *var, const char *value)
461 {
464 - if (!strcmp(var, "branch.autosetupmerge"))
465 - branch_track = git_config_bool(var, value);
466 -
462 return git_default_config(var, value);
463 }
464
@@ -478,7 +473,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
473 OPT__QUIET(&opts.quiet),
474 OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
475 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),
478 OPT_BOOLEAN('f', NULL, &opts.force, "force"),
479 OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
480 OPT_END(),
@@ -489,7 +485,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
485
486 git_config(git_checkout_config);
487
492 - opts.track = branch_track;
488 + opts.track = git_branch_track;
489
490 argc = parse_options(argc, argv, options, checkout_usage, 0);
491 if (argc) {
@@ -518,7 +514,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
514 argc--;
515 }
516
521 - if (!opts.new_branch && (opts.track != branch_track))
517 + if (!opts.new_branch && (opts.track != git_branch_track))
518 die("git checkout: --track and --no-track require -b");
519
520 if (opts.force && opts.merge)
cache.h
+9
@@ -373,6 +373,15 @@ extern size_t packed_git_limit;
373 extern size_t delta_base_cache_limit;
374 extern int auto_crlf;
375
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 +
385 #define GIT_REPO_VERSION 0
386 extern int repository_format_version;
387 extern int check_repository_format(void);
config.c
+8
@@ -454,6 +454,14 @@ int git_default_config(const char *var, const char *value)
454 whitespace_rule_cfg = parse_whitespace_rule(value);
455 return 0;
456 }
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 + }
465
466 /* Add other config variables here and to Documentation/config.txt. */
467 return 0;
environment.c
+1
@@ -36,6 +36,7 @@ char *editor_program;
36 char *excludes_file;
37 int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
38 unsigned whitespace_rule_cfg = WS_DEFAULT_RULE;
39 +enum branch_track git_branch_track = BRANCH_TRACK_REMOTE;
40
41 /* This is set by setup_git_dir_gently() and/or git_default_config() */
42 char *git_work_tree_cfg;
t/t3200-branch.sh
+21 -1
@@ -15,6 +15,9 @@ test_expect_success \
15 'echo Hello > A &&
16 git update-index --add A &&
17 git-commit -m "Initial commit." &&
18 + echo World >> A &&
19 + git update-index --add A &&
20 + git-commit -m "Second commit." &&
21 HEAD=$(git rev-parse --verify HEAD)'
22
23 test_expect_failure \
@@ -169,7 +172,9 @@ test_expect_success 'test overriding tracking setup via --no-track' \
172 ! test "$(git config branch.my2.merge)" = refs/heads/master'
173
174 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 &&
178 test -z "$(git config branch.my6.remote)" &&
179 test -z "$(git config branch.my6.merge)"'
180
@@ -190,6 +195,21 @@ test_expect_success 'test deleting branch without config' \
195 'git branch my7 s &&
196 test "$(git branch -d my7 2>&1)" = "Deleted branch my7."'
197
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 +
213 # Keep this test last, as it changes the current branch
214 cat >expect <<EOF
215 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
t/t7201-co.sh
+24
@@ -263,4 +263,28 @@ test_expect_success 'checkout with ambiguous tag/branch names' '
263
264 '
265
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 +
290 test_done