From 9ed36cfa35cfbd09c454f12194a91cd50ba284d1 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Tue, 19 Feb 2008 11:24:37 -0500 Subject: [PATCH 1/2] 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 Signed-off-by: Junio C Hamano --- branch.c | 44 ++++++++++++++++++++++++-------------------- branch.h | 2 +- builtin-branch.c | 13 +++++-------- builtin-checkout.c | 14 +++++--------- cache.h | 9 +++++++++ config.c | 8 ++++++++ environment.c | 1 + t/t3200-branch.sh | 22 +++++++++++++++++++++- t/t7201-co.sh | 24 ++++++++++++++++++++++++ 9 files changed, 98 insertions(+), 39 deletions(-) diff --git a/branch.c b/branch.c index 1fc8788897..daf862e728 100644 --- a/branch.c +++ b/branch.c @@ -37,7 +37,8 @@ static int find_tracked_branch(struct remote *remote, void *priv) * to infer the settings for branch..{remote,merge} from the * config. */ -static int setup_tracking(const char *new_ref, const char *orig_ref) +static int setup_tracking(const char *new_ref, const char *orig_ref, + enum branch_track track) { char key[1024]; struct tracking tracking; @@ -48,30 +49,36 @@ static int setup_tracking(const char *new_ref, const char *orig_ref) memset(&tracking, 0, sizeof(tracking)); tracking.spec.dst = (char *)orig_ref; - if (for_each_remote(find_tracked_branch, &tracking) || - !tracking.matches) + if (for_each_remote(find_tracked_branch, &tracking)) return 1; + if (!tracking.matches) + switch (track) { + case BRANCH_TRACK_ALWAYS: + case BRANCH_TRACK_EXPLICIT: + break; + default: + return 1; + } + if (tracking.matches > 1) return error("Not tracking: ambiguous information for ref %s", orig_ref); - if (tracking.matches == 1) { - sprintf(key, "branch.%s.remote", new_ref); - git_config_set(key, tracking.remote ? tracking.remote : "."); - sprintf(key, "branch.%s.merge", new_ref); - git_config_set(key, tracking.src); - free(tracking.src); - printf("Branch %s set up to track remote branch %s.\n", - new_ref, orig_ref); - } + sprintf(key, "branch.%s.remote", new_ref); + git_config_set(key, tracking.remote ? tracking.remote : "."); + sprintf(key, "branch.%s.merge", new_ref); + git_config_set(key, tracking.src ? tracking.src : orig_ref); + free(tracking.src); + printf("Branch %s set up to track %s branch %s.\n", new_ref, + tracking.remote ? "remote" : "local", orig_ref); return 0; } void create_branch(const char *head, const char *name, const char *start_name, - int force, int reflog, int track) + int force, int reflog, enum branch_track track) { struct ref_lock *lock; struct commit *commit; @@ -98,7 +105,8 @@ void create_branch(const char *head, switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) { case 0: /* Not branching from any existing branch */ - real_ref = NULL; + if (track == BRANCH_TRACK_EXPLICIT) + die("Cannot setup tracking information; starting point is not a branch."); break; case 1: /* Unique completion -- good */ @@ -126,17 +134,13 @@ void create_branch(const char *head, snprintf(msg, sizeof msg, "branch: Created from %s", start_name); - /* When branching off a remote branch, set up so that git-pull - automatically merges from there. So far, this is only done for - remotes registered via .git/config. */ if (real_ref && track) - setup_tracking(name, real_ref); + setup_tracking(name, real_ref, track); if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); - if (real_ref) - free(real_ref); + free(real_ref); } void remove_branch_state(void) diff --git a/branch.h b/branch.h index d30abe0369..9f0c2a2c1f 100644 --- a/branch.h +++ b/branch.h @@ -13,7 +13,7 @@ * branch for (if any). */ void create_branch(const char *head, const char *name, const char *start_name, - int force, int reflog, int track); + int force, int reflog, enum branch_track track); /* * Remove information about the state of working on the current diff --git a/builtin-branch.c b/builtin-branch.c index 1e0c9dea3f..32eaf0d1e4 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -30,8 +30,6 @@ static const char * const builtin_branch_usage[] = { static const char *head; static unsigned char head_sha1[20]; -static int branch_track = 1; - static int branch_use_color; static char branch_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ @@ -74,9 +72,6 @@ static int git_branch_config(const char *var, const char *value) color_parse(value, var, branch_colors[slot]); return 0; } - if (!strcmp(var, "branch.autosetupmerge")) - branch_track = git_config_bool(var, value); - return git_default_config(var, value); } @@ -417,14 +412,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix) { int delete = 0, rename = 0, force_create = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; - int reflog = 0, track; + int reflog = 0; + enum branch_track track; int kinds = REF_LOCAL_BRANCH; struct commit_list *with_commit = NULL; struct option options[] = { OPT_GROUP("Generic options"), OPT__VERBOSE(&verbose), - OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_SET_INT( 0 , "track", &track, "set up tracking mode (see git-pull(1))", + BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", REF_REMOTE_BRANCH), @@ -451,7 +448,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) }; git_config(git_branch_config); - track = branch_track; + track = git_branch_track; argc = parse_options(argc, argv, options, builtin_branch_usage, 0); if (!!delete + !!rename + !!force_create > 1) usage_with_options(builtin_branch_usage, options); diff --git a/builtin-checkout.c b/builtin-checkout.c index 5291f72a37..b1820a4462 100644 --- a/builtin-checkout.c +++ b/builtin-checkout.c @@ -186,7 +186,7 @@ struct checkout_opts { char *new_branch; int new_branch_log; - int track; + enum branch_track track; }; struct branch_info { @@ -457,13 +457,8 @@ static int switch_branches(struct checkout_opts *opts, return post_checkout_hook(old.commit, new->commit, 1); } -static int branch_track = 0; - static int git_checkout_config(const char *var, const char *value) { - if (!strcmp(var, "branch.autosetupmerge")) - branch_track = git_config_bool(var, value); - return git_default_config(var, value); } @@ -478,7 +473,8 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) OPT__QUIET(&opts.quiet), OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new branch"), - OPT_BOOLEAN( 0 , "track", &opts.track, "track"), + OPT_SET_INT( 0 , "track", &opts.track, "track", + BRANCH_TRACK_EXPLICIT), OPT_BOOLEAN('f', NULL, &opts.force, "force"), OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_END(), @@ -489,7 +485,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) git_config(git_checkout_config); - opts.track = branch_track; + opts.track = git_branch_track; argc = parse_options(argc, argv, options, checkout_usage, 0); if (argc) { @@ -518,7 +514,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix) argc--; } - if (!opts.new_branch && (opts.track != branch_track)) + if (!opts.new_branch && (opts.track != git_branch_track)) die("git checkout: --track and --no-track require -b"); if (opts.force && opts.merge) diff --git a/cache.h b/cache.h index 888895a969..0cd1368aef 100644 --- a/cache.h +++ b/cache.h @@ -373,6 +373,15 @@ extern size_t packed_git_limit; extern size_t delta_base_cache_limit; extern int auto_crlf; +enum branch_track { + BRANCH_TRACK_NEVER = 0, + BRANCH_TRACK_REMOTE, + BRANCH_TRACK_ALWAYS, + BRANCH_TRACK_EXPLICIT, +}; + +extern enum branch_track git_branch_track; + #define GIT_REPO_VERSION 0 extern int repository_format_version; extern int check_repository_format(void); diff --git a/config.c b/config.c index 526a3f4294..97d75051a4 100644 --- a/config.c +++ b/config.c @@ -454,6 +454,14 @@ int git_default_config(const char *var, const char *value) whitespace_rule_cfg = parse_whitespace_rule(value); return 0; } + if (!strcmp(var, "branch.autosetupmerge")) { + if (value && !strcasecmp(value, "always")) { + git_branch_track = BRANCH_TRACK_ALWAYS; + return 0; + } + git_branch_track = git_config_bool(var, value); + return 0; + } /* Add other config variables here and to Documentation/config.txt. */ return 0; diff --git a/environment.c b/environment.c index 18a1c4eec4..1f74b4b557 100644 --- a/environment.c +++ b/environment.c @@ -36,6 +36,7 @@ char *editor_program; char *excludes_file; int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; +enum branch_track git_branch_track = BRANCH_TRACK_REMOTE; /* This is set by setup_git_dir_gently() and/or git_default_config() */ char *git_work_tree_cfg; diff --git a/t/t3200-branch.sh b/t/t3200-branch.sh index ef1eeb7d8a..900d814627 100755 --- a/t/t3200-branch.sh +++ b/t/t3200-branch.sh @@ -15,6 +15,9 @@ test_expect_success \ 'echo Hello > A && git update-index --add A && git-commit -m "Initial commit." && + echo World >> A && + git update-index --add A && + git-commit -m "Second commit." && HEAD=$(git rev-parse --verify HEAD)' test_expect_failure \ @@ -169,7 +172,9 @@ test_expect_success 'test overriding tracking setup via --no-track' \ ! test "$(git config branch.my2.merge)" = refs/heads/master' test_expect_success 'no tracking without .fetch entries' \ - 'git branch --track my6 s && + 'git config branch.autosetupmerge true && + git branch my6 s && + git config branch.automsetupmerge false && test -z "$(git config branch.my6.remote)" && test -z "$(git config branch.my6.merge)"' @@ -190,6 +195,21 @@ test_expect_success 'test deleting branch without config' \ 'git branch my7 s && test "$(git branch -d my7 2>&1)" = "Deleted branch my7."' +test_expect_success 'test --track without .fetch entries' \ + 'git branch --track my8 && + test "$(git config branch.my8.remote)" && + test "$(git config branch.my8.merge)"' + +test_expect_success \ + 'branch from non-branch HEAD w/autosetupmerge=always' \ + 'git config branch.autosetupmerge always && + git branch my9 HEAD^ && + git config branch.autosetupmerge false' + +test_expect_success \ + 'branch from non-branch HEAD w/--track causes failure' \ + '!(git branch --track my10 HEAD^)' + # Keep this test last, as it changes the current branch cat >expect < 1117150200 +0000 branch: Created from master diff --git a/t/t7201-co.sh b/t/t7201-co.sh index 5492f21c7e..17cff8d515 100755 --- a/t/t7201-co.sh +++ b/t/t7201-co.sh @@ -263,4 +263,28 @@ test_expect_success 'checkout with ambiguous tag/branch names' ' ' +test_expect_success \ + 'checkout w/--track sets up tracking' ' + git config branch.autosetupmerge false && + git checkout master && + git checkout --track -b track1 && + test "$(git config branch.track1.remote)" && + test "$(git config branch.track1.merge)"' + +test_expect_success \ + 'checkout w/autosetupmerge=always sets up tracking' ' + git config branch.autosetupmerge always && + git checkout master && + git checkout -b track2 && + test "$(git config branch.track2.remote)" && + test "$(git config branch.track2.merge)" + git config branch.autosetupmerge false' + +test_expect_success \ + 'checkout w/--track from non-branch HEAD fails' ' + git checkout -b delete-me master && + rm .git/refs/heads/delete-me && + test refs/heads/delete-me = "$(git symbolic-ref HEAD)" && + !(git checkout --track -b track)' + test_done From 572fc81d21b26d856dd3c2cc366b7452a4ed16e4 Mon Sep 17 00:00:00 2001 From: Jay Soffian Date: Tue, 19 Feb 2008 11:24:38 -0500 Subject: [PATCH 2/2] doc: documentation update for the branch track changes Documents the branch.autosetupmerge=always setting and usage of --track when branching from a local branch. Signed-off-by: Jay Soffian Signed-off-by: Junio C Hamano --- Documentation/config.txt | 10 +++++++--- Documentation/git-branch.txt | 33 ++++++++++++++++----------------- Documentation/git-checkout.txt | 23 +++++++++++------------ 3 files changed, 34 insertions(+), 32 deletions(-) diff --git a/Documentation/config.txt b/Documentation/config.txt index 877eda960d..f5994bd4a7 100644 --- a/Documentation/config.txt +++ b/Documentation/config.txt @@ -330,10 +330,14 @@ apply.whitespace:: branch.autosetupmerge:: Tells `git-branch` and `git-checkout` to setup new branches - so that linkgit:git-pull[1] will appropriately merge from that - remote branch. Note that even if this option is not set, + so that linkgit:git-pull[1] will appropriately merge from the + starting point branch. Note that even if this option is not set, this behavior can be chosen per-branch using the `--track` - and `--no-track` options. This option defaults to false. + and `--no-track` options. The valid settings are: `false` -- no + automatic setup is done; `true` -- automatic setup is done when the + starting point is a remote branch; `always` -- automatic setup is + done when the starting point is either a local branch or remote + branch. This option defaults to true. branch..remote:: When in branch , it tells `git fetch` which remote to fetch. diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index f920c04cc0..6f07a17a2c 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -34,12 +34,11 @@ Note that this will create the new branch, but it will not switch the working tree to it; use "git checkout " to switch to the new branch. -When a local branch is started off a remote branch, git can setup the -branch so that linkgit:git-pull[1] will appropriately merge from that -remote branch. If this behavior is desired, it is possible to make it -the default using the global `branch.autosetupmerge` configuration -flag. Otherwise, it can be chosen per-branch using the `--track` -and `--no-track` options. +When a local branch is started off a remote branch, git sets up the +branch so that linkgit:git-pull[1] will appropriately merge from +the remote branch. This behavior may be changed via the global +`branch.autosetupmerge` configuration flag. That setting can be +overridden by using the `--track` and `--no-track` options. With a '-m' or '-M' option, will be renamed to . If had a corresponding reflog, it is renamed to match @@ -105,19 +104,19 @@ OPTIONS Display the full sha1s in output listing rather than abbreviating them. --track:: - Set up configuration so that git-pull will automatically - retrieve data from the remote branch. Use this if you always - pull from the same remote branch into the new branch, or if you - don't want to use "git pull " explicitly. Set the - branch.autosetupmerge configuration variable to true if you - want git-checkout and git-branch to always behave as if - '--track' were given. + When creating a new branch, set up configuration so that git-pull + will automatically retrieve data from the start point, which must be + a branch. Use this if you always pull from the same upstream branch + into the new branch, and if you don't want to use "git pull + " explicitly. This behavior is the default + when the start point is a remote branch. Set the + branch.autosetupmerge configuration variable to `false` if you want + git-checkout and git-branch to always behave as if '--no-track' were + given. Set it to `always` if you want this behavior when the + start-point is either a local or remote branch. --no-track:: - When a branch is created off a remote branch, - set up configuration so that git-pull will not retrieve data - from the remote branch, ignoring the branch.autosetupmerge - configuration variable. + Ignore the branch.autosetupmerge configuration variable. :: The name of the branch to create or delete. diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt index 584359ff3f..4014e7256d 100644 --- a/Documentation/git-checkout.txt +++ b/Documentation/git-checkout.txt @@ -48,20 +48,19 @@ OPTIONS may restrict the characters allowed in a branch name. --track:: - When -b is given and a branch is created off a remote branch, - set up configuration so that git-pull will automatically - retrieve data from the remote branch. Use this if you always - pull from the same remote branch into the new branch, or if you - don't want to use "git pull " explicitly. Set the - branch.autosetupmerge configuration variable to true if you - want git-checkout and git-branch to always behave as if - '--track' were given. + When creating a new branch, set up configuration so that git-pull + will automatically retrieve data from the start point, which must be + a branch. Use this if you always pull from the same upstream branch + into the new branch, and if you don't want to use "git pull + " explicitly. This behavior is the default + when the start point is a remote branch. Set the + branch.autosetupmerge configuration variable to `false` if you want + git-checkout and git-branch to always behave as if '--no-track' were + given. Set it to `always` if you want this behavior when the + start-point is either a local or remote branch. --no-track:: - When -b is given and a branch is created off a remote branch, - set up configuration so that git-pull will not retrieve data - from the remote branch, ignoring the branch.autosetupmerge - configuration variable. + Ignore the branch.autosetupmerge configuration variable. -l:: Create the new branch's reflog. This activates recording of