Skip to content

Commit 6c3daa2

Browse files
committed
Merge branch 'ra/decorate-limit-refs'
The tagnames "git log --decorate" uses to annotate the commits can now be limited to subset of available refs with the two additional options, --decorate-refs[-exclude]=<pattern>. * ra/decorate-limit-refs: log: add option to choose which refs to decorate
2 parents 721cc43 + 65516f5 commit 6c3daa2

File tree

9 files changed

+232
-11
lines changed

9 files changed

+232
-11
lines changed

Documentation/git-log.txt

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ OPTIONS
3838
are shown as if 'short' were given, otherwise no ref names are
3939
shown. The default option is 'short'.
4040

41+
--decorate-refs=<pattern>::
42+
--decorate-refs-exclude=<pattern>::
43+
If no `--decorate-refs` is given, pretend as if all refs were
44+
included. For each candidate, do not use it for decoration if it
45+
matches any patterns given to `--decorate-refs-exclude` or if it
46+
doesn't match any of the patterns given to `--decorate-refs`.
47+
4148
--source::
4249
Print out the ref name given on the command line by which each
4350
commit was reached.

builtin/log.c

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,11 +142,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
142142
struct userformat_want w;
143143
int quiet = 0, source = 0, mailmap = 0;
144144
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
145+
static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
146+
static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
147+
struct decoration_filter decoration_filter = {&decorate_refs_include,
148+
&decorate_refs_exclude};
145149

146150
const struct option builtin_log_options[] = {
147151
OPT__QUIET(&quiet, N_("suppress diff output")),
148152
OPT_BOOL(0, "source", &source, N_("show source")),
149153
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
154+
OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
155+
N_("pattern"), N_("only decorate refs that match <pattern>")),
156+
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
157+
N_("pattern"), N_("do not decorate refs that match <pattern>")),
150158
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
151159
PARSE_OPT_OPTARG, decorate_callback},
152160
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
@@ -205,7 +213,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
205213

206214
if (decoration_style) {
207215
rev->show_decorations = 1;
208-
load_ref_decorations(decoration_style);
216+
load_ref_decorations(&decoration_filter, decoration_style);
209217
}
210218

211219
if (rev->line_level_traverse)

log-tree.c

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -94,8 +94,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
9494
{
9595
struct object *obj;
9696
enum decoration_type type = DECORATION_NONE;
97+
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
9798

98-
assert(cb_data == NULL);
99+
if (filter && !ref_filter_match(refname,
100+
filter->include_ref_pattern,
101+
filter->exclude_ref_pattern))
102+
return 0;
99103

100104
if (starts_with(refname, git_replace_ref_base)) {
101105
struct object_id original_oid;
@@ -148,15 +152,23 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
148152
return 0;
149153
}
150154

151-
void load_ref_decorations(int flags)
155+
void load_ref_decorations(struct decoration_filter *filter, int flags)
152156
{
153157
if (!decoration_loaded) {
154-
158+
if (filter) {
159+
struct string_list_item *item;
160+
for_each_string_list_item(item, filter->exclude_ref_pattern) {
161+
normalize_glob_ref(item, NULL, item->string);
162+
}
163+
for_each_string_list_item(item, filter->include_ref_pattern) {
164+
normalize_glob_ref(item, NULL, item->string);
165+
}
166+
}
155167
decoration_loaded = 1;
156168
decoration_flags = flags;
157-
for_each_ref(add_ref_decoration, NULL);
158-
head_ref(add_ref_decoration, NULL);
159-
for_each_commit_graft(add_graft_decoration, NULL);
169+
for_each_ref(add_ref_decoration, filter);
170+
head_ref(add_ref_decoration, filter);
171+
for_each_commit_graft(add_graft_decoration, filter);
160172
}
161173
}
162174

log-tree.h

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ struct log_info {
77
struct commit *commit, *parent;
88
};
99

10+
struct decoration_filter {
11+
struct string_list *include_ref_pattern, *exclude_ref_pattern;
12+
};
13+
1014
int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
1115
void init_log_tree_opt(struct rev_info *);
1216
int log_tree_diff_flush(struct rev_info *);
@@ -24,7 +28,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit);
2428
void log_write_email_headers(struct rev_info *opt, struct commit *commit,
2529
const char **extra_headers_p,
2630
int *need_8bit_cte_p);
27-
void load_ref_decorations(int flags);
31+
void load_ref_decorations(struct decoration_filter *filter, int flags);
2832

2933
#define FORMAT_PATCH_NAME_MAX 64
3034
void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);

pretty.c

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1186,11 +1186,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
11861186
strbuf_addstr(sb, get_revision_mark(NULL, commit));
11871187
return 1;
11881188
case 'd':
1189-
load_ref_decorations(DECORATE_SHORT_REFS);
1189+
load_ref_decorations(NULL, DECORATE_SHORT_REFS);
11901190
format_decorations(sb, commit, c->auto_color);
11911191
return 1;
11921192
case 'D':
1193-
load_ref_decorations(DECORATE_SHORT_REFS);
1193+
load_ref_decorations(NULL, DECORATE_SHORT_REFS);
11941194
format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
11951195
return 1;
11961196
case 'g': /* reflog info */

refs.c

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,50 @@ int ref_exists(const char *refname)
242242
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
243243
}
244244

245+
static int match_ref_pattern(const char *refname,
246+
const struct string_list_item *item)
247+
{
248+
int matched = 0;
249+
if (item->util == NULL) {
250+
if (!wildmatch(item->string, refname, 0))
251+
matched = 1;
252+
} else {
253+
const char *rest;
254+
if (skip_prefix(refname, item->string, &rest) &&
255+
(!*rest || *rest == '/'))
256+
matched = 1;
257+
}
258+
return matched;
259+
}
260+
261+
int ref_filter_match(const char *refname,
262+
const struct string_list *include_patterns,
263+
const struct string_list *exclude_patterns)
264+
{
265+
struct string_list_item *item;
266+
267+
if (exclude_patterns && exclude_patterns->nr) {
268+
for_each_string_list_item(item, exclude_patterns) {
269+
if (match_ref_pattern(refname, item))
270+
return 0;
271+
}
272+
}
273+
274+
if (include_patterns && include_patterns->nr) {
275+
int found = 0;
276+
for_each_string_list_item(item, include_patterns) {
277+
if (match_ref_pattern(refname, item)) {
278+
found = 1;
279+
break;
280+
}
281+
}
282+
283+
if (!found)
284+
return 0;
285+
}
286+
return 1;
287+
}
288+
245289
static int filter_refs(const char *refname, const struct object_id *oid,
246290
int flags, void *data)
247291
{
@@ -369,6 +413,27 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
369413
return ret;
370414
}
371415

416+
void normalize_glob_ref(struct string_list_item *item, const char *prefix,
417+
const char *pattern)
418+
{
419+
struct strbuf normalized_pattern = STRBUF_INIT;
420+
421+
if (*pattern == '/')
422+
BUG("pattern must not start with '/'");
423+
424+
if (prefix) {
425+
strbuf_addstr(&normalized_pattern, prefix);
426+
}
427+
else if (!starts_with(pattern, "refs/"))
428+
strbuf_addstr(&normalized_pattern, "refs/");
429+
strbuf_addstr(&normalized_pattern, pattern);
430+
strbuf_strip_suffix(&normalized_pattern, "/");
431+
432+
item->string = strbuf_detach(&normalized_pattern, NULL);
433+
item->util = has_glob_specials(pattern) ? NULL : item->string;
434+
strbuf_release(&normalized_pattern);
435+
}
436+
372437
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
373438
const char *prefix, void *cb_data)
374439
{

refs.h

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -312,6 +312,30 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
312312
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
313313
int for_each_rawref(each_ref_fn fn, void *cb_data);
314314

315+
/*
316+
* Normalizes partial refs to their fully qualified form.
317+
* Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
318+
* <prefix> will default to 'refs/' if NULL.
319+
*
320+
* item.string will be set to the result.
321+
* item.util will be set to NULL if <pattern> contains glob characters, or
322+
* non-NULL if it doesn't.
323+
*/
324+
void normalize_glob_ref(struct string_list_item *item, const char *prefix,
325+
const char *pattern);
326+
327+
/*
328+
* Returns 0 if refname matches any of the exclude_patterns, or if it doesn't
329+
* match any of the include_patterns. Returns 1 otherwise.
330+
*
331+
* If pattern list is NULL or empty, matching against that list is skipped.
332+
* This has the effect of matching everything by default, unless the user
333+
* specifies rules otherwise.
334+
*/
335+
int ref_filter_match(const char *refname,
336+
const struct string_list *include_patterns,
337+
const struct string_list *exclude_patterns);
338+
315339
static inline const char *has_glob_specials(const char *pattern)
316340
{
317341
return strpbrk(pattern, "?*[");

revision.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1832,7 +1832,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
18321832
revs->simplify_by_decoration = 1;
18331833
revs->limited = 1;
18341834
revs->prune = 1;
1835-
load_ref_decorations(DECORATE_SHORT_REFS);
1835+
load_ref_decorations(NULL, DECORATE_SHORT_REFS);
18361836
} else if (!strcmp(arg, "--date-order")) {
18371837
revs->sort_order = REV_SORT_BY_COMMIT_DATE;
18381838
revs->topo_order = 1;

t/t4202-log.sh

Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -737,6 +737,107 @@ test_expect_success 'log.decorate configuration' '
737737
738738
'
739739

740+
test_expect_success 'decorate-refs with glob' '
741+
cat >expect.decorate <<-\EOF &&
742+
Merge-tag-reach
743+
Merge-tags-octopus-a-and-octopus-b
744+
seventh
745+
octopus-b (octopus-b)
746+
octopus-a (octopus-a)
747+
reach
748+
EOF
749+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
750+
--decorate-refs="heads/octopus*" >actual &&
751+
test_cmp expect.decorate actual
752+
'
753+
754+
test_expect_success 'decorate-refs without globs' '
755+
cat >expect.decorate <<-\EOF &&
756+
Merge-tag-reach
757+
Merge-tags-octopus-a-and-octopus-b
758+
seventh
759+
octopus-b
760+
octopus-a
761+
reach (tag: reach)
762+
EOF
763+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
764+
--decorate-refs="tags/reach" >actual &&
765+
test_cmp expect.decorate actual
766+
'
767+
768+
test_expect_success 'multiple decorate-refs' '
769+
cat >expect.decorate <<-\EOF &&
770+
Merge-tag-reach
771+
Merge-tags-octopus-a-and-octopus-b
772+
seventh
773+
octopus-b (octopus-b)
774+
octopus-a (octopus-a)
775+
reach (tag: reach)
776+
EOF
777+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
778+
--decorate-refs="heads/octopus*" \
779+
--decorate-refs="tags/reach" >actual &&
780+
test_cmp expect.decorate actual
781+
'
782+
783+
test_expect_success 'decorate-refs-exclude with glob' '
784+
cat >expect.decorate <<-\EOF &&
785+
Merge-tag-reach (HEAD -> master)
786+
Merge-tags-octopus-a-and-octopus-b
787+
seventh (tag: seventh)
788+
octopus-b (tag: octopus-b)
789+
octopus-a (tag: octopus-a)
790+
reach (tag: reach, reach)
791+
EOF
792+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
793+
--decorate-refs-exclude="heads/octopus*" >actual &&
794+
test_cmp expect.decorate actual
795+
'
796+
797+
test_expect_success 'decorate-refs-exclude without globs' '
798+
cat >expect.decorate <<-\EOF &&
799+
Merge-tag-reach (HEAD -> master)
800+
Merge-tags-octopus-a-and-octopus-b
801+
seventh (tag: seventh)
802+
octopus-b (tag: octopus-b, octopus-b)
803+
octopus-a (tag: octopus-a, octopus-a)
804+
reach (reach)
805+
EOF
806+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
807+
--decorate-refs-exclude="tags/reach" >actual &&
808+
test_cmp expect.decorate actual
809+
'
810+
811+
test_expect_success 'multiple decorate-refs-exclude' '
812+
cat >expect.decorate <<-\EOF &&
813+
Merge-tag-reach (HEAD -> master)
814+
Merge-tags-octopus-a-and-octopus-b
815+
seventh (tag: seventh)
816+
octopus-b (tag: octopus-b)
817+
octopus-a (tag: octopus-a)
818+
reach (reach)
819+
EOF
820+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
821+
--decorate-refs-exclude="heads/octopus*" \
822+
--decorate-refs-exclude="tags/reach" >actual &&
823+
test_cmp expect.decorate actual
824+
'
825+
826+
test_expect_success 'decorate-refs and decorate-refs-exclude' '
827+
cat >expect.decorate <<-\EOF &&
828+
Merge-tag-reach (master)
829+
Merge-tags-octopus-a-and-octopus-b
830+
seventh
831+
octopus-b
832+
octopus-a
833+
reach (reach)
834+
EOF
835+
git log -n6 --decorate=short --pretty="tformat:%f%d" \
836+
--decorate-refs="heads/*" \
837+
--decorate-refs-exclude="heads/oc*" >actual &&
838+
test_cmp expect.decorate actual
839+
'
840+
740841
test_expect_success 'log.decorate config parsing' '
741842
git log --oneline --decorate=full >expect.full &&
742843
git log --oneline --decorate=short >expect.short &&

0 commit comments

Comments
 (0)