When `git describe` uses `--match`, it matches only tags, basically
ignoring the `--all` argument even when it is specified.

Fix it by also matching branch name and $remote_name/$remote_branch_name,
for remote-tracking references, with the specified patterns. Update
documentation accordingly and add tests.

Signed-off-by: Max Kirillov <m...@max630.net>
---
Requires https://public-inbox.org/git/20170916055344.31866-1-...@max630.net/

This extends --match to branches and remote-tracking references. It is in some 
respect
regression, if anybody have used --all and --match together this would find 
another
reference, but since that combination did not make sense anyway probably it is 
not
a big issue.

There are ambiguity with this approach if --match=foo matches tag "foo", or 
branch "foo".
Probably to resolve it there should appear some --match-full option, so that 
--match would mean
full reference name, with prefix. It could be a room for further improvement.

>From documentation I removed the usage example part, mainly to not expand the 
>size too much, but
probably they do not really belong there.
 Documentation/git-describe.txt | 24 ++++++++++++++----------
 builtin/describe.c             | 26 ++++++++++++++++++++++----
 t/t6120-describe.sh            | 18 ++++++++++++++++++
 3 files changed, 54 insertions(+), 14 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index 26f19d3b07..c924c945ba 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -87,19 +87,23 @@ OPTIONS
 
 --match <pattern>::
        Only consider tags matching the given `glob(7)` pattern,
-       excluding the "refs/tags/" prefix.  This can be used to avoid
-       leaking private tags from the repository. If given multiple times, a
-       list of patterns will be accumulated, and tags matching any of the
-       patterns will be considered. Use `--no-match` to clear and reset the
-       list of patterns.
+       excluding the "refs/tags/" prefix. If used with `--all`, it also
+       considers local branches and remote-tracking references matching the
+       pattern, excluding respectively "refs/heads/" and "refs/remotes/"
+       prefix; references of other types are never considered. If given
+       multiple times, a list of patterns will be accumulated, and tags
+       matching any of the patterns will be considered.  Use `--no-match` to
+       clear and reset the list of patterns.
 
 --exclude <pattern>::
        Do not consider tags matching the given `glob(7)` pattern, excluding
-       the "refs/tags/" prefix. This can be used to narrow the tag space and
-       find only tags matching some meaningful criteria. If given multiple
-       times, a list of patterns will be accumulated and tags matching any
-       of the patterns will be excluded. When combined with --match a tag will
-       be considered when it matches at least one --match pattern and does not
+       the "refs/tags/" prefix. If used with `--all`, it also does not consider
+       local branches and remote-tracking references matching the pattern,
+       excluding respectively "refs/heads/" and "refs/remotes/" prefix;
+       references of other types are never considered. If given multiple times,
+       a list of patterns will be accumulated and tags matching any of the
+       patterns will be excluded. When combined with --match a tag will be
+       considered when it matches at least one --match pattern and does not
        match any of the --exclude patterns. Use `--no-exclude` to clear and
        reset the list of patterns.
 
diff --git a/builtin/describe.c b/builtin/describe.c
index 94ff2fba0b..2a2e998063 100644
--- a/builtin/describe.c
+++ b/builtin/describe.c
@@ -124,6 +124,22 @@ static void add_to_known_names(const char *path,
        }
 }
 
+/* Drops prefix. Returns NULL if the path is not expected with current 
settings. */
+static const char *get_path_to_match(int is_tag, int all, const char *path)
+{
+       if (is_tag)
+               return path + 10;
+       else if (all) {
+               if (starts_with(path, "refs/heads/"))
+                       return path + 11; /* "refs/heads/..." */
+               else if (starts_with(path, "refs/remotes/"))
+                       return path + 13; /* "refs/remotes/..." */
+               else
+                       return 0;
+       } else
+               return NULL;
+}
+
 static int get_name(const char *path, const struct object_id *oid, int flag, 
void *cb_data)
 {
        int is_tag = starts_with(path, "refs/tags/");
@@ -140,12 +156,13 @@ static int get_name(const char *path, const struct 
object_id *oid, int flag, voi
         */
        if (exclude_patterns.nr) {
                struct string_list_item *item;
+               const char *path_to_match = get_path_to_match(is_tag, all, 
path);
 
-               if (!is_tag)
+               if (!path_to_match)
                        return 0;
 
                for_each_string_list_item(item, &exclude_patterns) {
-                       if (!wildmatch(item->string, path + 10, 0))
+                       if (!wildmatch(item->string, path_to_match, 0))
                                return 0;
                }
        }
@@ -156,13 +173,14 @@ static int get_name(const char *path, const struct 
object_id *oid, int flag, voi
         */
        if (patterns.nr) {
                int found = 0;
+               const char *path_to_match = get_path_to_match(is_tag, all, 
path);
                struct string_list_item *item;
 
-               if (!is_tag)
+               if (!path_to_match)
                        return 0;
 
                for_each_string_list_item(item, &patterns) {
-                       if (!wildmatch(item->string, path + 10, 0)) {
+                       if (!wildmatch(item->string, path_to_match, 0)) {
                                found = 1;
                                break;
                        }
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 25110ea55d..fac52bd9dc 100755
--- a/t/t6120-describe.sh
+++ b/t/t6120-describe.sh
@@ -190,6 +190,24 @@ check_describe "test1-lightweight-*" --long --tags 
--match="test1-*" --match="te
 
 check_describe "test1-lightweight-*" --long --tags --match="test3-*" 
--match="test1-*" HEAD
 
+test_expect_success 'set-up branches' '
+       git branch branch_A A &&
+       git branch branch_c c &&
+       git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" &&
+       git update-ref refs/remotes/origin/remote_branch_c "c^{commit}" &&
+       git update-ref refs/original/original_branch_A test-annotated~2
+'
+
+check_describe "heads/branch_A*" --all --match="branch_*" --exclude="branch_c" 
HEAD
+
+check_describe "remotes/origin/remote_branch_A*" --all 
--match="origin/remote_branch_*" --exclude="origin/remote_branch_c" HEAD
+
+check_describe "original/original_branch_A*" --all test-annotated~1
+
+test_expect_success '--match does not work for other types' '
+       test_must_fail git describe --all --match="*original_branch_*" 
test-annotated~1
+'
+
 test_expect_success 'name-rev with exact tags' '
        echo A >expect &&
        tag_object=$(git rev-parse refs/tags/A) &&
-- 
2.11.0.1122.gc3fec58.dirty

Reply via email to