From c9f7a793e81f517593383768ec8839dac85368c4 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Thu, 16 Apr 2020 14:15:48 +0000 Subject: [PATCH 1/2] log-tree: make ref_filter_match() a helper method The ref_filter_match() method is defined in refs.h and implemented in refs.c, but is only used by add_ref_decoration() in log-tree.c. Move it into that file as a static helper method. The match_ref_pattern() comes along for the ride. While moving the code, also make a slight adjustment to have ref_filter_match() take a struct decoration_filter pointer instead of multiple string lists. This is non-functional, but will make a later change be much cleaner. The diff is easier to parse when using the --color-moved option. Reported-by: Junio C Hamano Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- log-tree.c | 49 ++++++++++++++++++++++++++++++++++++++++++++++--- refs.c | 44 -------------------------------------------- refs.h | 12 ------------ 3 files changed, 46 insertions(+), 59 deletions(-) diff --git a/log-tree.c b/log-tree.c index 897a90233e..fa6af69da3 100644 --- a/log-tree.c +++ b/log-tree.c @@ -81,6 +81,51 @@ const struct name_decoration *get_name_decoration(const struct object *obj) return lookup_decoration(&name_decoration, obj); } +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; +} + +static int ref_filter_match(const char *refname, + const struct decoration_filter *filter) +{ + struct string_list_item *item; + const struct string_list *exclude_patterns = filter->exclude_ref_pattern; + const struct string_list *include_patterns = filter->include_ref_pattern; + + 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 add_ref_decoration(const char *refname, const struct object_id *oid, int flags, void *cb_data) { @@ -88,9 +133,7 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid, enum decoration_type type = DECORATION_NONE; struct decoration_filter *filter = (struct decoration_filter *)cb_data; - if (filter && !ref_filter_match(refname, - filter->include_ref_pattern, - filter->exclude_ref_pattern)) + if (filter && !ref_filter_match(refname, filter)) return 0; if (starts_with(refname, git_replace_ref_base)) { diff --git a/refs.c b/refs.c index 1ab0bb54d3..28c91d603c 100644 --- a/refs.c +++ b/refs.c @@ -321,50 +321,6 @@ int ref_exists(const char *refname) return refs_ref_exists(get_main_ref_store(the_repository), refname); } -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, int flags, void *data) { diff --git a/refs.h b/refs.h index 545029c6d8..a92d2c74c8 100644 --- a/refs.h +++ b/refs.h @@ -361,18 +361,6 @@ int for_each_rawref(each_ref_fn fn, void *cb_data); 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) { return strpbrk(pattern, "?*["); From a6be5e6764aabd4b418f7f365254518ec44c38d8 Mon Sep 17 00:00:00 2001 From: Derrick Stolee Date: Thu, 16 Apr 2020 14:15:49 +0000 Subject: [PATCH 2/2] log: add log.excludeDecoration config option In 'git log', the --decorate-refs-exclude option appends a pattern to a string_list. This list is used to prevent showing some refs in the decoration output, or even by --simplify-by-decoration. Users may want to use their refs space to store utility refs that should not appear in the decoration output. For example, Scalar [1] runs a background fetch but places the "new" refs inside the refs/scalar/hidden//* refspace instead of refs//* to avoid updating remote refs when the user is not looking. However, these "hidden" refs appear during regular 'git log' queries. A similar idea to use "hidden" refs is under consideration for core Git [2]. Add the 'log.excludeDecoration' config option so users can exclude some refs from decorations by default instead of needing to use --decorate-refs-exclude manually. The config value is multi-valued much like the command-line option. The documentation is careful to point out that the config value can be overridden by the --decorate-refs option, even though --decorate-refs-exclude would always "win" over --decorate-refs. Since the 'log.excludeDecoration' takes lower precedence to --decorate-refs, and --decorate-refs-exclude takes higher precedence, the struct decoration_filter needed another field. This led also to new logic in load_ref_decorations() and ref_filter_match(). There are several tests in t4202-log.sh that test the --decorate-refs-(include|exclude) options, so these are extended. Since the expected output is already stored as a file, most tests could simply replace a "--decorate-refs-exclude" option with an in-line config setting. Other tests involve the precedence of the config option compared to command-line options and needed more modification. [1] https://github.com/microsoft/scalar [2] https://lore.kernel.org/git/77b1da5d3063a2404cd750adfe3bb8be9b6c497d.1585946894.git.gitgitgadget@gmail.com/ Helped-by: Junio C Hamano Signed-off-by: Derrick Stolee Signed-off-by: Junio C Hamano --- Documentation/config/log.txt | 6 +++++ Documentation/git-log.txt | 5 +++- builtin/log.c | 16 ++++++++++- log-tree.c | 24 +++++++++++------ log-tree.h | 4 ++- t/t4202-log.sh | 51 +++++++++++++++++++++++++++++++++++- 6 files changed, 94 insertions(+), 12 deletions(-) diff --git a/Documentation/config/log.txt b/Documentation/config/log.txt index e9e1e397f3..208d5fdcaa 100644 --- a/Documentation/config/log.txt +++ b/Documentation/config/log.txt @@ -18,6 +18,12 @@ log.decorate:: names are shown. This is the same as the `--decorate` option of the `git log`. +log.excludeDecoration:: + Exclude the specified patterns from the log decorations. This is + similar to the `--decorate-refs-exclude` command-line option, but + the config option can be overridden by the `--decorate-refs` + option. + log.follow:: If `true`, `git log` will act as if the `--follow` option was used when a single is given. This has the same limitations as `--follow`, diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt index bed09bb09e..17592234ba 100644 --- a/Documentation/git-log.txt +++ b/Documentation/git-log.txt @@ -43,7 +43,10 @@ OPTIONS 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`. + doesn't match any of the patterns given to `--decorate-refs`. The + `log.excludeDecoration` config option allows excluding refs from + the decorations, but an explicit `--decorate-refs` pattern will + override a match in `log.excludeDecoration`. --source:: Print out the ref name given on the command line by which each diff --git a/builtin/log.c b/builtin/log.c index 83a4a6188e..72192710dc 100644 --- a/builtin/log.c +++ b/builtin/log.c @@ -164,9 +164,11 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, int quiet = 0, source = 0, mailmap; 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_exclude_config = 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}; + &decorate_refs_exclude, + &decorate_refs_exclude_config}; static struct revision_sources revision_sources; const struct option builtin_log_options[] = { @@ -236,7 +238,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix, } if (decoration_style) { + const struct string_list *config_exclude = + repo_config_get_value_multi(the_repository, + "log.excludeDecoration"); + + if (config_exclude) { + struct string_list_item *item; + for_each_string_list_item(item, config_exclude) + string_list_append(&decorate_refs_exclude_config, + item->string); + } + rev->show_decorations = 1; + load_ref_decorations(&decoration_filter, decoration_style); } diff --git a/log-tree.c b/log-tree.c index fa6af69da3..1cfd345861 100644 --- a/log-tree.c +++ b/log-tree.c @@ -103,6 +103,8 @@ static int ref_filter_match(const char *refname, struct string_list_item *item; const struct string_list *exclude_patterns = filter->exclude_ref_pattern; const struct string_list *include_patterns = filter->include_ref_pattern; + const struct string_list *exclude_patterns_config = + filter->exclude_ref_config_pattern; if (exclude_patterns && exclude_patterns->nr) { for_each_string_list_item(item, exclude_patterns) { @@ -112,17 +114,20 @@ static int ref_filter_match(const char *refname, } 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 (match_ref_pattern(refname, item)) + return 1; } - - if (!found) - return 0; + return 0; } + + if (exclude_patterns_config && exclude_patterns_config->nr) { + for_each_string_list_item(item, exclude_patterns_config) { + if (match_ref_pattern(refname, item)) + return 0; + } + } + return 1; } @@ -198,6 +203,9 @@ void load_ref_decorations(struct decoration_filter *filter, int flags) for_each_string_list_item(item, filter->include_ref_pattern) { normalize_glob_ref(item, NULL, item->string); } + for_each_string_list_item(item, filter->exclude_ref_config_pattern) { + normalize_glob_ref(item, NULL, item->string); + } } decoration_loaded = 1; decoration_flags = flags; diff --git a/log-tree.h b/log-tree.h index e668628074..8fa79289ec 100644 --- a/log-tree.h +++ b/log-tree.h @@ -8,7 +8,9 @@ struct log_info { }; struct decoration_filter { - struct string_list *include_ref_pattern, *exclude_ref_pattern; + struct string_list *include_ref_pattern; + struct string_list *exclude_ref_pattern; + struct string_list *exclude_ref_config_pattern; }; int parse_decorate_color_config(const char *var, const char *slot_name, const char *value); diff --git a/t/t4202-log.sh b/t/t4202-log.sh index 5eeb739f3e..f1ea7d97f5 100755 --- a/t/t4202-log.sh +++ b/t/t4202-log.sh @@ -742,8 +742,24 @@ test_expect_success 'decorate-refs with glob' ' octopus-a (octopus-a) reach EOF + cat >expect.no-decorate <<-\EOF && + Merge-tag-reach + Merge-tags-octopus-a-and-octopus-b + seventh + octopus-b + octopus-a + reach + EOF git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs="heads/octopus*" >actual && + test_cmp expect.decorate actual && + git log -n6 --decorate=short --pretty="tformat:%f%d" \ + --decorate-refs-exclude="heads/octopus*" \ + --decorate-refs="heads/octopus*" >actual && + test_cmp expect.no-decorate actual && + git -c log.excludeDecoration="heads/octopus*" log \ + -n6 --decorate=short --pretty="tformat:%f%d" \ + --decorate-refs="heads/octopus*" >actual && test_cmp expect.decorate actual ' @@ -787,6 +803,9 @@ test_expect_success 'decorate-refs-exclude with glob' ' EOF git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs-exclude="heads/octopus*" >actual && + test_cmp expect.decorate actual && + git -c log.excludeDecoration="heads/octopus*" log \ + -n6 --decorate=short --pretty="tformat:%f%d" >actual && test_cmp expect.decorate actual ' @@ -801,6 +820,9 @@ test_expect_success 'decorate-refs-exclude without globs' ' EOF git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs-exclude="tags/reach" >actual && + test_cmp expect.decorate actual && + git -c log.excludeDecoration="tags/reach" log \ + -n6 --decorate=short --pretty="tformat:%f%d" >actual && test_cmp expect.decorate actual ' @@ -816,11 +838,19 @@ test_expect_success 'multiple decorate-refs-exclude' ' 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 && + git -c log.excludeDecoration="heads/octopus*" \ + -c log.excludeDecoration="tags/reach" log \ + -n6 --decorate=short --pretty="tformat:%f%d" >actual && + test_cmp expect.decorate actual && + git -c log.excludeDecoration="heads/octopus*" log \ + --decorate-refs-exclude="tags/reach" \ + -n6 --decorate=short --pretty="tformat:%f%d" >actual && test_cmp expect.decorate actual ' test_expect_success 'decorate-refs and decorate-refs-exclude' ' - cat >expect.decorate <<-\EOF && + cat >expect.no-decorate <<-\EOF && Merge-tag-reach (master) Merge-tags-octopus-a-and-octopus-b seventh @@ -831,6 +861,21 @@ test_expect_success 'decorate-refs and decorate-refs-exclude' ' git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs="heads/*" \ --decorate-refs-exclude="heads/oc*" >actual && + test_cmp expect.no-decorate actual +' + +test_expect_success 'deocrate-refs and log.excludeDecoration' ' + cat >expect.decorate <<-\EOF && + Merge-tag-reach (master) + Merge-tags-octopus-a-and-octopus-b + seventh + octopus-b (octopus-b) + octopus-a (octopus-a) + reach (reach) + EOF + git -c log.excludeDecoration="heads/oc*" log \ + --decorate-refs="heads/*" \ + -n6 --decorate=short --pretty="tformat:%f%d" >actual && test_cmp expect.decorate actual ' @@ -846,6 +891,10 @@ test_expect_success 'decorate-refs-exclude and simplify-by-decoration' ' git log -n6 --decorate=short --pretty="tformat:%f%d" \ --decorate-refs-exclude="*octopus*" \ --simplify-by-decoration >actual && + test_cmp expect.decorate actual && + git -c log.excludeDecoration="*octopus*" log \ + -n6 --decorate=short --pretty="tformat:%f%d" \ + --simplify-by-decoration >actual && test_cmp expect.decorate actual '