зеркало из https://github.com/microsoft/git.git
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:
Коммит
5fa00a4dcf
|
@ -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.
|
||||
|
|
|
@ -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
|
Загрузка…
Ссылка в новой задаче