describe: teach --match to handle branches and remotes

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 <max@max630.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Max Kirillov committed Sep 20, 2017 at 04:10 UTC 6d68b2ab78cb670dd474dd61b1b2ec0a407cb9b8
3 files changed +58 -22
Documentation/git-describe.txt
+14 -10
@@ -87,19 +87,23 @@ OPTIONS
87
88 --match <pattern>::
89 Only consider tags matching the given `glob(7)` pattern,
90 - excluding the "refs/tags/" prefix. This can be used to avoid
91 - leaking private tags from the repository. If given multiple times, a
92 - list of patterns will be accumulated, and tags matching any of the
93 - patterns will be considered. Use `--no-match` to clear and reset the
94 - list of patterns.
90 + excluding the "refs/tags/" prefix. If used with `--all`, it also
91 + considers local branches and remote-tracking references matching the
92 + pattern, excluding respectively "refs/heads/" and "refs/remotes/"
93 + prefix; references of other types are never considered. If given
94 + multiple times, a list of patterns will be accumulated, and tags
95 + matching any of the patterns will be considered. Use `--no-match` to
96 + clear and reset the list of patterns.
97
98 --exclude <pattern>::
99 Do not consider tags matching the given `glob(7)` pattern, excluding
98 - the "refs/tags/" prefix. This can be used to narrow the tag space and
99 - find only tags matching some meaningful criteria. If given multiple
100 - times, a list of patterns will be accumulated and tags matching any
101 - of the patterns will be excluded. When combined with --match a tag will
102 - be considered when it matches at least one --match pattern and does not
100 + the "refs/tags/" prefix. If used with `--all`, it also does not consider
101 + local branches and remote-tracking references matching the pattern,
102 + excluding respectively "refs/heads/" and "refs/remotes/" prefix;
103 + references of other types are never considered. If given multiple times,
104 + a list of patterns will be accumulated and tags matching any of the
105 + patterns will be excluded. When combined with --match a tag will be
106 + considered when it matches at least one --match pattern and does not
107 match any of the --exclude patterns. Use `--no-exclude` to clear and
108 reset the list of patterns.
109
builtin/describe.c
+17 -12
@@ -129,13 +129,24 @@ static void add_to_known_names(const char *path,
129
130 static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data)
131 {
132 - int is_tag = starts_with(path, "refs/tags/");
132 + int is_tag = 0;
133 struct object_id peeled;
134 int is_annotated, prio;
135 -
136 - /* Reject anything outside refs/tags/ unless --all */
137 - if (!all && !is_tag)
135 + const char *path_to_match = NULL;
136 +
137 + if (skip_prefix(path, "refs/tags/", &path_to_match)) {
138 + is_tag = 1;
139 + } else if (all) {
140 + if ((exclude_patterns.nr || patterns.nr) &&
141 + !skip_prefix(path, "refs/heads/", &path_to_match) &&
142 + !skip_prefix(path, "refs/remotes/", &path_to_match)) {
143 + /* Only accept reference of known type if there are match/exclude patterns */
144 + return 0;
145 + }
146 + } else {
147 + /* Reject anything outside refs/tags/ unless --all */
148 return 0;
149 + }
150
151 /*
152 * If we're given exclude patterns, first exclude any tag which match
@@ -144,11 +155,8 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
155 if (exclude_patterns.nr) {
156 struct string_list_item *item;
157
147 - if (!is_tag)
148 - return 0;
149 -
158 for_each_string_list_item(item, &exclude_patterns) {
151 - if (!wildmatch(item->string, path + 10, 0))
159 + if (!wildmatch(item->string, path_to_match, 0))
160 return 0;
161 }
162 }
@@ -161,11 +169,8 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi
169 int found = 0;
170 struct string_list_item *item;
171
164 - if (!is_tag)
165 - return 0;
166 -
172 for_each_string_list_item(item, &patterns) {
168 - if (!wildmatch(item->string, path + 10, 0)) {
173 + if (!wildmatch(item->string, path_to_match, 0)) {
174 found = 1;
175 break;
176 }
t/t6120-describe.sh
+27
@@ -190,6 +190,33 @@ check_describe "test1-lightweight-*" --long --tags --match="test1-*" --match="te
190
191 check_describe "test1-lightweight-*" --long --tags --match="test3-*" --match="test1-*" HEAD
192
193 +test_expect_success 'set-up branches' '
194 + git branch branch_A A &&
195 + git branch branch_C c &&
196 + git update-ref refs/remotes/origin/remote_branch_A "A^{commit}" &&
197 + git update-ref refs/remotes/origin/remote_branch_C "c^{commit}" &&
198 + git update-ref refs/original/original_branch_A test-annotated~2
199 +'
200 +
201 +check_describe "heads/branch_A*" --all --match="branch_*" --exclude="branch_C" HEAD
202 +
203 +check_describe "remotes/origin/remote_branch_A*" --all --match="origin/remote_branch_*" --exclude="origin/remote_branch_C" HEAD
204 +
205 +check_describe "original/original_branch_A*" --all test-annotated~1
206 +
207 +test_expect_success '--match does not work for other types' '
208 + test_must_fail git describe --all --match="*original_branch_*" test-annotated~1
209 +'
210 +
211 +test_expect_success '--exclude does not work for other types' '
212 + R=$(git describe --all --exclude="any_pattern_even_not_matching" test-annotated~1) &&
213 + case "$R" in
214 + *original_branch_A*) echo "fail: Found unknown reference $R with --exclude"
215 + false;;
216 + *) echo ok: Found some known type;;
217 + esac
218 +'
219 +
220 test_expect_success 'name-rev with exact tags' '
221 echo A >expect &&
222 tag_object=$(git rev-parse refs/tags/A) &&