summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLibravatar Junio C Hamano <gitster@pobox.com>2007-12-01 13:58:54 -0800
committerLibravatar Junio C Hamano <gitster@pobox.com>2007-12-01 13:58:54 -0800
commit5fa00a4dcf5d283c0af50b246b60977c66193c63 (patch)
tree366a867165f16aec0c35c4c61ffaa775f5be2f79
parentMerge branch 'cr/tag-options' (diff)
parentgit-branch --contains: doc and test (diff)
downloadtgif-5fa00a4dcf5d283c0af50b246b60977c66193c63.tar.xz
Merge branch 'jc/branch-contains'
* jc/branch-contains: git-branch --contains: doc and test git-branch --contains=commit parse-options: Allow to hide options from the default usage.
-rw-r--r--Documentation/git-branch.txt4
-rw-r--r--builtin-branch.c57
-rw-r--r--parse-options.c17
-rw-r--r--parse-options.h3
-rwxr-xr-xt/t3201-branch-contains.sh58
5 files changed, 134 insertions, 5 deletions
diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt
index f87b6968b4..d3f21c7975 100644
--- a/Documentation/git-branch.txt
+++ b/Documentation/git-branch.txt
@@ -10,6 +10,7 @@ SYNOPSIS
[verse]
'git-branch' [--color | --no-color] [-r | -a]
[-v [--abbrev=<length> | --no-abbrev]]
+ [--contains <commit>]
'git-branch' [--track | --no-track] [-l] [-f] <branchname> [<start-point>]
'git-branch' (-m | -M) [<oldbranch>] <newbranch>
'git-branch' (-d | -D) [-r] <branchname>...
@@ -20,6 +21,9 @@ 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 <commit>`, shows only the branches that
+contains the named commit (in other words, the branches whose
+tip commits are descendant of the named commit).
In its second form, a new branch named <branchname> will be created.
It will start out with a head equal to the one given as <start-point>.
diff --git a/builtin-branch.c b/builtin-branch.c
index 2694c9cf49..c64768beb2 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -184,9 +184,30 @@ struct ref_item {
struct ref_list {
int index, alloc, maxwidth;
struct ref_item *list;
+ struct commit_list *with_commit;
int kinds;
};
+static int has_commit(const unsigned char *sha1, struct commit_list *with_commit)
+{
+ struct commit *commit;
+
+ if (!with_commit)
+ return 1;
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (!commit)
+ return 0;
+ while (with_commit) {
+ struct commit *other;
+
+ other = with_commit->item;
+ with_commit = with_commit->next;
+ if (in_merge_bases(other, &commit, 1))
+ return 1;
+ }
+ return 0;
+}
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct ref_list *ref_list = (struct ref_list*)(cb_data);
@@ -206,6 +227,10 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
refname += 10;
}
+ /* Filter with with_commit if specified */
+ if (!has_commit(sha1, ref_list->with_commit))
+ return 0;
+
/* Don't add types the caller doesn't want */
if ((kind & ref_list->kinds) == 0)
return 0;
@@ -296,19 +321,20 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
}
}
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
+static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
{
int i;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
ref_list.kinds = kinds;
+ ref_list.with_commit = with_commit;
for_each_ref(append_ref, &ref_list);
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached) {
+ if (detached && has_commit(head_sha1, with_commit)) {
struct ref_item item;
item.name = xstrdup("(no branch)");
item.kind = REF_LOCAL_BRANCH;
@@ -505,12 +531,29 @@ static void rename_branch(const char *oldname, const char *newname, int force)
die("Branch is renamed, but update of config-file failed");
}
+static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+
+ if (!arg)
+ return -1;
+ if (get_sha1(arg, sha1))
+ die("malformed object name %s", arg);
+ commit = lookup_commit_reference(sha1);
+ if (!commit)
+ die("no such commit %s", arg);
+ commit_list_insert(commit, opt->value);
+ return 0;
+}
+
int cmd_branch(int argc, const char **argv, const char *prefix)
{
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;
+ struct commit_list *with_commit = NULL;
struct option options[] = {
OPT_GROUP("Generic options"),
@@ -519,6 +562,14 @@ 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, "with", &with_commit, "commit",
+ "print only branches that contain the commit",
+ PARSE_OPT_HIDDEN, opt_parse_with_commit,
+ },
OPT__ABBREV(&abbrev),
OPT_GROUP("Specific git-branch actions:"),
@@ -554,7 +605,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
if (delete)
return delete_branches(argc, argv, delete > 1, kinds);
else if (argc == 0)
- print_ref_list(kinds, detached, verbose, abbrev);
+ print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
diff --git a/parse-options.c b/parse-options.c
index d3e608ac45..e12b428c0a 100644
--- a/parse-options.c
+++ b/parse-options.c
@@ -216,6 +216,9 @@ is_abbreviated:
return error("unknown option `%s'", arg);
}
+static NORETURN void usage_with_options_internal(const char * const *,
+ const struct option *, int);
+
int parse_options(int argc, const char **argv, const struct option *options,
const char * const usagestr[], int flags)
{
@@ -249,6 +252,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
break;
}
+ if (!strcmp(arg + 2, "help-all"))
+ usage_with_options_internal(usagestr, options, 1);
if (!strcmp(arg + 2, "help"))
usage_with_options(usagestr, options);
if (parse_long_opt(&args, arg + 2, options))
@@ -263,8 +268,8 @@ int parse_options(int argc, const char **argv, const struct option *options,
#define USAGE_OPTS_WIDTH 24
#define USAGE_GAP 2
-void usage_with_options(const char * const *usagestr,
- const struct option *opts)
+void usage_with_options_internal(const char * const *usagestr,
+ const struct option *opts, int full)
{
fprintf(stderr, "usage: %s\n", *usagestr++);
while (*usagestr && **usagestr)
@@ -285,6 +290,8 @@ void usage_with_options(const char * const *usagestr,
fprintf(stderr, "%s\n", opts->help);
continue;
}
+ if (!full && (opts->flags & PARSE_OPT_HIDDEN))
+ continue;
pos = fprintf(stderr, " ");
if (opts->short_name)
@@ -335,6 +342,12 @@ void usage_with_options(const char * const *usagestr,
exit(129);
}
+void usage_with_options(const char * const *usagestr,
+ const struct option *opts)
+{
+ usage_with_options_internal(usagestr, opts, 0);
+}
+
/*----- some often used options -----*/
#include "cache.h"
diff --git a/parse-options.h b/parse-options.h
index a8760ac4b2..102ac31fb7 100644
--- a/parse-options.h
+++ b/parse-options.h
@@ -24,6 +24,7 @@ enum parse_opt_option_flags {
PARSE_OPT_OPTARG = 1,
PARSE_OPT_NOARG = 2,
PARSE_OPT_NONEG = 4,
+ PARSE_OPT_HIDDEN = 8,
};
struct option;
@@ -57,6 +58,8 @@ typedef int parse_opt_cb(const struct option *, const char *arg, int unset);
* PARSE_OPT_OPTARG: says that the argument is optionnal (not for BOOLEANs)
* PARSE_OPT_NOARG: says that this option takes no argument, for CALLBACKs
* PARSE_OPT_NONEG: says that this option cannot be negated
+ * PARSE_OPT_HIDDEN this option is skipped in the default usage, showed in
+ * the long one.
*
* `callback`::
* pointer to the callback to use for OPTION_CALLBACK.
diff --git a/t/t3201-branch-contains.sh b/t/t3201-branch-contains.sh
new file mode 100755
index 0000000000..9ef593f0e1
--- /dev/null
+++ b/t/t3201-branch-contains.sh
@@ -0,0 +1,58 @@
+#!/bin/sh
+
+test_description='branch --contains <commit>'
+
+. ./test-lib.sh
+
+test_expect_success setup '
+
+ >file &&
+ git add file &&
+ test_tick &&
+ git commit -m initial &&
+ git branch side &&
+
+ echo 1 >file &&
+ test_tick &&
+ git commit -a -m "second on master" &&
+
+ git checkout side &&
+ echo 1 >file &&
+ test_tick &&
+ git commit -a -m "second on side" &&
+
+ git merge master
+
+'
+
+test_expect_success 'branch --contains=master' '
+
+ git branch --contains=master >actual &&
+ {
+ echo " master" && echo "* side"
+ } >expect &&
+ diff -u expect actual
+
+'
+
+test_expect_success 'branch --contains master' '
+
+ git branch --contains master >actual &&
+ {
+ echo " master" && echo "* side"
+ } >expect &&
+ diff -u expect actual
+
+'
+
+test_expect_success 'branch --contains=side' '
+
+ git branch --contains=side >actual &&
+ {
+ echo "* side"
+ } >expect &&
+ diff -u expect actual
+
+'
+
+test_done