Merge branch 'js/branch-track'

* js/branch-track:
  doc: documentation update for the branch track changes
  branch: optionally setup branch.*.merge from upstream local branches

Conflicts:

	Documentation/config.txt
	Documentation/git-branch.txt
	Documentation/git-checkout.txt
	builtin-branch.c
	cache.h
	t/t7201-co.sh
This commit is contained in:
Junio C Hamano 2008-02-27 13:02:57 -08:00
commit 60b188a984
12 changed files with 131 additions and 73 deletions

View file

@ -379,10 +379,14 @@ apply.whitespace::
branch.autosetupmerge:: branch.autosetupmerge::
Tells `git-branch` and `git-checkout` to setup new branches Tells `git-branch` and `git-checkout` to setup new branches
so that linkgit:git-pull[1] will appropriately merge from that so that linkgit:git-pull[1] will appropriately merge from the
remote branch. Note that even if this option is not set, starting point branch. Note that even if this option is not set,
this behavior can be chosen per-branch using the `--track` 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:: branch.<name>.remote::
When in branch <name>, it tells `git fetch` which remote to fetch. When in branch <name>, it tells `git fetch` which remote to fetch.

View file

@ -35,11 +35,10 @@ working tree to it; use "git checkout <newbranch>" to switch to the
new branch. new branch.
When a local branch is started off a remote branch, git sets up the 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 branch so that linkgit:git-pull[1] will appropriately merge from
remote branch. If this behavior is not desired, it is possible to the remote branch. This behavior may be changed via the global
disable it using the global `branch.autosetupmerge` configuration `branch.autosetupmerge` configuration flag. That setting can be
flag. That setting can be overridden by using the `--track` overridden by using the `--track` and `--no-track` options.
and `--no-track` options.
With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>. With a '-m' or '-M' option, <oldbranch> will be renamed to <newbranch>.
If <oldbranch> had a corresponding reflog, it is renamed to match 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. Display the full sha1s in output listing rather than abbreviating them.
--track:: --track::
Set up configuration so that git-pull will automatically When creating a new branch, set up configuration so that git-pull
retrieve data from the remote branch. Use this if you always will automatically retrieve data from the start point, which must be
pull from the same remote branch into the new branch, or if you a branch. Use this if you always pull from the same upstream branch
don't want to use "git pull <repository> <refspec>" explicitly. into the new branch, and if you don't want to use "git pull
This behavior is the default. Set the <repository> <refspec>" explicitly. This behavior is the default
branch.autosetupmerge configuration variable to false if you when the start point is a remote branch. Set the
want git-checkout and git-branch to always behave as if branch.autosetupmerge configuration variable to `false` if you want
'--no-track' were given. 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:: --no-track::
When a branch is created off a remote branch, Ignore the branch.autosetupmerge configuration variable.
set up configuration so that git-pull will not retrieve data
from the remote branch, ignoring the branch.autosetupmerge
configuration variable.
<branchname>:: <branchname>::
The name of the branch to create or delete. The name of the branch to create or delete.

View file

@ -48,21 +48,19 @@ OPTIONS
may restrict the characters allowed in a branch name. may restrict the characters allowed in a branch name.
--track:: --track::
When -b is given and a branch is created off a remote branch, When creating a new branch, set up configuration so that git-pull
set up configuration so that git-pull will automatically will automatically retrieve data from the start point, which must be
retrieve data from the remote branch. Use this if you always a branch. Use this if you always pull from the same upstream branch
pull from the same remote branch into the new branch, or if you into the new branch, and if you don't want to use "git pull
don't want to use "git pull <repository> <refspec>" explicitly. <repository> <refspec>" explicitly. This behavior is the default
This behavior is the default. Set the when the start point is a remote branch. Set the
branch.autosetupmerge configuration variable to false if you branch.autosetupmerge configuration variable to `false` if you want
want git-checkout and git-branch to always behave as if git-checkout and git-branch to always behave as if '--no-track' were
'--no-track' were given. given. Set it to `always` if you want this behavior when the
start-point is either a local or remote branch.
--no-track:: --no-track::
When -b is given and a branch is created off a remote branch, Ignore the branch.autosetupmerge configuration variable.
set up configuration so that git-pull will not retrieve data
from the remote branch, ignoring the branch.autosetupmerge
configuration variable.
-l:: -l::
Create the new branch's reflog. This activates recording of Create the new branch's reflog. This activates recording of

View file

@ -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 * to infer the settings for branch.<new_ref>.{remote,merge} from the
* config. * 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]; char key[1024];
struct tracking tracking; struct tracking tracking;
@ -48,30 +49,36 @@ static int setup_tracking(const char *new_ref, const char *orig_ref)
memset(&tracking, 0, sizeof(tracking)); memset(&tracking, 0, sizeof(tracking));
tracking.spec.dst = (char *)orig_ref; tracking.spec.dst = (char *)orig_ref;
if (for_each_remote(find_tracked_branch, &tracking) || if (for_each_remote(find_tracked_branch, &tracking))
!tracking.matches)
return 1; return 1;
if (!tracking.matches)
switch (track) {
case BRANCH_TRACK_ALWAYS:
case BRANCH_TRACK_EXPLICIT:
break;
default:
return 1;
}
if (tracking.matches > 1) if (tracking.matches > 1)
return error("Not tracking: ambiguous information for ref %s", return error("Not tracking: ambiguous information for ref %s",
orig_ref); orig_ref);
if (tracking.matches == 1) { sprintf(key, "branch.%s.remote", new_ref);
sprintf(key, "branch.%s.remote", new_ref); git_config_set(key, tracking.remote ? tracking.remote : ".");
git_config_set(key, tracking.remote ? tracking.remote : "."); sprintf(key, "branch.%s.merge", new_ref);
sprintf(key, "branch.%s.merge", new_ref); git_config_set(key, tracking.src ? tracking.src : orig_ref);
git_config_set(key, tracking.src); free(tracking.src);
free(tracking.src); printf("Branch %s set up to track %s branch %s.\n", new_ref,
printf("Branch %s set up to track remote branch %s.\n", tracking.remote ? "remote" : "local", orig_ref);
new_ref, orig_ref);
}
return 0; return 0;
} }
void create_branch(const char *head, void create_branch(const char *head,
const char *name, const char *start_name, 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 ref_lock *lock;
struct commit *commit; struct commit *commit;
@ -98,7 +105,8 @@ void create_branch(const char *head,
switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) { switch (dwim_ref(start_name, strlen(start_name), sha1, &real_ref)) {
case 0: case 0:
/* Not branching from any existing branch */ /* 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; break;
case 1: case 1:
/* Unique completion -- good */ /* Unique completion -- good */
@ -126,17 +134,13 @@ void create_branch(const char *head,
snprintf(msg, sizeof msg, "branch: Created from %s", snprintf(msg, sizeof msg, "branch: Created from %s",
start_name); 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) if (real_ref && track)
setup_tracking(name, real_ref); setup_tracking(name, real_ref, track);
if (write_ref_sha1(lock, sha1, msg) < 0) if (write_ref_sha1(lock, sha1, msg) < 0)
die("Failed to write ref: %s.", strerror(errno)); die("Failed to write ref: %s.", strerror(errno));
if (real_ref) free(real_ref);
free(real_ref);
} }
void remove_branch_state(void) void remove_branch_state(void)

View file

@ -13,7 +13,7 @@
* branch for (if any). * branch for (if any).
*/ */
void create_branch(const char *head, const char *name, const char *start_name, 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 * Remove information about the state of working on the current

View file

@ -30,8 +30,6 @@ static const char * const builtin_branch_usage[] = {
static const char *head; static const char *head;
static unsigned char head_sha1[20]; static unsigned char head_sha1[20];
static int branch_track = 1;
static int branch_use_color = -1; static int branch_use_color = -1;
static char branch_colors[][COLOR_MAXLEN] = { static char branch_colors[][COLOR_MAXLEN] = {
"\033[m", /* reset */ "\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]); color_parse(value, var, branch_colors[slot]);
return 0; return 0;
} }
if (!strcmp(var, "branch.autosetupmerge")) {
branch_track = git_config_bool(var, value);
return 0;
}
return git_color_default_config(var, value); 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 delete = 0, rename = 0, force_create = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 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; int kinds = REF_LOCAL_BRANCH;
struct commit_list *with_commit = NULL; struct commit_list *with_commit = NULL;
struct option options[] = { struct option options[] = {
OPT_GROUP("Generic options"), OPT_GROUP("Generic options"),
OPT__VERBOSE(&verbose), 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_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
REF_REMOTE_BRANCH), REF_REMOTE_BRANCH),
@ -458,7 +454,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (branch_use_color == -1) if (branch_use_color == -1)
branch_use_color = git_use_color_default; branch_use_color = git_use_color_default;
track = branch_track; track = git_branch_track;
argc = parse_options(argc, argv, options, builtin_branch_usage, 0); argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
if (!!delete + !!rename + !!force_create > 1) if (!!delete + !!rename + !!force_create > 1)
usage_with_options(builtin_branch_usage, options); usage_with_options(builtin_branch_usage, options);

View file

@ -186,7 +186,7 @@ struct checkout_opts {
char *new_branch; char *new_branch;
int new_branch_log; int new_branch_log;
int track; enum branch_track track;
}; };
struct branch_info { 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); 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) 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); 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__QUIET(&opts.quiet),
OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"), OPT_STRING('b', NULL, &opts.new_branch, "new branch", "branch"),
OPT_BOOLEAN('l', NULL, &opts.new_branch_log, "log for new 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('f', NULL, &opts.force, "force"),
OPT_BOOLEAN('m', NULL, &opts.merge, "merge"), OPT_BOOLEAN('m', NULL, &opts.merge, "merge"),
OPT_END(), OPT_END(),
@ -512,7 +508,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
git_config(git_checkout_config); git_config(git_checkout_config);
opts.track = branch_track; opts.track = git_branch_track;
argc = parse_options(argc, argv, options, checkout_usage, 0); argc = parse_options(argc, argv, options, checkout_usage, 0);
if (argc) { if (argc) {
@ -541,7 +537,7 @@ int cmd_checkout(int argc, const char **argv, const char *prefix)
argc--; 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"); die("git checkout: --track and --no-track require -b");
if (opts.force && opts.merge) if (opts.force && opts.merge)

View file

@ -425,6 +425,15 @@ enum safe_crlf {
extern enum safe_crlf 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 #define GIT_REPO_VERSION 0
extern int repository_format_version; extern int repository_format_version;
extern int check_repository_format(void); extern int check_repository_format(void);

View file

@ -471,6 +471,14 @@ int git_default_config(const char *var, const char *value)
whitespace_rule_cfg = parse_whitespace_rule(value); whitespace_rule_cfg = parse_whitespace_rule(value);
return 0; 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. */ /* Add other config variables here and to Documentation/config.txt. */
return 0; return 0;

View file

@ -37,6 +37,7 @@ const char *excludes_file;
int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */ int auto_crlf = 0; /* 1: both ways, -1: only when adding git objects */
enum safe_crlf safe_crlf = SAFE_CRLF_WARN; enum safe_crlf safe_crlf = SAFE_CRLF_WARN;
unsigned whitespace_rule_cfg = WS_DEFAULT_RULE; 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() */ /* This is set by setup_git_dir_gently() and/or git_default_config() */
char *git_work_tree_cfg; char *git_work_tree_cfg;

View file

@ -15,6 +15,9 @@ test_expect_success \
'echo Hello > A && 'echo Hello > A &&
git update-index --add A && git update-index --add A &&
git-commit -m "Initial commit." && git-commit -m "Initial commit." &&
echo World >> A &&
git update-index --add A &&
git-commit -m "Second commit." &&
HEAD=$(git rev-parse --verify HEAD)' HEAD=$(git rev-parse --verify HEAD)'
test_expect_success \ 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 "$(git config branch.my2.merge)" = refs/heads/master'
test_expect_success 'no tracking without .fetch entries' \ 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.remote)" &&
test -z "$(git config branch.my6.merge)"' test -z "$(git config branch.my6.merge)"'
@ -192,6 +197,21 @@ test_expect_success 'test deleting branch without config' \
'git branch my7 s && 'git branch my7 s &&
test "$(git branch -d my7 2>&1)" = "Deleted branch my7."' 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 # Keep this test last, as it changes the current branch
cat >expect <<EOF cat >expect <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master 0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master

View file

@ -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 test_done