Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns

2017-01-18 Thread Junio C Hamano
Jacob Keller  writes:

> On Wed, Jan 18, 2017 at 12:04 PM, Junio C Hamano  wrote:
>
>> I agree that we cannot short-cut on the first match to make sure
>> that the outcome is stable, but I wondered what should be shown when
>> we do have multiple matches.  Say I gave
>>
>> --refs="v*" --refs="refs/tags/v1.*"
>>
>> and refs/tags/v1.0 matched.  The above code would say we can
>> abbreviate.
>>
>> What is the reason behind this design decision?  Is it because it is
>> clear that the user shows her willingness to accept more compact
>> form by having --refs="v*" that would allow shortening?  If that is
>> the case, I think I agree with the reasoning.  But we probably want
>> to write it down somewhere, because another reasoning, which may
>> also be valid, would call for an opposite behaviour (i.e. the more
>> specific --refs="refs/tags/v1.*" also matched, so let's show that
>> fact by not shortening).
>
> I'm not sure which reasoning makes most sense. Any other opinions?

FWIW, I do think that the design decision to declare that it can be
abbreviated if the ref matches at least one short pattern makes
sense, and I am guessing (because you didn't answer when asked what
_your_ reasoning behind the code was) that you are in agreement.  I
just want it to be spelled out probably as in-code comment, so that
people who later come to this part of the code know why it was
designed that way.  And they can disagree and change it if the end
result is better---I just want to make sure that they can understand
what they are disagreeing when it happens, as opposed to them
scratching their head saying "we do not know why it was chosen to be
done this way, let's make a random change to make it behave
differently".


Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns

2017-01-18 Thread Jacob Keller
On Wed, Jan 18, 2017 at 12:04 PM, Junio C Hamano  wrote:
> Jacob Keller  writes:
>
>> From: Jacob Keller 
>>
>> Teach git name-rev to take a string list of patterns from --refs instead
>> of only a single pattern. The list of patterns will be matched
>> inclusively, such that a ref only needs to match one pattern to be
>> included. If a ref will only be excluded if it does not match any of the
>> patterns.
>
> I think "If a" in the last sentence should be "A".

You are correct, that is a typo.

>
>>  --refs=::
>>   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.
>
> Unlike 1/5, this is facing the end-users, and the last sentence is
> very appropriate.

Yes.

>
>> + 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) {
>> + /*
>> +  * We want to check every pattern even if we already
>> +  * found a match, just in case one of the later
>> +  * patterns could abbreviate the output.
>> +  */
>> + switch (subpath_matches(path, item->string)) {
>> + case -1: /* did not match */
>> + break;
>> + case 0: /* matched fully */
>> + matched = 1;
>> + break;
>> + default: /* matched subpath */
>> + matched = 1;
>> + can_abbreviate_output = 1;
>> + break;
>> + }
>>   }
>
> I agree that we cannot short-cut on the first match to make sure
> that the outcome is stable, but I wondered what should be shown when
> we do have multiple matches.  Say I gave
>
> --refs="v*" --refs="refs/tags/v1.*"
>
> and refs/tags/v1.0 matched.  The above code would say we can
> abbreviate.
>
> What is the reason behind this design decision?  Is it because it is
> clear that the user shows her willingness to accept more compact
> form by having --refs="v*" that would allow shortening?  If that is
> the case, I think I agree with the reasoning.  But we probably want
> to write it down somewhere, because another reasoning, which may
> also be valid, would call for an opposite behaviour (i.e. the more
> specific --refs="refs/tags/v1.*" also matched, so let's show that
> fact by not shortening).
>

I'm not sure which reasoning makes most sense. Any other opinions?

Thanks,
Jake


Re: [PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns

2017-01-18 Thread Junio C Hamano
Jacob Keller  writes:

> From: Jacob Keller 
>
> Teach git name-rev to take a string list of patterns from --refs instead
> of only a single pattern. The list of patterns will be matched
> inclusively, such that a ref only needs to match one pattern to be
> included. If a ref will only be excluded if it does not match any of the
> patterns.

I think "If a" in the last sentence should be "A".

>  --refs=::
>   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.

Unlike 1/5, this is facing the end-users, and the last sentence is
very appropriate.

> + 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) {
> + /*
> +  * We want to check every pattern even if we already
> +  * found a match, just in case one of the later
> +  * patterns could abbreviate the output.
> +  */
> + switch (subpath_matches(path, item->string)) {
> + case -1: /* did not match */
> + break;
> + case 0: /* matched fully */
> + matched = 1;
> + break;
> + default: /* matched subpath */
> + matched = 1;
> + can_abbreviate_output = 1;
> + break;
> + }
>   }

I agree that we cannot short-cut on the first match to make sure
that the outcome is stable, but I wondered what should be shown when
we do have multiple matches.  Say I gave

--refs="v*" --refs="refs/tags/v1.*"

and refs/tags/v1.0 matched.  The above code would say we can
abbreviate.

What is the reason behind this design decision?  Is it because it is
clear that the user shows her willingness to accept more compact
form by having --refs="v*" that would allow shortening?  If that is
the case, I think I agree with the reasoning.  But we probably want
to write it down somewhere, because another reasoning, which may
also be valid, would call for an opposite behaviour (i.e. the more
specific --refs="refs/tags/v1.*" also matched, so let's show that
fact by not shortening).



[PATCH v3 2/5] name-rev: extend --refs to accept multiple patterns

2017-01-17 Thread Jacob Keller
From: Jacob Keller 

Teach git name-rev to take a string list of patterns from --refs instead
of only a single pattern. The list of patterns will be matched
inclusively, such that a ref only needs to match one pattern to be
included. If a ref will only be excluded if it does not match any of the
patterns.

Add tests and documentation for this change. The tests do use
dynamically generated output as part of the expected output because the
expected output is the raw commit-id and it seemed like a bad idea to
hardcode that into a tests expected output.

Signed-off-by: Jacob Keller 
---
 Documentation/git-name-rev.txt   |  4 +++-
 builtin/name-rev.c   | 41 +---
 t/t6007-rev-list-cherry-pick-file.sh | 26 +++
 3 files changed, 58 insertions(+), 13 deletions(-)

diff --git a/Documentation/git-name-rev.txt b/Documentation/git-name-rev.txt
index ca28fb8e2a07..7433627db12d 100644
--- a/Documentation/git-name-rev.txt
+++ b/Documentation/git-name-rev.txt
@@ -26,7 +26,9 @@ OPTIONS
 
 --refs=::
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
diff --git a/builtin/name-rev.c b/builtin/name-rev.c
index cd89d48b65e8..000a2a700ed3 100644
--- a/builtin/name-rev.c
+++ b/builtin/name-rev.c
@@ -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,16 +150,33 @@ 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)) {
-   case -1: /* did not match */
-   return 0;
-   case 0:  /* matched fully */
-   break;
-   default: /* matched subpath */
-   can_abbreviate_output = 1;
-   break;
+   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) {
+   /*
+* We want to check every pattern even if we already
+* found a match, just in case one of the later
+* patterns could abbreviate the output.
+*/
+   switch (subpath_matches(path, item->string)) {
+   case -1: /* did not match */
+   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);
@@ -306,11 +323,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 ")),
OPT_GROUP(""),
OPT_BOOL(0, "all", &all, N_("list all commits reachable from 
all refs")),
diff --git a/t/t6007-rev-list-cherry-pick-file.sh 
b/t/t6007-rev-list-cherry-pick-file.sh
index 1408b608eb03..d9827a6389a3 100755
--- a/t/t6007-rev-list-cherry-pick-file.sh
+++ b/t/t6007-rev-list-cherry-pick-file.sh
@@ -99,6 +99,32 @@ test_expect_success '--cherry-pick bar does not come up 
empty (II)' '