From 0746d19a823aa12239df9d32db6fa6795d8c8539 Mon Sep 17 00:00:00 2001 From: Paolo Bonzini Date: Thu, 8 Mar 2007 10:58:35 +0100 Subject: git-branch, git-checkout: autosetup for remote branch tracking In order to track and build on top of a branch 'topic' you track from your upstream repository, you often would end up doing this sequence: git checkout -b mytopic origin/topic git config --add branch.mytopic.remote origin git config --add branch.mytopic.merge refs/heads/topic This would first fork your own 'mytopic' branch from the 'topic' branch you track from the 'origin' repository; then it would set up two configuration variables so that 'git pull' without parameters does the right thing while you are on your own 'mytopic' branch. This commit adds a --track option to git-branch, so that "git branch --track mytopic origin/topic" performs the latter two actions when creating your 'mytopic' branch. If the configuration variable branch.autosetupmerge is set to true, you do not have to pass the --track option explicitly; further patches in this series allow setting the variable with a "git remote add" option. The configuration variable is off by default, and there is a --no-track option to countermand it even if the variable is set. Signed-off-by: Paolo Bonzini Signed-off-by: Junio C Hamano --- builtin-branch.c | 140 +++++++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 132 insertions(+), 8 deletions(-) (limited to 'builtin-branch.c') diff --git a/builtin-branch.c b/builtin-branch.c index 06d8a8ce0..e161e0097 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= | --no-abbrev]]"; + "git-branch [-r] (-d | -D) | [--track | --no-track] [-l] [-f] [] | (-m | -M) [] | [--color | --no-color] [-r | -a] [-v [--abbrev= | --no-abbrev]]"; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -22,6 +22,8 @@ static const char builtin_branch_usage[] = static const char *head; static unsigned char head_sha1[20]; +static int branch_track_remotes; + static int branch_use_color; static char branch_colors[][COLOR_MAXLEN] = { "\033[m", /* reset */ @@ -64,6 +66,9 @@ 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_remotes = git_config_bool(var, value); + return git_default_config(var, value); } @@ -309,14 +314,109 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev) free_ref_list(&ref_list); } +static char *config_repo; +static char *config_remote; +static const char *start_ref; +static int start_len; +static int base_len; + +static int get_remote_branch_name(const char *value) +{ + const char *colon; + const char *end; + + if (*value == '+') + value++; + + colon = strchr(value, ':'); + if (!colon) + return 0; + + end = value + strlen(value); + + /* Try an exact match first. */ + if (!strcmp(colon + 1, start_ref)) { + /* Truncate the value before the colon. */ + nfasprintf(&config_repo, "%.*s", colon - value, value); + return 1; + } + + /* Try with a wildcard match now. */ + if (end - value > 2 && end[-2] == '/' && end[-1] == '*' && + colon - value > 2 && colon[-2] == '/' && colon[-1] == '*' && + (end - 2) - (colon + 1) == base_len && + !strncmp(colon + 1, start_ref, base_len)) { + /* Replace the star with the remote branch name. */ + nfasprintf(&config_repo, "%.*s%s", + (colon - 2) - value, value, + start_ref + base_len); + return 1; + } + + return 0; +} + +static int get_remote_config(const char *key, const char *value) +{ + const char *var; + if (prefixcmp(key, "remote.")) + return 0; + + var = strrchr(key, '.'); + if (var == key + 6) + return 0; + + if (!strcmp(var, ".fetch") && get_remote_branch_name(value)) + nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7); + + return 0; +} + +static void set_branch_defaults(const char *name, const char *real_ref) +{ + char key[1024]; + const char *slash = strrchr(real_ref, '/'); + + if (!slash) + return; + + start_ref = real_ref; + start_len = strlen(real_ref); + base_len = slash - real_ref; + git_config(get_remote_config); + + if (config_repo && config_remote) { + if (sizeof(key) <= + snprintf(key, sizeof(key), "branch.%s.remote", name)) + die("what a long branch name you have!"); + git_config_set(key, config_remote); + + /* + * We do not have to check if we have enough space for + * the 'merge' key, since it's shorter than the + * previous 'remote' key, which we already checked. + */ + snprintf(key, sizeof(key), "branch.%s.merge", name); + git_config_set(key, config_repo); + + printf("Branch %s set up to track remote branch %s.\n", + name, real_ref); + } + + if (config_repo) + free(config_repo); + if (config_remote) + free(config_remote); +} + 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]; + char *real_ref, ref[PATH_MAX], msg[PATH_MAX + 20]; int forcing = 0; snprintf(ref, sizeof ref, "refs/heads/%s", name); @@ -331,10 +431,14 @@ static void create_branch(const char *name, const char *start_name, forcing = 1; } - if (start_sha1) + if (start_sha1) { /* detached HEAD */ hashcpy(sha1, start_sha1); - else if (get_sha1(start_name, sha1)) + real_ref = NULL; + } + else if (dwim_ref(start_name, strlen(start_name), sha1, &real_ref) > 1) + die("Ambiguous object name: '%s'.", start_name); + else if (real_ref == NULL) die("Not a valid object name: '%s'.", start_name); if ((commit = lookup_commit_reference(sha1)) == NULL) @@ -355,8 +459,17 @@ 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. So far, this is only done for + remotes registered via .git/config. */ + if (real_ref && track) + set_branch_defaults(name, real_ref); + 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) @@ -398,11 +511,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 = branch_track_remotes; for (i = 1; i < argc; i++) { const char *arg = argv[i]; @@ -413,6 +527,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; @@ -499,9 +621,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); -- cgit v1.2.1