Skip to content
This repository was archived by the owner on Nov 9, 2017. It is now read-only.

Commit 10167eb

Browse files
committed
Merge branch 'jc/ref-excludes'
People often wished a way to tell "git log --branches" (and "git log --remotes --not --branches") to exclude some local branches from the expansion of "--branches" (similarly for "--tags", "--all" and "--glob=<pattern>"). Now they have one. * jc/ref-excludes: rev-parse: introduce --exclude=<glob> to tame wildcards rev-list --exclude: export add/clear-ref-exclusion and ref-excluded API rev-list --exclude: tests document --exclude option revision: introduce --exclude=<glob> to tame wildcards
2 parents 3576f11 + 9dc01bf commit 10167eb

File tree

6 files changed

+155
-2
lines changed

6 files changed

+155
-2
lines changed

Documentation/git-rev-parse.txt

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -177,6 +177,20 @@ shown. If the pattern does not contain a globbing character (`?`,
177177
character (`?`, `*`, or `[`), it is turned into a prefix
178178
match by appending `/*`.
179179

180+
--exclude=<glob-pattern>::
181+
Do not include refs matching '<glob-pattern>' that the next `--all`,
182+
`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise
183+
consider. Repetitions of this option accumulate exclusion patterns
184+
up to the next `--all`, `--branches`, `--tags`, `--remotes`, or
185+
`--glob` option (other options or arguments do not clear
186+
accumlated patterns).
187+
+
188+
The patterns given should not begin with `refs/heads`, `refs/tags`, or
189+
`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,
190+
respectively, and they must begin with `refs/` when applied to `--glob`
191+
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
192+
explicitly.
193+
180194
--disambiguate=<prefix>::
181195
Show every object whose name begins with the given prefix.
182196
The <prefix> must be at least 4 hexadecimal digits long to

Documentation/rev-list-options.txt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,21 @@ parents) and `--max-parents=-1` (negative numbers denote no upper limit).
153153
is automatically prepended if missing. If pattern lacks '?', '{asterisk}',
154154
or '[', '/{asterisk}' at the end is implied.
155155

156+
--exclude=<glob-pattern>::
157+
158+
Do not include refs matching '<glob-pattern>' that the next `--all`,
159+
`--branches`, `--tags`, `--remotes`, or `--glob` would otherwise
160+
consider. Repetitions of this option accumulate exclusion patterns
161+
up to the next `--all`, `--branches`, `--tags`, `--remotes`, or
162+
`--glob` option (other options or arguments do not clear
163+
accumlated patterns).
164+
+
165+
The patterns given should not begin with `refs/heads`, `refs/tags`, or
166+
`refs/remotes` when applied to `--branches`, `--tags`, or `--remotes`,
167+
respectively, and they must begin with `refs/` when applied to `--glob`
168+
or `--all`. If a trailing '/{asterisk}' is intended, it must be given
169+
explicitly.
170+
156171
--ignore-missing::
157172
Upon seeing an invalid object name in the input, pretend as if
158173
the bad input was not given.

builtin/rev-parse.c

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,8 @@
99
#include "quote.h"
1010
#include "builtin.h"
1111
#include "parse-options.h"
12+
#include "diff.h"
13+
#include "revision.h"
1214

1315
#define DO_REVS 1
1416
#define DO_NOREV 2
@@ -31,6 +33,7 @@ static int abbrev_ref_strict;
3133
static int output_sq;
3234

3335
static int stuck_long;
36+
static struct string_list *ref_excludes;
3437

3538
/*
3639
* Some arguments are relevant "revision" arguments,
@@ -187,6 +190,8 @@ static int show_default(void)
187190

188191
static int show_reference(const char *refname, const unsigned char *sha1, int flag, void *cb_data)
189192
{
193+
if (ref_excluded(ref_excludes, refname))
194+
return 0;
190195
show_rev(NORMAL, sha1, refname);
191196
return 0;
192197
}
@@ -625,32 +630,43 @@ int cmd_rev_parse(int argc, const char **argv, const char *prefix)
625630
if (!prefixcmp(arg, "--branches=")) {
626631
for_each_glob_ref_in(show_reference, arg + 11,
627632
"refs/heads/", NULL);
633+
clear_ref_exclusion(&ref_excludes);
628634
continue;
629635
}
630636
if (!strcmp(arg, "--branches")) {
631637
for_each_branch_ref(show_reference, NULL);
638+
clear_ref_exclusion(&ref_excludes);
632639
continue;
633640
}
634641
if (!prefixcmp(arg, "--tags=")) {
635642
for_each_glob_ref_in(show_reference, arg + 7,
636643
"refs/tags/", NULL);
644+
clear_ref_exclusion(&ref_excludes);
637645
continue;
638646
}
639647
if (!strcmp(arg, "--tags")) {
640648
for_each_tag_ref(show_reference, NULL);
649+
clear_ref_exclusion(&ref_excludes);
641650
continue;
642651
}
643652
if (!prefixcmp(arg, "--glob=")) {
644653
for_each_glob_ref(show_reference, arg + 7, NULL);
654+
clear_ref_exclusion(&ref_excludes);
645655
continue;
646656
}
647657
if (!prefixcmp(arg, "--remotes=")) {
648658
for_each_glob_ref_in(show_reference, arg + 10,
649659
"refs/remotes/", NULL);
660+
clear_ref_exclusion(&ref_excludes);
650661
continue;
651662
}
652663
if (!strcmp(arg, "--remotes")) {
653664
for_each_remote_ref(show_reference, NULL);
665+
clear_ref_exclusion(&ref_excludes);
666+
continue;
667+
}
668+
if (!prefixcmp(arg, "--exclude=")) {
669+
add_ref_exclusion(&ref_excludes, arg + 10);
654670
continue;
655671
}
656672
if (!strcmp(arg, "--local-env-vars")) {

revision.c

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1180,11 +1180,28 @@ struct all_refs_cb {
11801180
const char *name_for_errormsg;
11811181
};
11821182

1183+
int ref_excluded(struct string_list *ref_excludes, const char *path)
1184+
{
1185+
struct string_list_item *item;
1186+
1187+
if (!ref_excludes)
1188+
return 0;
1189+
for_each_string_list_item(item, ref_excludes) {
1190+
if (!fnmatch(item->string, path, 0))
1191+
return 1;
1192+
}
1193+
return 0;
1194+
}
1195+
11831196
static int handle_one_ref(const char *path, const unsigned char *sha1, int flag, void *cb_data)
11841197
{
11851198
struct all_refs_cb *cb = cb_data;
1186-
struct object *object = get_reference(cb->all_revs, path, sha1,
1187-
cb->all_flags);
1199+
struct object *object;
1200+
1201+
if (ref_excluded(cb->all_revs->ref_excludes, path))
1202+
return 0;
1203+
1204+
object = get_reference(cb->all_revs, path, sha1, cb->all_flags);
11881205
add_rev_cmdline(cb->all_revs, object, path, REV_CMD_REF, cb->all_flags);
11891206
add_pending_sha1(cb->all_revs, path, sha1, cb->all_flags);
11901207
return 0;
@@ -1197,6 +1214,24 @@ static void init_all_refs_cb(struct all_refs_cb *cb, struct rev_info *revs,
11971214
cb->all_flags = flags;
11981215
}
11991216

1217+
void clear_ref_exclusion(struct string_list **ref_excludes_p)
1218+
{
1219+
if (*ref_excludes_p) {
1220+
string_list_clear(*ref_excludes_p, 0);
1221+
free(*ref_excludes_p);
1222+
}
1223+
*ref_excludes_p = NULL;
1224+
}
1225+
1226+
void add_ref_exclusion(struct string_list **ref_excludes_p, const char *exclude)
1227+
{
1228+
if (!*ref_excludes_p) {
1229+
*ref_excludes_p = xcalloc(1, sizeof(**ref_excludes_p));
1230+
(*ref_excludes_p)->strdup_strings = 1;
1231+
}
1232+
string_list_append(*ref_excludes_p, exclude);
1233+
}
1234+
12001235
static void handle_refs(const char *submodule, struct rev_info *revs, unsigned flags,
12011236
int (*for_each)(const char *, each_ref_fn, void *))
12021237
{
@@ -1969,33 +2004,44 @@ static int handle_revision_pseudo_opt(const char *submodule,
19692004
if (!strcmp(arg, "--all")) {
19702005
handle_refs(submodule, revs, *flags, for_each_ref_submodule);
19712006
handle_refs(submodule, revs, *flags, head_ref_submodule);
2007+
clear_ref_exclusion(&revs->ref_excludes);
19722008
} else if (!strcmp(arg, "--branches")) {
19732009
handle_refs(submodule, revs, *flags, for_each_branch_ref_submodule);
2010+
clear_ref_exclusion(&revs->ref_excludes);
19742011
} else if (!strcmp(arg, "--bisect")) {
19752012
handle_refs(submodule, revs, *flags, for_each_bad_bisect_ref);
19762013
handle_refs(submodule, revs, *flags ^ (UNINTERESTING | BOTTOM), for_each_good_bisect_ref);
19772014
revs->bisect = 1;
19782015
} else if (!strcmp(arg, "--tags")) {
19792016
handle_refs(submodule, revs, *flags, for_each_tag_ref_submodule);
2017+
clear_ref_exclusion(&revs->ref_excludes);
19802018
} else if (!strcmp(arg, "--remotes")) {
19812019
handle_refs(submodule, revs, *flags, for_each_remote_ref_submodule);
2020+
clear_ref_exclusion(&revs->ref_excludes);
19822021
} else if ((argcount = parse_long_opt("glob", argv, &optarg))) {
19832022
struct all_refs_cb cb;
19842023
init_all_refs_cb(&cb, revs, *flags);
19852024
for_each_glob_ref(handle_one_ref, optarg, &cb);
2025+
clear_ref_exclusion(&revs->ref_excludes);
2026+
return argcount;
2027+
} else if ((argcount = parse_long_opt("exclude", argv, &optarg))) {
2028+
add_ref_exclusion(&revs->ref_excludes, optarg);
19862029
return argcount;
19872030
} else if (!prefixcmp(arg, "--branches=")) {
19882031
struct all_refs_cb cb;
19892032
init_all_refs_cb(&cb, revs, *flags);
19902033
for_each_glob_ref_in(handle_one_ref, arg + 11, "refs/heads/", &cb);
2034+
clear_ref_exclusion(&revs->ref_excludes);
19912035
} else if (!prefixcmp(arg, "--tags=")) {
19922036
struct all_refs_cb cb;
19932037
init_all_refs_cb(&cb, revs, *flags);
19942038
for_each_glob_ref_in(handle_one_ref, arg + 7, "refs/tags/", &cb);
2039+
clear_ref_exclusion(&revs->ref_excludes);
19952040
} else if (!prefixcmp(arg, "--remotes=")) {
19962041
struct all_refs_cb cb;
19972042
init_all_refs_cb(&cb, revs, *flags);
19982043
for_each_glob_ref_in(handle_one_ref, arg + 10, "refs/remotes/", &cb);
2044+
clear_ref_exclusion(&revs->ref_excludes);
19992045
} else if (!strcmp(arg, "--reflog")) {
20002046
handle_reflog(revs, *flags);
20012047
} else if (!strcmp(arg, "--not")) {

revision.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,6 +61,9 @@ struct rev_info {
6161
/* The end-points specified by the end user */
6262
struct rev_cmdline_info cmdline;
6363

64+
/* excluding from --branches, --refs, etc. expansion */
65+
struct string_list *ref_excludes;
66+
6467
/* Basic information */
6568
const char *prefix;
6669
const char *def;
@@ -194,6 +197,11 @@ struct rev_info {
194197
struct saved_parents *saved_parents_slab;
195198
};
196199

200+
extern int ref_excluded(struct string_list *, const char *path);
201+
void clear_ref_exclusion(struct string_list **);
202+
void add_ref_exclusion(struct string_list **, const char *exclude);
203+
204+
197205
#define REV_TREE_SAME 0
198206
#define REV_TREE_NEW 1 /* Only new files */
199207
#define REV_TREE_OLD 2 /* Only files removed */

t/t6018-rev-list-glob.sh

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,18 @@ test_expect_success 'rev-parse --remotes=foo' '
129129
130130
'
131131

132+
test_expect_success 'rev-parse --exclude with --branches' '
133+
compare rev-parse "--exclude=*/* --branches" "master someref subspace-x"
134+
'
135+
136+
test_expect_success 'rev-parse --exclude with --all' '
137+
compare rev-parse "--exclude=refs/remotes/* --all" "--branches --tags"
138+
'
139+
140+
test_expect_success 'rev-parse accumulates multiple --exclude' '
141+
compare rev-parse "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
142+
'
143+
132144
test_expect_success 'rev-list --glob=refs/heads/subspace/*' '
133145
134146
compare rev-list "subspace/one subspace/two" "--glob=refs/heads/subspace/*"
@@ -231,6 +243,48 @@ test_expect_success 'rev-list --remotes=foo' '
231243
232244
'
233245

246+
test_expect_success 'rev-list --exclude with --branches' '
247+
compare rev-list "--exclude=*/* --branches" "master someref subspace-x"
248+
'
249+
250+
test_expect_success 'rev-list --exclude with --all' '
251+
compare rev-list "--exclude=refs/remotes/* --all" "--branches --tags"
252+
'
253+
254+
test_expect_success 'rev-list accumulates multiple --exclude' '
255+
compare rev-list "--exclude=refs/remotes/* --exclude=refs/tags/* --all" --branches
256+
'
257+
258+
259+
# "git rev-list<ENTER>" is likely to be a bug in the calling script and may
260+
# deserve an error message, but do cases where set of refs programatically
261+
# given using globbing and/or --stdin need to fail with the same error, or
262+
# are we better off reporting a success with no output? The following few
263+
# tests document the current behaviour to remind us that we might want to
264+
# think about this issue.
265+
266+
test_expect_failure 'rev-list may want to succeed with empty output on no input (1)' '
267+
>expect &&
268+
git rev-list --stdin <expect >actual &&
269+
test_cmp expect actual
270+
'
271+
272+
test_expect_failure 'rev-list may want to succeed with empty output on no input (2)' '
273+
>expect &&
274+
git rev-list --exclude=* --all >actual &&
275+
test_cmp expect actual
276+
'
277+
278+
test_expect_failure 'rev-list may want to succeed with empty output on no input (3)' '
279+
(
280+
test_create_repo empty &&
281+
cd empty &&
282+
>expect &&
283+
git rev-list --all >actual &&
284+
test_cmp expect actual
285+
)
286+
'
287+
234288
test_expect_success 'shortlog accepts --glob/--tags/--remotes' '
235289
236290
compare shortlog "subspace/one subspace/two" --branches=subspace &&

0 commit comments

Comments
 (0)