name-rev: extend --refs to accept multiple patterns

Teach git name-rev to take multiple --refs stored as a string list of
patterns. The list of patterns will be matched inclusively, and each ref
only needs to match one pattern to be included. A ref will only be
excluded if it does not match any of the given patterns. Additionally,
if any of the patterns would allow abbreviation, then we will abbreviate
the ref, even if another pattern is more strict and would not have
allowed abbreviation on its own.

Add tests and documentation for this change. The tests expected output
is dynamically generated.  This is in order to avoid hard-coding
a commit object name in the test results (as the expected output is to
simply leave the commit object unnamed).

Signed-off-by: Jacob Keller <jacob.keller@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Jacob Keller 2017-01-18 15:06:05 -08:00 коммит произвёл Junio C Hamano
Родитель 4a68748893
Коммит 290be6674a
3 изменённых файлов: 63 добавлений и 13 удалений

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

@ -26,7 +26,9 @@ OPTIONS
--refs=<pattern>::
Only use refs whose names match a given shell pattern. The pattern
can be one of branch name, tag name or fully qualified ref name.
can be one of branch name, tag name or fully qualified ref name. If
given multiple times, use refs whose names match any of the given shell
patterns. Use `--no-refs` to clear any previous ref patterns given.
--all::
List all commits reachable from all refs

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

@ -108,7 +108,7 @@ static const char *name_ref_abbrev(const char *refname, int shorten_unambiguous)
struct name_ref_data {
int tags_only;
int name_only;
const char *ref_filter;
struct string_list ref_filters;
};
static struct tip_table {
@ -150,18 +150,40 @@ static int name_ref(const char *path, const struct object_id *oid, int flags, vo
if (data->tags_only && !starts_with(path, "refs/tags/"))
return 0;
if (data->ref_filter) {
switch (subpath_matches(path, data->ref_filter)) {
if (data->ref_filters.nr) {
struct string_list_item *item;
int matched = 0;
/* See if any of the patterns match. */
for_each_string_list_item(item, &data->ref_filters) {
/*
* Check all patterns even after finding a match, so
* that we can see if a match with a subpath exists.
* When a user asked for 'refs/tags/v*' and 'v1.*',
* both of which match, the user is showing her
* willingness to accept a shortened output by having
* the 'v1.*' in the acceptable refnames, so we
* shouldn't stop when seeing 'refs/tags/v1.4' matches
* 'refs/tags/v*'. We should show it as 'v1.4'.
*/
switch (subpath_matches(path, item->string)) {
case -1: /* did not match */
return 0;
break;
case 0: /* matched fully */
matched = 1;
break;
default: /* matched subpath */
matched = 1;
can_abbreviate_output = 1;
break;
}
}
/* If none of the patterns matched, stop now */
if (!matched)
return 0;
}
add_to_tip_table(oid->hash, path, can_abbreviate_output);
while (o && o->type == OBJ_TAG) {
@ -306,11 +328,11 @@ int cmd_name_rev(int argc, const char **argv, const char *prefix)
{
struct object_array revs = OBJECT_ARRAY_INIT;
int all = 0, transform_stdin = 0, allow_undefined = 1, always = 0, peel_tag = 0;
struct name_ref_data data = { 0, 0, NULL };
struct name_ref_data data = { 0, 0, STRING_LIST_INIT_NODUP };
struct option opts[] = {
OPT_BOOL(0, "name-only", &data.name_only, N_("print only names (no SHA-1)")),
OPT_BOOL(0, "tags", &data.tags_only, N_("only use tags to name the commits")),
OPT_STRING(0, "refs", &data.ref_filter, N_("pattern"),
OPT_STRING_LIST(0, "refs", &data.ref_filters, N_("pattern"),
N_("only use refs matching <pattern>")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from all refs")),

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

@ -99,6 +99,32 @@ test_expect_success '--cherry-pick bar does not come up empty (II)' '
test_cmp actual.named expect
'
test_expect_success 'name-rev multiple --refs combine inclusive' '
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" \
<actual >actual.named &&
test_cmp actual.named expect
'
cat >expect <<EOF
<tags/F
EOF
test_expect_success 'name-rev --refs excludes non-matched patterns' '
git rev-list --left-right --right-only --cherry-pick F...E -- bar >>expect &&
git rev-list --left-right --cherry-pick F...E -- bar >actual &&
git name-rev --stdin --name-only --refs="*tags/F" \
<actual >actual.named &&
test_cmp actual.named expect
'
test_expect_success 'name-rev --no-refs clears the refs list' '
git rev-list --left-right --cherry-pick F...E -- bar >expect &&
git name-rev --stdin --name-only --refs="*tags/F" --refs="*tags/E" --no-refs --refs="*tags/G" \
<expect >actual &&
test_cmp actual expect
'
cat >expect <<EOF
+tags/F
=tags/D