X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=builtin-branch.c;h=2694c9cf497bb0f20c771a560d65eae5702bf931;hb=9fac800cae287256cea0512f5c5effae7a7aa784;hp=77b85dde1f661069d2e07314a1d6bf80f0b27efc;hpb=1fd81efad0d65afba91f8fb27b40a6345bfb50de;p=git.git diff --git a/builtin-branch.c b/builtin-branch.c index 77b85dde1..2694c9cf4 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -1,7 +1,7 @@ /* * Builtin "git branch" * - * Copyright (c) 2006 Kristian Høgsberg + * Copyright (c) 2006 Kristian Høgsberg * Based on git-branch.sh by Junio C Hamano. */ @@ -10,9 +10,16 @@ #include "refs.h" #include "commit.h" #include "builtin.h" - -static const char builtin_branch_usage[] = - "git-branch [-r] (-d | -D) | [--track | --no-track] [-l] [-f] [] | (-m | -M) [] | [--color | --no-color] [-r | -a] [-v [--abbrev= | --no-abbrev]]"; +#include "remote.h" +#include "parse-options.h" + +static const char * const builtin_branch_usage[] = { + "git-branch [options] [-r | -a]", + "git-branch [options] [-l] [-f] []", + "git-branch [options] [-r] (-d | -D) ", + "git-branch [options] (-m | -M) [] ", + NULL +}; #define REF_UNKNOWN_TYPE 0x00 #define REF_LOCAL_BRANCH 0x01 @@ -22,7 +29,7 @@ static const char builtin_branch_usage[] = static const char *head; static unsigned char head_sha1[20]; -static int branch_track_remotes; +static int branch_track = 1; static int branch_use_color; static char branch_colors[][COLOR_MAXLEN] = { @@ -67,7 +74,7 @@ static int git_branch_config(const char *var, const char *value) return 0; } if (!strcmp(var, "branch.autosetupmerge")) - branch_track_remotes = git_config_bool(var, value); + branch_track = git_config_bool(var, value); return git_default_config(var, value); } @@ -141,7 +148,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) if (!force && !in_merge_bases(rev, &head_rev, 1)) { - error("The branch '%s' is not a strict subset of " + error("The branch '%s' is not an ancestor of " "your current HEAD.\n" "If you are sure you want to delete it, " "run 'git branch -D %s'.", argv[i], argv[i]); @@ -267,23 +274,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } if (verbose) { - char *subject = NULL; - unsigned long subject_len = 0; + struct strbuf subject; const char *sub = " **** invalid ref ****"; + strbuf_init(&subject, 0); + commit = lookup_commit(item->sha1); if (commit && !parse_commit(commit)) { - pretty_print_commit(CMIT_FMT_ONELINE, commit, ~0, - &subject, &subject_len, 0, - NULL, NULL, 0); - sub = subject; + pretty_print_commit(CMIT_FMT_ONELINE, commit, + &subject, 0, NULL, NULL, 0, 0); + sub = subject.buf; } printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), find_unique_abbrev(item->sha1, abbrev), sub); - if (subject) - free(subject); + strbuf_release(&subject); } else { printf("%c %s%s%s\n", c, branch_get_color(color), item->name, branch_get_color(COLOR_BRANCH_RESET)); @@ -324,125 +330,70 @@ 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; +struct tracking { + struct refspec spec; + char *src; + const char *remote; + int matches; +}; -static int get_remote_branch_name(const char *value) +static int find_tracked_branch(struct remote *remote, void *priv) { - const char *colon; - const char *end; - - if (*value == '+') - value++; + struct tracking *tracking = priv; - colon = strchr(value, ':'); - if (!colon) - return 0; - - end = value + strlen(value); - - /* - * Try an exact match first. I.e. handle the case where the - * value is "$anything:refs/foo/bar/baz" and start_ref is exactly - * "refs/foo/bar/baz". Then the name at the remote is $anything. - */ - if (!strcmp(colon + 1, start_ref)) { - /* Truncate the value before the colon. */ - nfasprintf(&config_repo, "%.*s", colon - value, value); - return 1; + if (!remote_find_tracking(remote, &tracking->spec)) { + if (++tracking->matches == 1) { + tracking->src = tracking->spec.src; + tracking->remote = remote->name; + } else { + free(tracking->spec.src); + if (tracking->src) { + free(tracking->src); + tracking->src = NULL; + } + } + tracking->spec.src = NULL; } - /* - * Is this a wildcard match? - */ - if ((end - 2 <= value) || end[-2] != '/' || end[-1] != '*' || - (colon - 2 <= value) || colon[-2] != '/' || colon[-1] != '*') - return 0; - - /* - * Value is "refs/foo/bar/:refs/baz/boa/" - * and start_ref begins with "refs/baz/boa/"; the name at the - * remote is refs/foo/bar/ with the remaining part of the - * start_ref. The length of the prefix on the RHS is (end - - * colon - 2), including the slash immediately before the - * asterisk. - */ - if ((strlen(start_ref) < end - colon - 2) || - memcmp(start_ref, colon + 1, end - colon - 2)) - return 0; /* does not match prefix */ - - /* Replace the asterisk with the remote branch name. */ - nfasprintf(&config_repo, "%.*s%s", - (colon - 1) - value, value, - start_ref + (end - colon - 2)); - return 1; -} - -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 || strcmp(var, ".fetch")) - return 0; - /* - * Ok, we are looking at key == "remote.$foo.fetch"; - */ - if (get_remote_branch_name(value)) - nfasprintf(&config_remote, "%.*s", var - (key + 7), key + 7); - return 0; } -static void set_branch_merge(const char *name, const char *config_remote, - const char *config_repo) + +/* + * This is called when new_ref is branched off of orig_ref, and tries + * to infer the settings for branch..{remote,merge} from the + * config. + */ +static int setup_tracking(const char *new_ref, const char *orig_ref) { char key[1024]; - 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); -} + struct tracking tracking; -static void set_branch_defaults(const char *name, const char *real_ref) -{ - /* - * name is the name of new branch under refs/heads; - * real_ref is typically refs/remotes/$foo/$bar, where - * $foo is the remote name (there typically are no slashes) - * and $bar is the branch name we map from the remote - * (it could have slashes). - */ - start_ref = real_ref; - git_config(get_remote_config); - if (!config_repo && !config_remote && - !prefixcmp(real_ref, "refs/heads/")) { - set_branch_merge(name, ".", real_ref); - printf("Branch %s set up to track local branch %s.\n", - name, real_ref); - } + if (strlen(new_ref) > 1024 - 7 - 7 - 1) + return error("Tracking not set up: name too long: %s", + new_ref); - if (config_repo && config_remote) { - set_branch_merge(name, config_remote, config_repo); + memset(&tracking, 0, sizeof(tracking)); + tracking.spec.dst = (char *)orig_ref; + if (for_each_remote(find_tracked_branch, &tracking) || + !tracking.matches) + 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", - name, real_ref); + new_ref, orig_ref); } - if (config_repo) - free(config_repo); - if (config_remote) - free(config_remote); + return 0; } static void create_branch(const char *name, const char *start_name, @@ -505,7 +456,7 @@ static void create_branch(const char *name, const char *start_name, 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); + setup_tracking(name, real_ref); if (write_ref_sha1(lock, sha1, msg) < 0) die("Failed to write ref: %s.", strerror(errno)); @@ -556,97 +507,37 @@ static void rename_branch(const char *oldname, const char *newname, int force) 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 delete = 0, rename = 0, force_create = 0; int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 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]; + struct option options[] = { + OPT_GROUP("Generic options"), + OPT__VERBOSE(&verbose), + OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"), + OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"), + OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches", + REF_REMOTE_BRANCH), + OPT__ABBREV(&abbrev), + + OPT_GROUP("Specific git-branch actions:"), + OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches", + REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), + OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1), + OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2), + OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1), + OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2), + OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"), + OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"), + OPT_END(), + }; - if (arg[0] != '-') - break; - if (!strcmp(arg, "--")) { - i++; - break; - } - if (!strcmp(arg, "--track")) { - track = 1; - continue; - } - if (!strcmp(arg, "--no-track")) { - track = 0; - continue; - } - if (!strcmp(arg, "-d")) { - delete = 1; - continue; - } - if (!strcmp(arg, "-D")) { - delete = 1; - force_delete = 1; - continue; - } - if (!strcmp(arg, "-f")) { - force_create = 1; - continue; - } - if (!strcmp(arg, "-m")) { - rename = 1; - continue; - } - if (!strcmp(arg, "-M")) { - rename = 1; - force_rename = 1; - continue; - } - if (!strcmp(arg, "-r")) { - kinds = REF_REMOTE_BRANCH; - continue; - } - if (!strcmp(arg, "-a")) { - kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH; - continue; - } - if (!strcmp(arg, "-l")) { - reflog = 1; - continue; - } - if (!prefixcmp(arg, "--no-abbrev")) { - abbrev = 0; - continue; - } - if (!prefixcmp(arg, "--abbrev=")) { - abbrev = strtoul(arg + 9, NULL, 10); - if (abbrev < MINIMUM_ABBREV) - abbrev = MINIMUM_ABBREV; - else if (abbrev > 40) - abbrev = 40; - continue; - } - if (!strcmp(arg, "-v")) { - verbose = 1; - continue; - } - if (!strcmp(arg, "--color")) { - branch_use_color = 1; - continue; - } - if (!strcmp(arg, "--no-color")) { - branch_use_color = 0; - continue; - } - usage(builtin_branch_usage); - } - - if ((delete && rename) || (delete && force_create) || - (rename && force_create)) - usage(builtin_branch_usage); + git_config(git_branch_config); + track = 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); head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) @@ -654,26 +545,25 @@ int cmd_branch(int argc, const char **argv, const char *prefix) head = xstrdup(head); if (!strcmp(head, "HEAD")) { detached = 1; - } - else { + } else { if (prefixcmp(head, "refs/heads/")) die("HEAD not found below refs/heads!"); head += 11; } if (delete) - return delete_branches(argc - i, argv + i, force_delete, kinds); - else if (i == argc) + return delete_branches(argc, argv, delete > 1, kinds); + else if (argc == 0) print_ref_list(kinds, detached, verbose, abbrev); - else if (rename && (i == argc - 1)) - rename_branch(head, argv[i], force_rename); - else if (rename && (i == argc - 2)) - rename_branch(argv[i], argv[i + 1], force_rename); - else if (i == argc - 1 || i == argc - 2) - create_branch(argv[i], (i == argc - 2) ? argv[i+1] : head, + else if (rename && (argc == 1)) + rename_branch(head, argv[0], rename > 1); + else if (rename && (argc == 2)) + rename_branch(argv[0], argv[1], rename > 1); + else if (argc <= 2) + create_branch(argv[0], (argc == 2) ? argv[1] : head, force_create, reflog, track); else - usage(builtin_branch_usage); + usage_with_options(builtin_branch_usage, options); return 0; }