Code

branch --merged/--no-merged: allow specifying arbitrary commit
[git.git] / builtin-branch.c
index 5bc4526f645d40a4a32c50240975ad26f49b8e3b..1926c47581050168abd0a5ed4e6ce45100cde2cf 100644 (file)
@@ -15,7 +15,7 @@
 #include "branch.h"
 
 static const char * const builtin_branch_usage[] = {
-       "git-branch [options] [-r | -a]",
+       "git-branch [options] [-r | -a] [--merged | --no-merged]",
        "git-branch [options] [-l] [-f] <branchname> [<start-point>]",
        "git-branch [options] [-r] (-d | -D) <branchname>",
        "git-branch [options] (-m | -M) [<oldbranch>] <newbranch>",
@@ -46,6 +46,13 @@ enum color_branch {
        COLOR_BRANCH_CURRENT = 4,
 };
 
+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)
 {
        if (!strcasecmp(var+ofs, "plain"))
@@ -61,7 +68,7 @@ static int parse_branch_color_slot(const char *var, int ofs)
        die("bad config variable '%s'", var);
 }
 
-static int git_branch_config(const char *var, const char *value)
+static int git_branch_config(const char *var, const char *value, void *cb)
 {
        if (!strcmp(var, "color.branch")) {
                branch_use_color = git_config_colorbool(var, value, -1);
@@ -74,7 +81,7 @@ static int git_branch_config(const char *var, const char *value)
                color_parse(value, var, branch_colors[slot]);
                return 0;
        }
-       return git_color_default_config(var, value);
+       return git_color_default_config(var, value, cb);
 }
 
 static const char *branch_get_color(enum color_branch ix)
@@ -210,6 +217,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        struct ref_item *newitem;
        int kind = REF_UNKNOWN_TYPE;
        int len;
+       static struct commit_list branch;
 
        /* Detect kind */
        if (!prefixcmp(refname, "refs/heads/")) {
@@ -231,6 +239,18 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
        if ((kind & ref_list->kinds) == 0)
                return 0;
 
+       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 (merge_filter == SHOW_NOT_MERGED &&
+                   has_commit(merge_filter_ref, &branch))
+                       return 0;
+               if (merge_filter == SHOW_MERGED &&
+                   !has_commit(merge_filter_ref, &branch))
+                       return 0;
+       }
+
        /* Resize buffer */
        if (ref_list->index >= ref_list->alloc) {
                ref_list->alloc = alloc_nr(ref_list->alloc);
@@ -408,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;
@@ -425,13 +459,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
                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_CALLBACK(0, "contains", &with_commit, "commit",
-                            "print only branches that contain the commit",
-                            opt_parse_with_commit),
+               {
+                       OPTION_CALLBACK, 0, "contains", &with_commit, "commit",
+                       "print only branches that contain the commit",
+                       PARSE_OPT_LASTARG_DEFAULT,
+                       opt_parse_with_commit, (intptr_t)"HEAD",
+               },
                {
                        OPTION_CALLBACK, 0, "with", &with_commit, "commit",
                        "print only branches that contain the commit",
-                       PARSE_OPT_HIDDEN, opt_parse_with_commit,
+                       PARSE_OPT_HIDDEN | PARSE_OPT_LASTARG_DEFAULT,
+                       opt_parse_with_commit, (intptr_t) "HEAD",
                },
                OPT__ABBREV(&abbrev),
 
@@ -444,18 +482,27 @@ 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)"),
+               {
+                       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(),
        };
 
-       git_config(git_branch_config);
+       git_config(git_branch_config, NULL);
 
        if (branch_use_color == -1)
                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)
@@ -468,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);