* git-branch: register where to merge from, when branching off a remote branch. A rather standard (in 1.5) procedure for branching off a remote archive is: git checkout -b branchname remote/upstreambranch git config --add branch.branchname.remote remote git config --add branch.branchname.merge refs/heads/upstreambranch In this case, we can save the user some effort if "git branch" (and "git checkout -b") automatically do the two "git-config --add"s when the source branch is remote. There is a good chance that some user wants to merge something different, but in that case they have to specify what to merge _anyway_. The behavior is controlled by core.trackremotebranches, and can be fine-grained to a specific invocation of "git branch" using the new --track and --no-track options. diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index aa1fdd4..14dc07d 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -9,7 +9,7 @@ SYNOPSIS -------- [verse] 'git-branch' [--color | --no-color] [-r | -a] [-v [--abbrev=]] -'git-branch' [-l] [-f] [] +'git-branch' [--track | --no-track] [-l] [-f] [] 'git-branch' (-m | -M) [] 'git-branch' (-d | -D) [-r] ... @@ -25,6 +25,12 @@ It will start out with a head equal to the one given as . If no is given, the branch will be created with a head equal to that of the currently checked out branch. +When a local branch is started off a remote branch, git will setup +the branch so that gitlink:git-pull[1] will appropriately merge from +that remote branch. If this behavior is undesired, it is possible +to change it using the `core.trackremotebranches` option, or 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 , and a reflog entry is created to remember the branch diff --git a/builtin-branch.c b/builtin-branch.c index d0179b0..20de049 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -12,7 +12,7 @@ #include "builtin.h" static const char builtin_branch_usage[] = - "git-branch [-r] (-d | -D) | [-l] [-f] [] | (-m | -M) [] | [--color | --no-color] [-r | -a] [-v [--abbrev=]]"; + "git-branch [-r] (-d | -D) | [--track | --no-track] [-l] [-f] [] | (-m | -M) [] | [--color | --no-color] [-r | -a] [-v [--abbrev=]]"; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -308,15 +307,36 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev) free_ref_list(&ref_list); } +static void register_branch_pull (const char *name, const char *remote_name) +{ + char *slash = strchr(remote_name, '/'); + + char *config_key = xmalloc(strlen(name) + 15); + char *merge_value = xmalloc(strlen(remote_name) + 10); + + char *remote_value = xstrdup(remote_name); + remote_value[slash - remote_name] = 0; + sprintf(config_key, "branch.%s.remote", name); + git_config_set(config_key, remote_value); + + sprintf(merge_value, "refs/heads/%s", slash + 1); + sprintf(config_key, "branch.%s.merge", name); + git_config_set(config_key, merge_value); + + free (config_key); + free (remote_value); + free (merge_value); +} + static void create_branch(const char *name, const char *start_name, unsigned char *start_sha1, - int force, int reflog) + int force, int reflog, int track) { struct ref_lock *lock; struct commit *commit; unsigned char sha1[20]; - char ref[PATH_MAX], msg[PATH_MAX + 20]; - int forcing = 0; + char *real_ref = NULL, ref[PATH_MAX], msg[PATH_MAX + 20]; + int forcing = 0, remote = 0; snprintf(ref, sizeof ref, "refs/heads/%s", name); if (check_ref_format(ref)) @@ -333,7 +353,9 @@ static void create_branch(const char *name, const char *start_name, if (start_sha1) /* detached HEAD */ hashcpy(sha1, start_sha1); - else if (get_sha1(start_name, sha1)) + else if (dwim_ref(start_name, strlen (start_name), sha1, &real_ref)) + remote = !prefixcmp(real_ref, "refs/remotes/"); + else die("Not a valid object name: '%s'.", start_name); if ((commit = lookup_commit_reference(sha1)) == NULL) @@ -354,8 +376,16 @@ static void create_branch(const char *name, const char *start_name, 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. */ + if (remote && track) + register_branch_pull (name, real_ref + 13); + if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); + + if (real_ref) + free (real_ref); } static void rename_branch(const char *oldname, const char *newname, int force) @@ -397,11 +427,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix) int delete = 0, force_delete = 0, force_create = 0; int rename = 0, force_rename = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0; - int reflog = 0; + int reflog = 0, track; int kinds = REF_LOCAL_BRANCH; int i; git_config(git_branch_config); + track = track_remote_branches; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -412,6 +443,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix) i++; break; } + if (!strcmp(arg, "--track")) { + track = 1; + continue; + } + if (!strcmp(arg, "--no-track")) { + track = 0; + continue; + } if (!strcmp(arg, "-d")) { delete = 1; continue; @@ -490,9 +529,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) else if (rename && (i == argc - 2)) rename_branch(argv[i], argv[i + 1], force_rename); else if (i == argc - 1) - create_branch(argv[i], head, head_sha1, force_create, reflog); + create_branch(argv[i], head, head_sha1, force_create, reflog, + track); else if (i == argc - 2) - create_branch(argv[i], argv[i+1], NULL, force_create, reflog); + create_branch(argv[i], argv[i+1], NULL, force_create, reflog, + track); else usage(builtin_branch_usage); diff --git a/cache.h b/cache.h index 8bbc142..585a9b4 100644 --- a/cache.h +++ b/cache.h @@ -205,6 +205,7 @@ extern int trust_executable_bit; extern int assume_unchanged; extern int prefer_symlink_refs; extern int log_all_ref_updates; +extern int track_remote_branches; extern int warn_ambiguous_refs; extern int shared_repository; extern const char *apply_default_whitespace; diff --git a/config.c b/config.c index 0ff413b..49df7bd 100644 --- a/config.c +++ b/config.c @@ -294,6 +294,11 @@ int git_default_config(const char *var, const char *value) return 0; } + if (!strcmp(var, "core.trackremotebranches")) { + track_remote_branches = git_config_bool(var, value); + return 0; + } + if (!strcmp(var, "core.legacyheaders")) { use_legacy_headers = git_config_bool(var, value); return 0; diff --git a/environment.c b/environment.c index 570e32a..e440d05 100644 --- a/environment.c +++ b/environment.c @@ -17,6 +17,7 @@ int assume_unchanged; int prefer_symlink_refs; int is_bare_repository_cfg = -1; /* unspecified */ int log_all_ref_updates = -1; /* unspecified */ +int track_remote_branches = 1; int warn_ambiguous_refs = 1; int repository_format_version; char *git_commit_encoding;