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
This commit is contained in:
Junio C Hamano 2017-12-13 13:28:54 -08:00
Родитель 721cc4314c 65516f586b
Коммит 6c3daa2346
9 изменённых файлов: 232 добавлений и 11 удалений

Просмотреть файл

@ -38,6 +38,13 @@ OPTIONS
are shown as if 'short' were given, otherwise no ref names are are shown as if 'short' were given, otherwise no ref names are
shown. The default option is 'short'. shown. The default option is 'short'.
--decorate-refs=<pattern>::
--decorate-refs-exclude=<pattern>::
If no `--decorate-refs` is given, pretend as if all refs were
included. For each candidate, do not use it for decoration if it
matches any patterns given to `--decorate-refs-exclude` or if it
doesn't match any of the patterns given to `--decorate-refs`.
--source:: --source::
Print out the ref name given on the command line by which each Print out the ref name given on the command line by which each
commit was reached. commit was reached.

Просмотреть файл

@ -142,11 +142,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
struct userformat_want w; struct userformat_want w;
int quiet = 0, source = 0, mailmap = 0; int quiet = 0, source = 0, mailmap = 0;
static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP}; static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
static struct string_list decorate_refs_exclude = STRING_LIST_INIT_NODUP;
static struct string_list decorate_refs_include = STRING_LIST_INIT_NODUP;
struct decoration_filter decoration_filter = {&decorate_refs_include,
&decorate_refs_exclude};
const struct option builtin_log_options[] = { const struct option builtin_log_options[] = {
OPT__QUIET(&quiet, N_("suppress diff output")), OPT__QUIET(&quiet, N_("suppress diff output")),
OPT_BOOL(0, "source", &source, N_("show source")), OPT_BOOL(0, "source", &source, N_("show source")),
OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")), OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
N_("pattern"), N_("only decorate refs that match <pattern>")),
OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
N_("pattern"), N_("do not decorate refs that match <pattern>")),
{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"), { OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
PARSE_OPT_OPTARG, decorate_callback}, PARSE_OPT_OPTARG, decorate_callback},
OPT_CALLBACK('L', NULL, &line_cb, "n,m:file", 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,
if (decoration_style) { if (decoration_style) {
rev->show_decorations = 1; rev->show_decorations = 1;
load_ref_decorations(decoration_style); load_ref_decorations(&decoration_filter, decoration_style);
} }
if (rev->line_level_traverse) if (rev->line_level_traverse)

Просмотреть файл

@ -94,8 +94,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
{ {
struct object *obj; struct object *obj;
enum decoration_type type = DECORATION_NONE; enum decoration_type type = DECORATION_NONE;
struct decoration_filter *filter = (struct decoration_filter *)cb_data;
assert(cb_data == NULL); if (filter && !ref_filter_match(refname,
filter->include_ref_pattern,
filter->exclude_ref_pattern))
return 0;
if (starts_with(refname, git_replace_ref_base)) { if (starts_with(refname, git_replace_ref_base)) {
struct object_id original_oid; struct object_id original_oid;
@ -148,15 +152,23 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
return 0; return 0;
} }
void load_ref_decorations(int flags) void load_ref_decorations(struct decoration_filter *filter, int flags)
{ {
if (!decoration_loaded) { if (!decoration_loaded) {
if (filter) {
struct string_list_item *item;
for_each_string_list_item(item, filter->exclude_ref_pattern) {
normalize_glob_ref(item, NULL, item->string);
}
for_each_string_list_item(item, filter->include_ref_pattern) {
normalize_glob_ref(item, NULL, item->string);
}
}
decoration_loaded = 1; decoration_loaded = 1;
decoration_flags = flags; decoration_flags = flags;
for_each_ref(add_ref_decoration, NULL); for_each_ref(add_ref_decoration, filter);
head_ref(add_ref_decoration, NULL); head_ref(add_ref_decoration, filter);
for_each_commit_graft(add_graft_decoration, NULL); for_each_commit_graft(add_graft_decoration, filter);
} }
} }

Просмотреть файл

@ -7,6 +7,10 @@ struct log_info {
struct commit *commit, *parent; struct commit *commit, *parent;
}; };
struct decoration_filter {
struct string_list *include_ref_pattern, *exclude_ref_pattern;
};
int parse_decorate_color_config(const char *var, const char *slot_name, const char *value); int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
void init_log_tree_opt(struct rev_info *); void init_log_tree_opt(struct rev_info *);
int log_tree_diff_flush(struct rev_info *); int log_tree_diff_flush(struct rev_info *);
@ -24,7 +28,7 @@ void show_decorations(struct rev_info *opt, struct commit *commit);
void log_write_email_headers(struct rev_info *opt, struct commit *commit, void log_write_email_headers(struct rev_info *opt, struct commit *commit,
const char **extra_headers_p, const char **extra_headers_p,
int *need_8bit_cte_p); int *need_8bit_cte_p);
void load_ref_decorations(int flags); void load_ref_decorations(struct decoration_filter *filter, int flags);
#define FORMAT_PATCH_NAME_MAX 64 #define FORMAT_PATCH_NAME_MAX 64
void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *); void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);

Просмотреть файл

@ -1186,11 +1186,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
strbuf_addstr(sb, get_revision_mark(NULL, commit)); strbuf_addstr(sb, get_revision_mark(NULL, commit));
return 1; return 1;
case 'd': case 'd':
load_ref_decorations(DECORATE_SHORT_REFS); load_ref_decorations(NULL, DECORATE_SHORT_REFS);
format_decorations(sb, commit, c->auto_color); format_decorations(sb, commit, c->auto_color);
return 1; return 1;
case 'D': case 'D':
load_ref_decorations(DECORATE_SHORT_REFS); load_ref_decorations(NULL, DECORATE_SHORT_REFS);
format_decorations_extended(sb, commit, c->auto_color, "", ", ", ""); format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
return 1; return 1;
case 'g': /* reflog info */ case 'g': /* reflog info */

65
refs.c
Просмотреть файл

@ -242,6 +242,50 @@ int ref_exists(const char *refname)
return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL); return !!resolve_ref_unsafe(refname, RESOLVE_REF_READING, NULL, NULL);
} }
static int match_ref_pattern(const char *refname,
const struct string_list_item *item)
{
int matched = 0;
if (item->util == NULL) {
if (!wildmatch(item->string, refname, 0))
matched = 1;
} else {
const char *rest;
if (skip_prefix(refname, item->string, &rest) &&
(!*rest || *rest == '/'))
matched = 1;
}
return matched;
}
int ref_filter_match(const char *refname,
const struct string_list *include_patterns,
const struct string_list *exclude_patterns)
{
struct string_list_item *item;
if (exclude_patterns && exclude_patterns->nr) {
for_each_string_list_item(item, exclude_patterns) {
if (match_ref_pattern(refname, item))
return 0;
}
}
if (include_patterns && include_patterns->nr) {
int found = 0;
for_each_string_list_item(item, include_patterns) {
if (match_ref_pattern(refname, item)) {
found = 1;
break;
}
}
if (!found)
return 0;
}
return 1;
}
static int filter_refs(const char *refname, const struct object_id *oid, static int filter_refs(const char *refname, const struct object_id *oid,
int flags, void *data) int flags, void *data)
{ {
@ -369,6 +413,27 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
return ret; return ret;
} }
void normalize_glob_ref(struct string_list_item *item, const char *prefix,
const char *pattern)
{
struct strbuf normalized_pattern = STRBUF_INIT;
if (*pattern == '/')
BUG("pattern must not start with '/'");
if (prefix) {
strbuf_addstr(&normalized_pattern, prefix);
}
else if (!starts_with(pattern, "refs/"))
strbuf_addstr(&normalized_pattern, "refs/");
strbuf_addstr(&normalized_pattern, pattern);
strbuf_strip_suffix(&normalized_pattern, "/");
item->string = strbuf_detach(&normalized_pattern, NULL);
item->util = has_glob_specials(pattern) ? NULL : item->string;
strbuf_release(&normalized_pattern);
}
int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
const char *prefix, void *cb_data) const char *prefix, void *cb_data)
{ {

24
refs.h
Просмотреть файл

@ -312,6 +312,30 @@ int for_each_namespaced_ref(each_ref_fn fn, void *cb_data);
int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data); int refs_for_each_rawref(struct ref_store *refs, each_ref_fn fn, void *cb_data);
int for_each_rawref(each_ref_fn fn, void *cb_data); int for_each_rawref(each_ref_fn fn, void *cb_data);
/*
* Normalizes partial refs to their fully qualified form.
* Will prepend <prefix> to the <pattern> if it doesn't start with 'refs/'.
* <prefix> will default to 'refs/' if NULL.
*
* item.string will be set to the result.
* item.util will be set to NULL if <pattern> contains glob characters, or
* non-NULL if it doesn't.
*/
void normalize_glob_ref(struct string_list_item *item, const char *prefix,
const char *pattern);
/*
* Returns 0 if refname matches any of the exclude_patterns, or if it doesn't
* match any of the include_patterns. Returns 1 otherwise.
*
* If pattern list is NULL or empty, matching against that list is skipped.
* This has the effect of matching everything by default, unless the user
* specifies rules otherwise.
*/
int ref_filter_match(const char *refname,
const struct string_list *include_patterns,
const struct string_list *exclude_patterns);
static inline const char *has_glob_specials(const char *pattern) static inline const char *has_glob_specials(const char *pattern)
{ {
return strpbrk(pattern, "?*["); return strpbrk(pattern, "?*[");

Просмотреть файл

@ -1832,7 +1832,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
revs->simplify_by_decoration = 1; revs->simplify_by_decoration = 1;
revs->limited = 1; revs->limited = 1;
revs->prune = 1; revs->prune = 1;
load_ref_decorations(DECORATE_SHORT_REFS); load_ref_decorations(NULL, DECORATE_SHORT_REFS);
} else if (!strcmp(arg, "--date-order")) { } else if (!strcmp(arg, "--date-order")) {
revs->sort_order = REV_SORT_BY_COMMIT_DATE; revs->sort_order = REV_SORT_BY_COMMIT_DATE;
revs->topo_order = 1; revs->topo_order = 1;

Просмотреть файл

@ -737,6 +737,107 @@ test_expect_success 'log.decorate configuration' '
' '
test_expect_success 'decorate-refs with glob' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach
Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b (octopus-b)
octopus-a (octopus-a)
reach
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs="heads/octopus*" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'decorate-refs without globs' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach
Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b
octopus-a
reach (tag: reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs="tags/reach" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'multiple decorate-refs' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach
Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b (octopus-b)
octopus-a (octopus-a)
reach (tag: reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs="heads/octopus*" \
--decorate-refs="tags/reach" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'decorate-refs-exclude with glob' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach (HEAD -> master)
Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b)
octopus-a (tag: octopus-a)
reach (tag: reach, reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs-exclude="heads/octopus*" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'decorate-refs-exclude without globs' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach (HEAD -> master)
Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b, octopus-b)
octopus-a (tag: octopus-a, octopus-a)
reach (reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs-exclude="tags/reach" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'multiple decorate-refs-exclude' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach (HEAD -> master)
Merge-tags-octopus-a-and-octopus-b
seventh (tag: seventh)
octopus-b (tag: octopus-b)
octopus-a (tag: octopus-a)
reach (reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs-exclude="heads/octopus*" \
--decorate-refs-exclude="tags/reach" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'decorate-refs and decorate-refs-exclude' '
cat >expect.decorate <<-\EOF &&
Merge-tag-reach (master)
Merge-tags-octopus-a-and-octopus-b
seventh
octopus-b
octopus-a
reach (reach)
EOF
git log -n6 --decorate=short --pretty="tformat:%f%d" \
--decorate-refs="heads/*" \
--decorate-refs-exclude="heads/oc*" >actual &&
test_cmp expect.decorate actual
'
test_expect_success 'log.decorate config parsing' ' test_expect_success 'log.decorate config parsing' '
git log --oneline --decorate=full >expect.full && git log --oneline --decorate=full >expect.full &&
git log --oneline --decorate=short >expect.short && git log --oneline --decorate=short >expect.short &&