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.
This commit is contained in:
Junio C Hamano 2007-12-01 13:58:54 -08:00
Родитель c4d48ab5af 3f7dfe77b7
Коммит 5fa00a4dcf
5 изменённых файлов: 134 добавлений и 5 удалений

Просмотреть файл

@ -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>.

Просмотреть файл

@ -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))

Просмотреть файл

@ -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"

Просмотреть файл

@ -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.

58
t/t3201-branch-contains.sh Executable file
Просмотреть файл

@ -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