aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Documentation/config.txt10
-rw-r--r--Documentation/git-branch.txt32
-rw-r--r--Documentation/git-checkout.txt24
-rw-r--r--branch.c44
-rw-r--r--branch.h2
-rw-r--r--builtin-branch.c14
-rw-r--r--builtin-checkout.c14
-rw-r--r--cache.h9
-rw-r--r--config.c8
-rw-r--r--environment.c1
-rwxr-xr-xt/t3200-branch.sh22
-rwxr-xr-xt/t7201-co.sh24
12 files changed, 131 insertions, 73 deletions
diff --git a/Documentation/config.txt b/Documentation/config.txt
index 8e361a1e7..4027726f2 100644
--- a/Documentation/config.txt
+++ b/Documentation/config.txt
@@ -379,10 +379,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 true.
+ 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.<name>.remote::
When in branch <name>, it tells `git fetch` which remote to fetch.
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index 7e8874aca..6f07a17a2 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -35,11 +35,10 @@ working tree to it; use "git checkout <newbranch>" to switch to the
new branch.
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 that
-remote branch. If this behavior is not desired, it is possible to
-disable it using the global `branch.autosetupmerge` configuration
-flag. That setting can be overridden by using the `--track`
-and `--no-track` options.
+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, <oldbranch> will be renamed to <newbranch>.
If <oldbranch> had a corresponding reflog, it is renamed to match
@@ -105,20 +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 <repository> <refspec>" explicitly.
- This behavior is the default. 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.
+ 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
+ <repository> <refspec>" 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.
<branchname>::
The name of the branch to create or delete.
diff --git a/Documentation/git-checkout.txt b/Documentation/git-checkout.txt
index b4cfa044b..4014e7256 100644
--- a/Documentation/git-checkout.txt
+++ b/Documentation/git-checkout.txt
@@ -48,21 +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 <repository> <refspec>" explicitly.
- This behavior is the default. 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.
+ 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
+ <repository> <refspec>" 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
diff --git a/branch.c b/branch.c
index 1fc878889..daf862e72 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.<new_ref>.{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 d30abe036..9f0c2a2c1 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 7e991030c..a7844454a 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 = -1;
static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */
@@ -76,10 +74,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 0;
- }
return git_color_default_config(var, value);
}
@@ -420,14 +414,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),
@@ -458,7 +454,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (branch_use_color == -1)
branch_use_color = git_use_color_default;
- 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 d5f093094..4a4bb8b77 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 {
@@ -480,13 +480,8 @@ static int switch_branches(struct checkout_opts *opts, struct branch_info *new)
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);
}
@@ -501,7 +496,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(),
@@ -512,7 +508,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) {
@@ -541,7 +537,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 9ebe7913b..f16d341f5 100644
--- a/cache.h
+++ b/cache.h
@@ -425,6 +425,15 @@ enum safe_crlf {
extern enum safe_crlf safe_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 cba2bcfb6..062449459 100644
--- a/config.c
+++ b/config.c
@@ -471,6 +471,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 3527f1663..6739a3f41 100644
--- a/environment.c
+++ b/environment.c
@@ -37,6 +37,7 @@ const char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
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 d21081d0f..38a90adad 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_success \
@@ -171,7 +174,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)"'
@@ -192,6 +197,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 <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
diff --git a/t/t7201-co.sh b/t/t7201-co.sh
index 724adef0b..63915cd87 100755
--- a/t/t7201-co.sh
+++ b/t/t7201-co.sh
@@ -313,4 +313,28 @@ test_expect_success 'checkout specific path while in subdirectory' '
'
+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