Skip to content

Commit

Permalink
branch: optionally setup branch.*.merge from upstream local branches
Browse files Browse the repository at this point in the history
"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>
  • Loading branch information
Jay Soffian authored and Junio C Hamano committed Feb 20, 2008
1 parent 569012b commit 9ed36cf
Show file tree
Hide file tree
Showing 9 changed files with 98 additions and 39 deletions.
44 changes: 24 additions & 20 deletions branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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 */
Expand Down Expand Up @@ -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)
Expand Down
2 changes: 1 addition & 1 deletion branch.h
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
13 changes: 5 additions & 8 deletions builtin-branch.c
Original file line number Diff line number Diff line change
Expand Up @@ -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 */
Expand Down Expand Up @@ -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);
}

Expand Down Expand Up @@ -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),
Expand All @@ -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);
Expand Down
14 changes: 5 additions & 9 deletions builtin-checkout.c
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ struct checkout_opts {

char *new_branch;
int new_branch_log;
int track;
enum branch_track track;
};

struct branch_info {
Expand Down Expand Up @@ -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);
}

Expand All @@ -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(),
Expand All @@ -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) {
Expand Down Expand Up @@ -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)
Expand Down
9 changes: 9 additions & 0 deletions cache.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
8 changes: 8 additions & 0 deletions config.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions environment.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
22 changes: 21 additions & 1 deletion t/t3200-branch.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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 \
Expand Down Expand Up @@ -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)"'

Expand All @@ -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 <<EOF
0000000000000000000000000000000000000000 $HEAD $GIT_COMMITTER_NAME <$GIT_COMMITTER_EMAIL> 1117150200 +0000 branch: Created from master
Expand Down
24 changes: 24 additions & 0 deletions t/t7201-co.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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

0 comments on commit 9ed36cf

Please sign in to comment.