diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 0fd58083eb2afdb7fed7ebf01bab3b5e1cae1421..450f90368f74ce104adaea4d8f290dbd99eb84e1 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -8,24 +8,27 @@ git-branch - List, create, or delete branches SYNOPSIS -------- [verse] -'git-branch' [--color | --no-color] [-r | -a] [--merged | --no-merged] - [-v [--abbrev= | --no-abbrev]] - [--contains ] +'git-branch' [--color | --no-color] [-r | -a] + [-v [--abbrev= | --no-abbrev]] + [(--merged | --no-merged | --contains) []] 'git-branch' [--track | --no-track] [-l] [-f] [] 'git-branch' (-m | -M) [] 'git-branch' (-d | -D) [-r] ... DESCRIPTION ----------- -With no arguments given a list of existing branches -will be shown, the current branch will be highlighted with an asterisk. -Option `-r` causes the remote-tracking branches to be listed, -and option `-a` shows both. -With `--contains `, shows only the branches that -contains the named commit (in other words, the branches whose -tip commits are descendant of the named commit). -With `--merged`, only branches merged into HEAD will be listed, and -with `--no-merged` only branches not merged into HEAD will be listed. + +With no arguments, existing branches are listed, the current branch will +be highlighted with an asterisk. Option `-r` causes the remote-tracking +branches to be listed, and option `-a` shows both. + +With `--contains`, shows only the branches that contains the named commit +(in other words, the branches whose tip commits are descendant of the +named commit). With `--merged`, only branches merged into the named +commit (i.e. the branches whose tip commits are reachable from the named +commit) will be listed. With `--no-merged` only branches not merged into +the named commit will be listed. Missing argument defaults to +'HEAD' (i.e. the tip of the current branch). In its second form, a new branch named will be created. It will start out with a head equal to the one given as . diff --git a/builtin-branch.c b/builtin-branch.c index 50cbc9c81a49d20cf289f3acc7843ce7170966d9..1926c47581050168abd0a5ed4e6ce45100cde2cf 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -46,7 +46,12 @@ enum color_branch { COLOR_BRANCH_CURRENT = 4, }; -static int mergefilter = -1; +static enum merge_filter { + NO_FILTER = 0, + SHOW_NOT_MERGED, + SHOW_MERGED, +} merge_filter; +static unsigned char merge_filter_ref[20]; static int parse_branch_color_slot(const char *var, int ofs) { @@ -234,13 +239,15 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, if ((kind & ref_list->kinds) == 0) return 0; - if (mergefilter > -1) { + if (merge_filter != NO_FILTER) { branch.item = lookup_commit_reference_gently(sha1, 1); if (!branch.item) die("Unable to lookup tip of branch %s", refname); - if (mergefilter == 0 && has_commit(head_sha1, &branch)) + if (merge_filter == SHOW_NOT_MERGED && + has_commit(merge_filter_ref, &branch)) return 0; - if (mergefilter == 1 && !has_commit(head_sha1, &branch)) + if (merge_filter == SHOW_MERGED && + !has_commit(merge_filter_ref, &branch)) return 0; } @@ -421,6 +428,20 @@ static int opt_parse_with_commit(const struct option *opt, const char *arg, int return 0; } +static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset) +{ + merge_filter = ((opt->long_name[0] == 'n') + ? SHOW_NOT_MERGED + : SHOW_MERGED); + if (unset) + merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */ + if (!arg) + arg = "HEAD"; + if (get_sha1(arg, merge_filter_ref)) + die("malformed object name %s", arg); + return 0; +} + int cmd_branch(int argc, const char **argv, const char *prefix) { int delete = 0, rename = 0, force_create = 0; @@ -461,7 +482,18 @@ int cmd_branch(int argc, const char **argv, const char *prefix) 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_SET_INT(0, "merged", &mergefilter, "list only merged branches", 1), + { + OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, + "commit", "print only not merged branches", + PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, + opt_parse_merge_filter, (intptr_t) "HEAD", + }, + { + OPTION_CALLBACK, 0, "merged", &merge_filter_ref, + "commit", "print only merged branches", + PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, + opt_parse_merge_filter, (intptr_t) "HEAD", + }, OPT_END(), }; @@ -471,9 +503,6 @@ int cmd_branch(int argc, const char **argv, const char *prefix) branch_use_color = git_use_color_default; 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); head = resolve_ref("HEAD", head_sha1, 0, NULL); if (!head) @@ -486,6 +515,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die("HEAD not found below refs/heads!"); head += 11; } + hashcpy(merge_filter_ref, head_sha1); + + argc = parse_options(argc, argv, options, builtin_branch_usage, 0); + if (!!delete + !!rename + !!force_create > 1) + usage_with_options(builtin_branch_usage, options); if (delete) return delete_branches(argc, argv, delete > 1, kinds);