All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v1 0/2] Add option to git log to choose which refs receive decoration
@ 2017-11-04  0:41 Rafael Ascensão
  2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
                   ` (2 more replies)
  0 siblings, 3 replies; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-04  0:41 UTC (permalink / raw)
  To: git
  Cc: Rafael Ascensão, me, gitster, hjemli, mhagger, pclouds,
	ilari.liusvaara

As suggested by Documentation/SubmittingPatches
Hi, this is my first patch.\n

I basically stumbled on the same issue mentioned here:
https://public-inbox.org/git/xmqqzim1pp4m.fsf@gitster.mtv.corp.google.com/

This patch implements two new command line options for `git log`:
`--decorate-refs=<pattern>` and `--decorate-refs-exlcude=<pattern>`

Both options accept a glob pattern which determines what decorations
commits receive.

At first I considered adding '--trim-decoration', that would filter refs
based on values passed to '--branches=' '--remotes=' '--tags=' and
'--exclude='.

After reading the email, I think it's better to have those two
behaviours decoupled.

I also had plans to add:
(Not sure if others deserve having their own command)
--decorate-branches=
--decorate-remotes=
--decorate-tags=

But was not sure if a 'niche' function like this is worth 5+ command
line options. I personally find that those two are enough.

---
Rafael Ascensão

Rafael Ascensão (2):
  refs: extract function to normalize partial refs
  log: add option to choose which refs to decorate

 Documentation/git-log.txt |  12 ++++++
 builtin/log.c             |  10 ++++-
 log-tree.c                |  37 ++++++++++++++---
 log-tree.h                |   6 ++-
 pretty.c                  |   4 +-
 refs.c                    |  34 +++++++++-------
 refs.h                    |  16 ++++++++
 revision.c                |   2 +-
 t/t4202-log.sh            | 101 ++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 198 insertions(+), 24 deletions(-)

-- 
2.15.0


^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04  0:41 [PATCH v1 0/2] Add option to git log to choose which refs receive decoration Rafael Ascensão
@ 2017-11-04  0:41 ` Rafael Ascensão
  2017-11-04  2:27   ` Junio C Hamano
  2017-11-05 13:42   ` Michael Haggerty
  2017-11-04  0:41 ` [PATCH v1 2/2] log: add option to choose which refs to decorate Rafael Ascensão
  2017-11-21 21:33 ` [PATCH v2] " Rafael Ascensão
  2 siblings, 2 replies; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-04  0:41 UTC (permalink / raw)
  To: git
  Cc: Rafael Ascensão, me, gitster, hjemli, mhagger, pclouds,
	ilari.liusvaara

`for_each_glob_ref_in` has some code built into it that converts
partial refs like 'heads/master' to their full qualified form
'refs/heads/master'. It also assume a trailing '/*' if no glob
characters are present in the pattern.

Extract that logic to its own function which can be reused elsewhere
where the same behaviour is needed, and add an ENSURE_GLOB flag
to toggle if a trailing '/*' is to be appended to the result.

Signed-off-by: Kevin Daudt <me@ikke.info>
Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
---
 refs.c | 34 ++++++++++++++++++++--------------
 refs.h | 16 ++++++++++++++++
 2 files changed, 36 insertions(+), 14 deletions(-)

diff --git a/refs.c b/refs.c
index c590a992f..1e74b48e6 100644
--- a/refs.c
+++ b/refs.c
@@ -369,32 +369,38 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 	return ret;
 }
 
-int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
-	const char *prefix, void *cb_data)
+void normalize_glob_ref(struct strbuf *normalized_pattern, const char *prefix,
+		const char *pattern, int flags)
 {
-	struct strbuf real_pattern = STRBUF_INIT;
-	struct ref_filter filter;
-	int ret;
-
 	if (!prefix && !starts_with(pattern, "refs/"))
-		strbuf_addstr(&real_pattern, "refs/");
+		strbuf_addstr(normalized_pattern, "refs/");
 	else if (prefix)
-		strbuf_addstr(&real_pattern, prefix);
-	strbuf_addstr(&real_pattern, pattern);
+		strbuf_addstr(normalized_pattern, prefix);
+	strbuf_addstr(normalized_pattern, pattern);
 
-	if (!has_glob_specials(pattern)) {
+	if (!has_glob_specials(pattern) && (flags & ENSURE_GLOB)) {
 		/* Append implied '/' '*' if not present. */
-		strbuf_complete(&real_pattern, '/');
+		strbuf_complete(normalized_pattern, '/');
 		/* No need to check for '*', there is none. */
-		strbuf_addch(&real_pattern, '*');
+		strbuf_addch(normalized_pattern, '*');
 	}
+}
+
+int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
+	const char *prefix, void *cb_data)
+{
+	struct strbuf normalized_pattern = STRBUF_INIT;
+	struct ref_filter filter;
+	int ret;
+
+	normalize_glob_ref(&normalized_pattern, prefix, pattern, ENSURE_GLOB);
 
-	filter.pattern = real_pattern.buf;
+	filter.pattern = normalized_pattern.buf;
 	filter.fn = fn;
 	filter.cb_data = cb_data;
 	ret = for_each_ref(filter_refs, &filter);
 
-	strbuf_release(&real_pattern);
+	strbuf_release(&normalized_pattern);
 	return ret;
 }
 
diff --git a/refs.h b/refs.h
index a02b628c8..9f9a8bb27 100644
--- a/refs.h
+++ b/refs.h
@@ -312,6 +312,22 @@ 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 for_each_rawref(each_ref_fn fn, void *cb_data);
 
+/*
+ * Normalizes partial refs to their full qualified form.
+ * If prefix is NULL, will prepend 'refs/' to the pattern if it doesn't start
+ * with 'refs/'. Results in refs/<pattern>
+ *
+ * If prefix is not NULL will result in <prefix>/<pattern>
+ *
+ * If ENSURE_GLOB is set and no glob characters are found in the
+ * pattern, a trailing </><*> will be appended to the result.
+ * (<> characters to avoid breaking C comment syntax)
+ */
+
+#define ENSURE_GLOB 1
+void normalize_glob_ref (struct strbuf *normalized_pattern, const char *prefix,
+				const char *pattern, int flags);
+
 static inline const char *has_glob_specials(const char *pattern)
 {
 	return strpbrk(pattern, "?*[");
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-04  0:41 [PATCH v1 0/2] Add option to git log to choose which refs receive decoration Rafael Ascensão
  2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
@ 2017-11-04  0:41 ` Rafael Ascensão
  2017-11-04  3:49   ` Junio C Hamano
  2017-11-21 21:33 ` [PATCH v2] " Rafael Ascensão
  2 siblings, 1 reply; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-04  0:41 UTC (permalink / raw)
  To: git
  Cc: Rafael Ascensão, me, gitster, hjemli, mhagger, pclouds,
	ilari.liusvaara

When `log --decorate` is used, git will decorate commits with all
available refs. While in most cases this the desired effect, under some
conditions it can lead to excessively verbose output.

Using `--exclude=<pattern>` can help mitigate that verboseness by
removing unnecessary 'branches' from the output. However, if the tip of
an excluded ref points to an ancestor of a non-excluded ref, git will
decorate it regardless.

With `--decorate-refs=<pattern>`, only refs that match <pattern> are
decorated while `--decorate-refs-exclude=<pattern>` allows to do the
reverse, remove ref decorations that match <pattern>

Both can be used together but --decorate-refs-exclude patterns have
precedence over --decorate-refs patterns.

The pattern follows similar rules as `--glob` except it doesn't assume a
trailing '/*' if glob characters are missing.

Both `--decorate-refs` and `--decorate-refs-exclude` can be used
multiple times.

Signed-off-by: Kevin Daudt <me@ikke.info>
Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
---
 Documentation/git-log.txt |  12 ++++++
 builtin/log.c             |  10 ++++-
 log-tree.c                |  37 ++++++++++++++---
 log-tree.h                |   6 ++-
 pretty.c                  |   4 +-
 revision.c                |   2 +-
 t/t4202-log.sh            | 101 ++++++++++++++++++++++++++++++++++++++++++++++
 7 files changed, 162 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 32246fdb0..314417d89 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -38,6 +38,18 @@ OPTIONS
 	are shown as if 'short' were given, otherwise no ref names are
 	shown. The default option is 'short'.
 
+--decorate-refs=<pattern>::
+	Only print ref names that match the specified pattern. Uses the same
+	rules as `git rev-list --glob` except it doesn't assume a trailing a
+	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
+	`--decorate-refs-exlclude` has precedence.
+
+--decorate-refs-exclude=<pattern>::
+	Do not print ref names that match the specified pattern. Uses the same
+	rules as `git rev-list --glob` except it doesn't assume a trailing a
+	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
+	Has precedence over `--decorate-refs`.
+
 --source::
 	Print out the ref name given on the command line by which each
 	commit was reached.
diff --git a/builtin/log.c b/builtin/log.c
index d81a09051..3587c0055 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -143,11 +143,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	struct userformat_want w;
 	int quiet = 0, source = 0, mailmap = 0;
 	static struct line_opt_callback_data line_cb = {NULL, NULL, STRING_LIST_INIT_DUP};
+	static struct string_list decorate_refs_exclude = STRING_LIST_INIT_DUP;
+	static struct string_list decorate_refs_include = STRING_LIST_INIT_DUP;
+	struct ref_include_exclude_list ref_filter_lists = {&decorate_refs_include,
+							    &decorate_refs_exclude};
 
 	const struct option builtin_log_options[] = {
 		OPT__QUIET(&quiet, N_("suppress diff output")),
 		OPT_BOOL(0, "source", &source, N_("show source")),
 		OPT_BOOL(0, "use-mailmap", &mailmap, N_("Use mail map file")),
+		OPT_STRING_LIST(0, "decorate-refs", &decorate_refs_include,
+				N_("ref"), N_("only decorate these refs")),
+		OPT_STRING_LIST(0, "decorate-refs-exclude", &decorate_refs_exclude,
+				N_("ref"), N_("do not decorate these refs")),
 		{ OPTION_CALLBACK, 0, "decorate", NULL, NULL, N_("decorate options"),
 		  PARSE_OPT_OPTARG, decorate_callback},
 		OPT_CALLBACK('L', NULL, &line_cb, "n,m:file",
@@ -206,7 +214,7 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 
 	if (decoration_style) {
 		rev->show_decorations = 1;
-		load_ref_decorations(decoration_style);
+		load_ref_decorations(decoration_style, &ref_filter_lists);
 	}
 
 	if (rev->line_level_traverse)
diff --git a/log-tree.c b/log-tree.c
index cea056234..8efc7ac3d 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -94,9 +94,33 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
 {
 	struct object *obj;
 	enum decoration_type type = DECORATION_NONE;
+	struct ref_include_exclude_list *filter = (struct ref_include_exclude_list *)cb_data;
+	struct string_list_item *item;
+	struct strbuf real_pattern = STRBUF_INIT;
+
+	if(filter && filter->exclude->nr > 0) {
+		/* if current ref is on the exclude list skip */
+		for_each_string_list_item(item, filter->exclude) {
+			strbuf_reset(&real_pattern);
+			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
+			if (!wildmatch(real_pattern.buf, refname, 0))
+				goto finish;
+		}
+	}
 
-	assert(cb_data == NULL);
+	if (filter && filter->include->nr > 0) {
+		/* if current ref is present on the include jump to decorate */
+		for_each_string_list_item(item, filter->include) {
+			strbuf_reset(&real_pattern);
+			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
+			if (!wildmatch(real_pattern.buf, refname, 0))
+				goto decorate;
+		}
+		/* Filter was given, but no match was found, skip */
+		goto finish;
+	}
 
+decorate:
 	if (starts_with(refname, git_replace_ref_base)) {
 		struct object_id original_oid;
 		if (!check_replace_refs)
@@ -136,6 +160,9 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
 			parse_object(&obj->oid);
 		add_name_decoration(DECORATION_REF_TAG, refname, obj);
 	}
+
+finish:
+	strbuf_release(&real_pattern);
 	return 0;
 }
 
@@ -148,15 +175,15 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
 	return 0;
 }
 
-void load_ref_decorations(int flags)
+void load_ref_decorations(int flags, struct ref_include_exclude_list *data)
 {
 	if (!decoration_loaded) {
 
 		decoration_loaded = 1;
 		decoration_flags = flags;
-		for_each_ref(add_ref_decoration, NULL);
-		head_ref(add_ref_decoration, NULL);
-		for_each_commit_graft(add_graft_decoration, NULL);
+		for_each_ref(add_ref_decoration, data);
+		head_ref(add_ref_decoration, data);
+		for_each_commit_graft(add_graft_decoration, data);
 	}
 }
 
diff --git a/log-tree.h b/log-tree.h
index 48f11fb74..66563af88 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -7,6 +7,10 @@ struct log_info {
 	struct commit *commit, *parent;
 };
 
+struct ref_include_exclude_list {
+	struct string_list *include, *exclude;
+};
+
 int parse_decorate_color_config(const char *var, const char *slot_name, const char *value);
 void init_log_tree_opt(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,
 			     const char **extra_headers_p,
 			     int *need_8bit_cte_p);
-void load_ref_decorations(int flags);
+void load_ref_decorations(int flags, struct ref_include_exclude_list *);
 
 #define FORMAT_PATCH_NAME_MAX 64
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
diff --git a/pretty.c b/pretty.c
index 2f6b0ae6c..87a6cc4f9 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1186,11 +1186,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		strbuf_addstr(sb, get_revision_mark(NULL, commit));
 		return 1;
 	case 'd':
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(DECORATE_SHORT_REFS, NULL);
 		format_decorations(sb, commit, c->auto_color);
 		return 1;
 	case 'D':
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(DECORATE_SHORT_REFS, NULL);
 		format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
 		return 1;
 	case 'g':		/* reflog info */
diff --git a/revision.c b/revision.c
index d167223e6..298ff054b 100644
--- a/revision.c
+++ b/revision.c
@@ -1822,7 +1822,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->simplify_by_decoration = 1;
 		revs->limited = 1;
 		revs->prune = 1;
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(DECORATE_SHORT_REFS, NULL);
 	} else if (!strcmp(arg, "--date-order")) {
 		revs->sort_order = REV_SORT_BY_COMMIT_DATE;
 		revs->topo_order = 1;
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 8f155da7a..e26d09a5c 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -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="%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="%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' '
 	git log --oneline --decorate=full >expect.full &&
 	git log --oneline --decorate=short >expect.short &&
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
@ 2017-11-04  2:27   ` Junio C Hamano
  2017-11-04  7:33     ` Rafael Ascensão
  2017-11-04 22:45     ` Kevin Daudt
  2017-11-05 13:42   ` Michael Haggerty
  1 sibling, 2 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-04  2:27 UTC (permalink / raw)
  To: Rafael Ascensão; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

> `for_each_glob_ref_in` has some code built into it that converts
> partial refs like 'heads/master' to their full qualified form

s/full/&y/ (read: that "full" needs "y" at the end).

> 'refs/heads/master'. It also assume a trailing '/*' if no glob

s/assume/&s/

> characters are present in the pattern.
>
> Extract that logic to its own function which can be reused elsewhere
> where the same behaviour is needed, and add an ENSURE_GLOB flag
> to toggle if a trailing '/*' is to be appended to the result.
>
> Signed-off-by: Kevin Daudt <me@ikke.info>
> Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>

Could you explain Kevin's sign-off we see above?  It is a bit
unusual (I am not yet saying it is wrong---I cannot judge until I
find out why it is there) to see a patch from person X with sign off
from person Y and then person X in that order.  It is normal for a
patch authored by person X to have sign-off by X and then Y if X
wrote it, signed it off and passed to Y, and then Y resent it after
signing it off (while preserving the authorship of X by adding an
in-body From: header), but I do not think that is what we have here.

It could be that you did pretty much all the work on this patch
and Kevin helped you to polish this patch off-list, in which case
the usual thing to do is to use "Helped-by: Kevin" instead.

> ---
>  refs.c | 34 ++++++++++++++++++++--------------
>  refs.h | 16 ++++++++++++++++
>  2 files changed, 36 insertions(+), 14 deletions(-)
>
> diff --git a/refs.c b/refs.c
> index c590a992f..1e74b48e6 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -369,32 +369,38 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>  	return ret;
>  }
>  
> -int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
> -	const char *prefix, void *cb_data)
> +void normalize_glob_ref(struct strbuf *normalized_pattern, const char *prefix,
> +		const char *pattern, int flags)

It is better to use "unsigned" for a single word "flags" used as a
collection of bits.  In older parts of the codebase, we have
codepaths that pass signed int as a flags word, simply because we
didn't know better, but we do not have to spread that practice to
new code.

>  {
> -	struct strbuf real_pattern = STRBUF_INIT;
> -	struct ref_filter filter;
> -	int ret;
> -
>  	if (!prefix && !starts_with(pattern, "refs/"))
> -		strbuf_addstr(&real_pattern, "refs/");
> +		strbuf_addstr(normalized_pattern, "refs/");
>  	else if (prefix)
> -		strbuf_addstr(&real_pattern, prefix);
> -	strbuf_addstr(&real_pattern, pattern);
> +		strbuf_addstr(normalized_pattern, prefix);
> +	strbuf_addstr(normalized_pattern, pattern);
>  
> -	if (!has_glob_specials(pattern)) {
> +	if (!has_glob_specials(pattern) && (flags & ENSURE_GLOB)) {
>  		/* Append implied '/' '*' if not present. */
> -		strbuf_complete(&real_pattern, '/');
> +		strbuf_complete(normalized_pattern, '/');
>  		/* No need to check for '*', there is none. */
> -		strbuf_addch(&real_pattern, '*');
> +		strbuf_addch(normalized_pattern, '*');
>  	}
> +}

The above looks like a pure and regression-free code movement (plus
a small new feature) that is faithful to the original, which is good.

I however notice that addition of /* to the tail is trying to be
careful by using strbuf_complete('/'), but prefixing with "refs/"
does not and we would end up with a double-slash if pattern begins
with a slash.  The contract between the caller of this function (or
its original, which is for_each_glob_ref_in()) and the callee is
that prefix must not begin with '/', so it may be OK, but we might
want to add "if (*pattern == '/') BUG(...)" at the beginning.  

I dunno.  In any case, that is totally outside the scope of this two
patch series.

> diff --git a/refs.h b/refs.h
> index a02b628c8..9f9a8bb27 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -312,6 +312,22 @@ 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 for_each_rawref(each_ref_fn fn, void *cb_data);
>  
> +/*
> + * Normalizes partial refs to their full qualified form.

s/full/&y/;

> + * If prefix is NULL, will prepend 'refs/' to the pattern if it doesn't start
> + * with 'refs/'. Results in refs/<pattern>
> + *
> + * If prefix is not NULL will result in <prefix>/<pattern>

s/NULL/&,/;

> + *
> + * If ENSURE_GLOB is set and no glob characters are found in the
> + * pattern, a trailing </><*> will be appended to the result.
> + * (<> characters to avoid breaking C comment syntax)
> + */
> +
> +#define ENSURE_GLOB 1
> +void normalize_glob_ref (struct strbuf *normalized_pattern, const char *prefix,
> +				const char *pattern, int flags);
> +
>  static inline const char *has_glob_specials(const char *pattern)
>  {
>  	return strpbrk(pattern, "?*[");

Thanks.  Other than the above minor points, looks good to me.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-04  0:41 ` [PATCH v1 2/2] log: add option to choose which refs to decorate Rafael Ascensão
@ 2017-11-04  3:49   ` Junio C Hamano
  2017-11-04  7:34     ` Rafael Ascensão
  2017-11-06 20:10     ` Jacob Keller
  0 siblings, 2 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-04  3:49 UTC (permalink / raw)
  To: Rafael Ascensão; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

> When `log --decorate` is used, git will decorate commits with all
> available refs. While in most cases this the desired effect, under some
> conditions it can lead to excessively verbose output.

Correct.

> Using `--exclude=<pattern>` can help mitigate that verboseness by
> removing unnecessary 'branches' from the output. However, if the tip of
> an excluded ref points to an ancestor of a non-excluded ref, git will
> decorate it regardless.

Is this even relevant?  I think the above would only serve to
confuse the readers.  --exclude, --branches, etc. are ways to
specify what starting points "git log" history traversal should
begin and has nothing to do with what set of refs are to be used to
decorate the commits that are shown.  But the paragraph makes
readers wonder if it might have any effect in some circumstances.

> With `--decorate-refs=<pattern>`, only refs that match <pattern> are
> decorated while `--decorate-refs-exclude=<pattern>` allows to do the
> reverse, remove ref decorations that match <pattern>

And "Only refs that match ... are decorated" is also confusing.  The
thing is, refs are never decorated, they are used for decorating
commits in the output from "git log".  For example, if you have 

	---A---B---C---D

and B is at the tip of the 'master' branch, the output from "git log
D" would decorate B with 'master', even if you do not say 'master'
on the command line as the commit to start the traversal from.

Perhaps drop the irrelevant paragraph about "--exclude" and write
something like this instead?

	When "--decorate-refs=<pattern>" is given, only the refs
	that match the pattern is used in decoration.  The refs that
	match the pattern, when "--decorate-refs-exclude=<pattern>"
	is given, are never used in decoration.

> Both can be used together but --decorate-refs-exclude patterns have
> precedence over --decorate-refs patterns.

A reasonable and an easy-to-explain way to mix zero or more positive
and zero or more negagive patterns that follows the convention used
elsewhere in the system (e.g. how negative pathspecs work) is

 (1) if there is no positive pattern given, pretend as if an
     inclusive default positive pattern was given;

 (2) for each candidate, reject it if it matches no positive
     pattern, or if it matches any one of negative patterns.

For pathspecs, we use "everything" as the inclusive default positive
pattern, I think, and for the set of refs used for decoration, a
reasonable choice would also be to use "everything", which matches
the current behaviour.

> The pattern follows similar rules as `--glob` except it doesn't assume a
> trailing '/*' if glob characters are missing.

Why should this be a special case that burdens users to remember one
more rule?  Wouldn't users find "--decorate-refs=refs/tags" useful
and it woulld be shorter and nicer than having to say "refs/tags/*"?

> diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
> index 32246fdb0..314417d89 100644
> --- a/Documentation/git-log.txt
> +++ b/Documentation/git-log.txt
> @@ -38,6 +38,18 @@ OPTIONS
>  	are shown as if 'short' were given, otherwise no ref names are
>  	shown. The default option is 'short'.
>  
> +--decorate-refs=<pattern>::
> +	Only print ref names that match the specified pattern. Uses the same
> +	rules as `git rev-list --glob` except it doesn't assume a trailing a
> +	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
> +	`--decorate-refs-exlclude` has precedence.
> +
> +--decorate-refs-exclude=<pattern>::
> +	Do not print ref names that match the specified pattern. Uses the same
> +	rules as `git rev-list --glob` except it doesn't assume a trailing a
> +	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
> +	Has precedence over `--decorate-refs`.

These two may be technically correct, but I wonder if we can make it
easier to understand (I found "precedence" bit hard to follow, as in
my mind, these are ANDed conditions and between (A & ~B), there is
no "precedence").  Also we'd want to clarify what happens when only
"--decorate-refs-exclude"s are given, which in turn necessitates us
to describe what happens when only "--decorate-refs"s are given.

> diff --git a/log-tree.c b/log-tree.c
> index cea056234..8efc7ac3d 100644
> --- a/log-tree.c
> +++ b/log-tree.c
> @@ -94,9 +94,33 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
>  {
>  	struct object *obj;
>  	enum decoration_type type = DECORATION_NONE;
> +	struct ref_include_exclude_list *filter = (struct ref_include_exclude_list *)cb_data;
> +	struct string_list_item *item;
> +	struct strbuf real_pattern = STRBUF_INIT;
> +
> +	if(filter && filter->exclude->nr > 0) {

Have SP before '('.

> +		/* if current ref is on the exclude list skip */
> +		for_each_string_list_item(item, filter->exclude) {
> +			strbuf_reset(&real_pattern);
> +			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
> +			if (!wildmatch(real_pattern.buf, refname, 0))
> +				goto finish;
> +		}
> +	}
>  
> -	assert(cb_data == NULL);
> +	if (filter && filter->include->nr > 0) {
> +		/* if current ref is present on the include jump to decorate */
> +		for_each_string_list_item(item, filter->include) {
> +			strbuf_reset(&real_pattern);
> +			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
> +			if (!wildmatch(real_pattern.buf, refname, 0))
> +				goto decorate;
> +		}
> +		/* Filter was given, but no match was found, skip */
> +		goto finish;
> +	}

The above seems to implement the natural mixing of negative and
positive patterns, which is good.

Unless I am missing something, I think these normalize_grob_ref()
calls should be removed from this function; add_ref_decoration() is
called once for EVERY ref the repository has, so you are normalizing
a handful of patterns you got from the user over and over to get the
same normalization, possibly thousands of times in a repository of a
project with long history.

You have finished collecting patterns on filter->{exclude,include}
list from the user by the time "for_each_ref(add_ref_decoration)" is
called in load_ref_decorations(), and these patterns never changes
after that.  

Perhaps normalize the patterns inside load_ref_decorations() only
once and have the normalized patterns in the filter lists?

> +decorate:
>  	if (starts_with(refname, git_replace_ref_base)) {
>  		struct object_id original_oid;
>  		if (!check_replace_refs)
> @@ -136,6 +160,9 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
>  			parse_object(&obj->oid);
>  		add_name_decoration(DECORATION_REF_TAG, refname, obj);
>  	}
> +
> +finish:
> +	strbuf_release(&real_pattern);
>  	return 0;
>  }
>  
> @@ -148,15 +175,15 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
>  	return 0;
>  }
>  
> -void load_ref_decorations(int flags)
> +void load_ref_decorations(int flags, struct ref_include_exclude_list *data)
>  {
>  	if (!decoration_loaded) {
>  
>  		decoration_loaded = 1;
>  		decoration_flags = flags;
> -		for_each_ref(add_ref_decoration, NULL);
> -		head_ref(add_ref_decoration, NULL);
> -		for_each_commit_graft(add_graft_decoration, NULL);
> +		for_each_ref(add_ref_decoration, data);
> +		head_ref(add_ref_decoration, data);
> +		for_each_commit_graft(add_graft_decoration, data);

Don't name that variable "data".

for_each_*() and friends that take a callback with callback specific
data MUST call the callback specific data as generic, e.g. cb_data,
because they do not know what they are passing.  The callers of
these functions, like this one, however, know what they are passing.
Also load_ref_decorations() itself knows what its second parameter
is.

    void load_ref_decorations(int flags, struct decoration_filter *filter)

or something (see below).

>  	}
>  }
>  
> diff --git a/log-tree.h b/log-tree.h
> index 48f11fb74..66563af88 100644
> --- a/log-tree.h
> +++ b/log-tree.h
> @@ -7,6 +7,10 @@ struct log_info {
>  	struct commit *commit, *parent;
>  };
>  
> +struct ref_include_exclude_list {
> +	struct string_list *include, *exclude;
> +};

The "decoration" is not the only thing related to "ref" in the
log-tree API; calling this structure that filters what refs to be
used for decoration with the above name without saying that this is
about "decoration" is too selfish and unmaintainable.

How about "struct decoration_filter" and rename the fields to say
"{include,exclude}_ref_pattern" or something like that?  The
renaming of the fields to include "ref" somewhere is coming from the
same concern---it will be selfish and narrow-minded to imagine that
the ways to filter refs used for decoration will stay forever only
based on refnames and nothing else, which would be the reason not to
have "ref" somewhere in the names.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04  2:27   ` Junio C Hamano
@ 2017-11-04  7:33     ` Rafael Ascensão
  2017-11-04 22:45     ` Kevin Daudt
  1 sibling, 0 replies; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-04  7:33 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

On 04/11/17 02:27, Junio C Hamano wrote:
> Rafael Ascensão <rafa.almas@gmail.com> writes:
> 
>> Signed-off-by: Kevin Daudt <me@ikke.info>
>> Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
> 
> Could you explain Kevin's sign-off we see above?  It is a bit
> unusual (I am not yet saying it is wrong---I cannot judge until I
> find out why it is there) to see a patch from person X with sign off
> from person Y and then person X in that order.  It is normal for a
> patch authored by person X to have sign-off by X and then Y if X
> wrote it, signed it off and passed to Y, and then Y resent it after
> signing it off (while preserving the authorship of X by adding an
> in-body From: header), but I do not think that is what we have here.
> 
> It could be that you did pretty much all the work on this patch
> and Kevin helped you to polish this patch off-list, in which case
> the usual thing to do is to use "Helped-by: Kevin" instead.

That's more or less what happened. I wouldn't say I did "pretty much all 
the work". Yes, I wrote the code but with great help of Kevin. The 
intention of the dual Signed-off-by was to equally attribute authorship 
of the patch. But if that creates ambiguity I will change it to 
"Helped-by" as suggested.

> It is better to use "unsigned" for a single word "flags" used as a
> collection of bits.  In older parts of the codebase, we have
> codepaths that pass signed int as a flags word, simply because we
> didn't know better, but we do not have to spread that practice to
> new code.

I noticed this, but chose to "mimic" the code around me. I'll correct it.
On a related note is there a guideline for defining flags or are
`#define FLAG (1u << 0)`, `#define FLAG (1 << 0)`
`#define FLAG 1` and `#define FLAG 0x1` equally accepted?

>>   {
>> -	struct strbuf real_pattern = STRBUF_INIT;
>> -	struct ref_filter filter;
>> -	int ret;
>> -
>>   	if (!prefix && !starts_with(pattern, "refs/"))
>> -		strbuf_addstr(&real_pattern, "refs/");
>> +		strbuf_addstr(normalized_pattern, "refs/");
>>   	else if (prefix)
>> -		strbuf_addstr(&real_pattern, prefix);
>> -	strbuf_addstr(&real_pattern, pattern);
>> +		strbuf_addstr(normalized_pattern, prefix);
>> +	strbuf_addstr(normalized_pattern, pattern);
>>   
>> -	if (!has_glob_specials(pattern)) {
>> +	if (!has_glob_specials(pattern) && (flags & ENSURE_GLOB)) {
>>   		/* Append implied '/' '*' if not present. */
>> -		strbuf_complete(&real_pattern, '/');
>> +		strbuf_complete(normalized_pattern, '/');
>>   		/* No need to check for '*', there is none. */
>> -		strbuf_addch(&real_pattern, '*');
>> +		strbuf_addch(normalized_pattern, '*');
>>   	}
>> +}
> 
> The above looks like a pure and regression-free code movement (plus
> a small new feature) that is faithful to the original, which is good.
> 
> I however notice that addition of /* to the tail is trying to be
> careful by using strbuf_complete('/'), but prefixing with "refs/"
> does not and we would end up with a double-slash if pattern begins
> with a slash.  The contract between the caller of this function (or
> its original, which is for_each_glob_ref_in()) and the callee is
> that prefix must not begin with '/', so it may be OK, but we might
> want to add "if (*pattern == '/') BUG(...)" at the beginning.
> 
> I dunno.  In any case, that is totally outside the scope of this two
> patch series.

I guess it doesn't hurt adding that safety net.

> Thanks.  Other than the above minor points, looks good to me.
I'll fix the mentioned issues. Thanks.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-04  3:49   ` Junio C Hamano
@ 2017-11-04  7:34     ` Rafael Ascensão
  2017-11-05  2:00       ` Junio C Hamano
  2017-11-06 20:10     ` Jacob Keller
  1 sibling, 1 reply; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-04  7:34 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

On 04/11/17 03:49, Junio C Hamano wrote:
> Rafael Ascensão <rafa.almas@gmail.com> writes:
> 
>> Using `--exclude=<pattern>` can help mitigate that verboseness by
>> removing unnecessary 'branches' from the output. However, if the tip of
>> an excluded ref points to an ancestor of a non-excluded ref, git will
>> decorate it regardless.
> 
> Is this even relevant?  I think the above would only serve to
> confuse the readers.  --exclude, --branches, etc. are ways to
> specify what starting points "git log" history traversal should
> begin and has nothing to do with what set of refs are to be used to
> decorate the commits that are shown.  But the paragraph makes
> readers wonder if it might have any effect in some circumstances.
> 
>> With `--decorate-refs=<pattern>`, only refs that match <pattern> are
>> decorated while `--decorate-refs-exclude=<pattern>` allows to do the
>> reverse, remove ref decorations that match <pattern>
> 
> And "Only refs that match ... are decorated" is also confusing.  The
> thing is, refs are never decorated, they are used for decorating
> commits in the output from "git log".  For example, if you have 
> 
> 	---A---B---C---D
> 
> and B is at the tip of the 'master' branch, the output from "git log
> D" would decorate B with 'master', even if you do not say 'master'
> on the command line as the commit to start the traversal from. >
> Perhaps drop the irrelevant paragraph about "--exclude" and write
> something like this instead?
> 
> 	When "--decorate-refs=<pattern>" is given, only the refs
> 	that match the pattern is used in decoration.  The refs that
> 	match the pattern, when "--decorate-refs-exclude=<pattern>"
> 	is given, are never used in decoration.
> 

What you explained was the reason I mentioned that. Because some users 
were wrongfully trying to remove decorations by trying to exclude the 
starting points. But I agree this adds little value and can generate 
further confusion. I will remove that section.

>> Both can be used together but --decorate-refs-exclude patterns have
>> precedence over --decorate-refs patterns.
> 
> A reasonable and an easy-to-explain way to mix zero or more positive
> and zero or more negagive patterns that follows the convention used
> elsewhere in the system (e.g. how negative pathspecs work) is
> 
>   (1) if there is no positive pattern given, pretend as if an
>       inclusive default positive pattern was given;
> 
>   (2) for each candidate, reject it if it matches no positive
>       pattern, or if it matches any one of negative patterns.
> 
> For pathspecs, we use "everything" as the inclusive default positive
> pattern, I think, and for the set of refs used for decoration, a
> reasonable choice would also be to use "everything", which matches
> the current behaviour.
> 

That's a nice explanation that fits the current "--decorate-refs" behavior.

>> The pattern follows similar rules as `--glob` except it doesn't assume a
>> trailing '/*' if glob characters are missing.
> 
> Why should this be a special case that burdens users to remember one
> more rule?  Wouldn't users find "--decorate-refs=refs/tags" useful
> and it woulld be shorter and nicer than having to say "refs/tags/*"?
> 

I wanted to allow exact patterns like:
"--decorate-refs=refs/heads/master" and for that I disabled the flag 
that adds the trailing '/*' if no globs are found. As a side effect, I 
lost the shortcut.

Is adding a yet another flag that appends '/*' only if the pattern 
equals "refs/{heads,remotes,tags}" a good idea?

Because changing the default behavior of that function has implications 
on multiple commands which I think shouldn't change. But at the same 
time, would be nice to have the logic that deals with glob-ref patterns 
all in one place.

What's the sane way to do this?

>> diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
>> index 32246fdb0..314417d89 100644
>> --- a/Documentation/git-log.txt
>> +++ b/Documentation/git-log.txt
>> @@ -38,6 +38,18 @@ OPTIONS
>>   	are shown as if 'short' were given, otherwise no ref names are
>>   	shown. The default option is 'short'.
>>   
>> +--decorate-refs=<pattern>::
>> +	Only print ref names that match the specified pattern. Uses the same
>> +	rules as `git rev-list --glob` except it doesn't assume a trailing a
>> +	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
>> +	`--decorate-refs-exlclude` has precedence.
>> +
>> +--decorate-refs-exclude=<pattern>::
>> +	Do not print ref names that match the specified pattern. Uses the same
>> +	rules as `git rev-list --glob` except it doesn't assume a trailing a
>> +	trailing '/{asterisk}' if pattern lacks '?', '{asterisk}', or '['.
>> +	Has precedence over `--decorate-refs`.

> These two may be technically correct, but I wonder if we can make it
> easier to understand (I found "precedence" bit hard to follow, as in
> my mind, these are ANDed conditions and between (A & ~B), there is
> no "precedence").  Also we'd want to clarify what happens when only
> "--decorate-refs-exclude"s are given, which in turn necessitates us
> to describe what happens when only "--decorate-refs"s are given.

I believe the same explanation mentioned earlier fits nicely here too.

>> diff --git a/log-tree.c b/log-tree.c
>> index cea056234..8efc7ac3d 100644
>> --- a/log-tree.c
>> +++ b/log-tree.c
>> @@ -94,9 +94,33 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
>>   {
>>   	struct object *obj;
>>   	enum decoration_type type = DECORATION_NONE;
>> +	struct ref_include_exclude_list *filter = (struct ref_include_exclude_list *)cb_data;
>> +	struct string_list_item *item;
>> +	struct strbuf real_pattern = STRBUF_INIT;
>> +
>> +	if(filter && filter->exclude->nr > 0) {
> 
> Have SP before '('.
> 
>> +		/* if current ref is on the exclude list skip */
>> +		for_each_string_list_item(item, filter->exclude) {
>> +			strbuf_reset(&real_pattern);
>> +			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
>> +			if (!wildmatch(real_pattern.buf, refname, 0))
>> +				goto finish;
>> +		}
>> +	}
>>   
>> -	assert(cb_data == NULL);
>> +	if (filter && filter->include->nr > 0) {
>> +		/* if current ref is present on the include jump to decorate */
>> +		for_each_string_list_item(item, filter->include) {
>> +			strbuf_reset(&real_pattern);
>> +			normalize_glob_ref(&real_pattern, NULL, item->string, 0);
>> +			if (!wildmatch(real_pattern.buf, refname, 0))
>> +				goto decorate;
>> +		}
>> +		/* Filter was given, but no match was found, skip */
>> +		goto finish;
>> +	}
> 
> The above seems to implement the natural mixing of negative and
> positive patterns, which is good.
> 
> Unless I am missing something, I think these normalize_grob_ref()
> calls should be removed from this function; add_ref_decoration() is
> called once for EVERY ref the repository has, so you are normalizing
> a handful of patterns you got from the user over and over to get the
> same normalization, possibly thousands of times in a repository of a
> project with long history.
> 
> You have finished collecting patterns on filter->{exclude,include}
> list from the user by the time "for_each_ref(add_ref_decoration)" is
> called in load_ref_decorations(), and these patterns never changes
> after that.
> 
> Perhaps normalize the patterns inside load_ref_decorations() only
> once and have the normalized patterns in the filter lists?
> 
This would be what a sane person would do. This detail went over my 
head. Will move it to load_ref_decorations()

>> +decorate:
>>   	if (starts_with(refname, git_replace_ref_base)) {
>>   		struct object_id original_oid;
>>   		if (!check_replace_refs)
>> @@ -136,6 +160,9 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
>>   			parse_object(&obj->oid);
>>   		add_name_decoration(DECORATION_REF_TAG, refname, obj);
>>   	}
>> +
>> +finish:
>> +	strbuf_release(&real_pattern);
>>   	return 0;
>>   }
>>   
>> @@ -148,15 +175,15 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
>>   	return 0;
>>   }
>>   
>> -void load_ref_decorations(int flags)
>> +void load_ref_decorations(int flags, struct ref_include_exclude_list *data)
>>   {
>>   	if (!decoration_loaded) {
>>   
>>   		decoration_loaded = 1;
>>   		decoration_flags = flags;
>> -		for_each_ref(add_ref_decoration, NULL);
>> -		head_ref(add_ref_decoration, NULL);
>> -		for_each_commit_graft(add_graft_decoration, NULL);
>> +		for_each_ref(add_ref_decoration, data);
>> +		head_ref(add_ref_decoration, data);
>> +		for_each_commit_graft(add_graft_decoration, data);
> 
> Don't name that variable "data".
> 
> for_each_*() and friends that take a callback with callback specific
> data MUST call the callback specific data as generic, e.g. cb_data,
> because they do not know what they are passing.  The callers of
> these functions, like this one, however, know what they are passing.
> Also load_ref_decorations() itself knows what its second parameter
> is.
> 
>      void load_ref_decorations(int flags, struct decoration_filter *filter)
> 
> or something (see below).
> 
>>   	}
>>   }
>>   
>> diff --git a/log-tree.h b/log-tree.h
>> index 48f11fb74..66563af88 100644
>> --- a/log-tree.h
>> +++ b/log-tree.h
>> @@ -7,6 +7,10 @@ struct log_info {
>>   	struct commit *commit, *parent;
>>   };
>>   
>> +struct ref_include_exclude_list {
>> +	struct string_list *include, *exclude;
>> +};
> 
> The "decoration" is not the only thing related to "ref" in the
> log-tree API; calling this structure that filters what refs to be
> used for decoration with the above name without saying that this is
> about "decoration" is too selfish and unmaintainable.
> 
> How about "struct decoration_filter" and rename the fields to say
> "{include,exclude}_ref_pattern" or something like that?  The
> renaming of the fields to include "ref" somewhere is coming from the
> same concern---it will be selfish and narrow-minded to imagine that
> the ways to filter refs used for decoration will stay forever only
> based on refnames and nothing else, which would be the reason not to
> have "ref" somewhere in the names.
> 
I will make the corrections. Thanks for the feedback.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04  2:27   ` Junio C Hamano
  2017-11-04  7:33     ` Rafael Ascensão
@ 2017-11-04 22:45     ` Kevin Daudt
  2017-11-05 13:21       ` Michael Haggerty
  1 sibling, 1 reply; 24+ messages in thread
From: Kevin Daudt @ 2017-11-04 22:45 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Kevin Daudt, git, hjemli, mhagger, pclouds, ilari.liusvaara,
	Rafael Ascensão

On Sat, Nov 04, 2017 at 11:27:39AM +0900, Junio C Hamano wrote:
> I however notice that addition of /* to the tail is trying to be
> careful by using strbuf_complete('/'), but prefixing with "refs/"
> does not and we would end up with a double-slash if pattern begins
> with a slash.  The contract between the caller of this function (or
> its original, which is for_each_glob_ref_in()) and the callee is
> that prefix must not begin with '/', so it may be OK, but we might
> want to add "if (*pattern == '/') BUG(...)" at the beginning.
>
> I dunno.  In any case, that is totally outside the scope of this two
> patch series.

I do think it's a good idea to make future readers of the code aware of
this contract, and adding a BUG assert does that quite well. Here is a
patch that implements it.

This applies of course on top of this patch series.

-- >8 --
Subject: [PATCH] normalize_glob_ref: assert implicit contract of prefix

normalize_glob_ref has an implicit contract of expecting 'prefix' to not
start with a '/', otherwise the pattern would end up with a
double-slash.

Mark it as a BUG when the prefix argument of normalize_glob_ref starts
with a '/' so that future callers will be aware of this contract.

Signed-off-by: Kevin Daudt <me@ikke.info>
---
 refs.c | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/refs.c b/refs.c
index e9ae659ae..6747981d1 100644
--- a/refs.c
+++ b/refs.c
@@ -372,6 +372,8 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 void normalize_glob_ref(struct strbuf *normalized_pattern, const char *prefix,
 		const char *pattern, int flags)
 {
+	if (prefix && *prefix == '/') BUG("prefix cannot not start with '/'");
+
 	if (!prefix && !starts_with(pattern, "refs/"))
 		strbuf_addstr(normalized_pattern, "refs/");
 	else if (prefix)
-- 
2.15.0.rc2.57.g2f899857a9


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-04  7:34     ` Rafael Ascensão
@ 2017-11-05  2:00       ` Junio C Hamano
  2017-11-05  6:17         ` Junio C Hamano
  0 siblings, 1 reply; 24+ messages in thread
From: Junio C Hamano @ 2017-11-05  2:00 UTC (permalink / raw)
  To: Rafael Ascensão; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

>>> The pattern follows similar rules as `--glob` except it doesn't assume a
>>> trailing '/*' if glob characters are missing.
>>
>> Why should this be a special case that burdens users to remember one
>> more rule?  Wouldn't users find "--decorate-refs=refs/tags" useful
>> and it woulld be shorter and nicer than having to say "refs/tags/*"?
>
> I wanted to allow exact patterns like:
> "--decorate-refs=refs/heads/master" and for that I disabled the flag
> that adds the trailing '/*' if no globs are found. As a side effect, I
> lost the shortcut.
>
> Is adding a yet another flag that appends '/*' only if the pattern
> equals "refs/{heads,remotes,tags}" a good idea?

No.

> Because changing the default behavior of that function has
> implications on multiple commands which I think shouldn't change. But
> at the same time, would be nice to have the logic that deals with
> glob-ref patterns all in one place.
>
> What's the sane way to do this?

Learn to type "--decorate-refs="refs/heads/[m]aster", and not twewak
the code at all, perhaps.  The users of existing "with no globbing,
/* is appended" interface are already used to that way and they do
not have to learn a new and inconsistent interface.

After all, "I only want to see 'git log' output with 'master'
decorated" (i.e. not specifying "this class of refs I can glob by
using the naming convention I am using" and instead enumerating the
ones you care about) does not sound like a sensible thing people
often want to do, so making it follow the other codepath so that
people can say "refs/tags" to get "refs/tags/*", while still allowing
such a rare but specific and exact one possible, may not sound too
bad to me.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-05  2:00       ` Junio C Hamano
@ 2017-11-05  6:17         ` Junio C Hamano
  2017-11-06  3:24           ` Rafael Ascensão
  2017-11-06  7:09           ` Michael Haggerty
  0 siblings, 2 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-05  6:17 UTC (permalink / raw)
  To: Rafael Ascensão, git; +Cc: me, hjemli, mhagger, pclouds, ilari.liusvaara

Junio C Hamano <gitster@pobox.com> writes:
> Rafael Ascensão <rafa.almas@gmail.com> writes:
> ...
>> Because changing the default behavior of that function has
>> implications on multiple commands which I think shouldn't change. But
>> at the same time, would be nice to have the logic that deals with
>> glob-ref patterns all in one place.
>>
>> What's the sane way to do this?
>
> Learn to type "--decorate-refs="refs/heads/[m]aster", and not twewak
> the code at all, perhaps.  The users of existing "with no globbing,
> /* is appended" interface are already used to that way and they do
> not have to learn a new and inconsistent interface.
>
> After all, "I only want to see 'git log' output with 'master'
> decorated" (i.e. not specifying "this class of refs I can glob by
> using the naming convention I am using" and instead enumerating the
> ones you care about) does not sound like a sensible thing people
> often want to do, so making it follow the other codepath so that
> people can say "refs/tags" to get "refs/tags/*", while still allowing
> such a rare but specific and exact one possible, may not sound too
> bad to me.

Having said all that, I can imagine another way out might be to
change the behaviour of this "normalize" thing to add two patterns,
the original pattern in addition to the original pattern plus "/*",
when it sees a pattern without any glob.  Many users who relied on
the current behaviour fed "refs/tags" knowing that it will match
everything under "refs/tags" i.e. "refs/tags/*", and they cannot
have a ref that is exactly "refs/tags", so adding the original
pattern without an extra trailing "/*" would not hurt them.  And
this will allow you to say "refs/heads/master" when you know you
want that exact ref, and in such a repository where that original
pattern without trailing "/*" would be useful, because you cannot
have "refs/heads/master/one" at the same time, having an extra
pattern that is the original plus "/*" would not hurt you, either.

This however needs a bit of thought to see if there are corner cases
that may result in unexpected and unwanted fallout, and something I
am reluctant to declare unilaterally that it is a better way to go.

Thoughts?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04 22:45     ` Kevin Daudt
@ 2017-11-05 13:21       ` Michael Haggerty
  0 siblings, 0 replies; 24+ messages in thread
From: Michael Haggerty @ 2017-11-05 13:21 UTC (permalink / raw)
  To: Kevin Daudt, Junio C Hamano
  Cc: git, hjemli, pclouds, ilari.liusvaara, Rafael Ascensão

On 11/04/2017 11:45 PM, Kevin Daudt wrote:
> On Sat, Nov 04, 2017 at 11:27:39AM +0900, Junio C Hamano wrote:
>> I however notice that addition of /* to the tail is trying to be
>> careful by using strbuf_complete('/'), but prefixing with "refs/"
>> does not and we would end up with a double-slash if pattern begins
>> with a slash.  The contract between the caller of this function (or
>> its original, which is for_each_glob_ref_in()) and the callee is
>> that prefix must not begin with '/', so it may be OK, but we might
>> want to add "if (*pattern == '/') BUG(...)" at the beginning.
>>
>> I dunno.  In any case, that is totally outside the scope of this two
>> patch series.
> 
> I do think it's a good idea to make future readers of the code aware of
> this contract, and adding a BUG assert does that quite well. Here is a
> patch that implements it.
> 
> This applies of course on top of this patch series.
> 
> -- >8 --
> Subject: [PATCH] normalize_glob_ref: assert implicit contract of prefix
> 
> normalize_glob_ref has an implicit contract of expecting 'prefix' to not
> start with a '/', otherwise the pattern would end up with a
> double-slash.
> 
> Mark it as a BUG when the prefix argument of normalize_glob_ref starts
> with a '/' so that future callers will be aware of this contract.
> 
> Signed-off-by: Kevin Daudt <me@ikke.info>
> ---
>  refs.c | 2 ++
>  1 file changed, 2 insertions(+)
> 
> diff --git a/refs.c b/refs.c
> index e9ae659ae..6747981d1 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -372,6 +372,8 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>  void normalize_glob_ref(struct strbuf *normalized_pattern, const char *prefix,
>  		const char *pattern, int flags)
>  {
> +	if (prefix && *prefix == '/') BUG("prefix cannot not start with '/'");

This should be split onto two lines.

Also, "prefix cannot not start ..." has two "not". I suggest changing it
to "prefix must not start ...", because that makes it clearer that the
caller is at fault.

What if the caller passes the empty string as prefix? In that case, the
end result would be "/<pattern>", which is also bogus.

> +
>  	if (!prefix && !starts_with(pattern, "refs/"))
>  		strbuf_addstr(normalized_pattern, "refs/");
>  	else if (prefix)

Michael

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
  2017-11-04  2:27   ` Junio C Hamano
@ 2017-11-05 13:42   ` Michael Haggerty
  2017-11-06  1:23     ` Junio C Hamano
  1 sibling, 1 reply; 24+ messages in thread
From: Michael Haggerty @ 2017-11-05 13:42 UTC (permalink / raw)
  To: Rafael Ascensão, git; +Cc: me, gitster, hjemli, pclouds, ilari.liusvaara

On 11/04/2017 01:41 AM, Rafael Ascensão wrote:
> `for_each_glob_ref_in` has some code built into it that converts
> partial refs like 'heads/master' to their full qualified form
> 'refs/heads/master'. It also assume a trailing '/*' if no glob
> characters are present in the pattern.
> 
> Extract that logic to its own function which can be reused elsewhere
> where the same behaviour is needed, and add an ENSURE_GLOB flag
> to toggle if a trailing '/*' is to be appended to the result.
> 
> Signed-off-by: Kevin Daudt <me@ikke.info>
> Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
> ---
>  refs.c | 34 ++++++++++++++++++++--------------
>  refs.h | 16 ++++++++++++++++
>  2 files changed, 36 insertions(+), 14 deletions(-)
> 
> diff --git a/refs.c b/refs.c
> index c590a992f..1e74b48e6 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -369,32 +369,38 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>  	return ret;
>  }
>  
> -int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
> -	const char *prefix, void *cb_data)
> +void normalize_glob_ref(struct strbuf *normalized_pattern, const char *prefix,
> +		const char *pattern, int flags)
>  {
> -	struct strbuf real_pattern = STRBUF_INIT;
> -	struct ref_filter filter;
> -	int ret;
> -
>  	if (!prefix && !starts_with(pattern, "refs/"))
> -		strbuf_addstr(&real_pattern, "refs/");
> +		strbuf_addstr(normalized_pattern, "refs/");
>  	else if (prefix)
> -		strbuf_addstr(&real_pattern, prefix);
> -	strbuf_addstr(&real_pattern, pattern);
> +		strbuf_addstr(normalized_pattern, prefix);
> +	strbuf_addstr(normalized_pattern, pattern);

I realize that the old code did this too, but while you are in the area
it might be nice to simplify the logic from

	if (!prefix && !starts_with(pattern, "refs/"))
		strbuf_addstr(normalized_pattern, "refs/");
	else if (prefix)
		strbuf_addstr(normalized_pattern, prefix);

to

	if (prefix)
		strbuf_addstr(normalized_pattern, prefix);
	else if (!starts_with(pattern, "refs/"))
		strbuf_addstr(normalized_pattern, "refs/");

This would avoid having to check twice whether `prefix` is NULL.

> -	if (!has_glob_specials(pattern)) {
> +	if (!has_glob_specials(pattern) && (flags & ENSURE_GLOB)) {
>  		/* Append implied '/' '*' if not present. */
> -		strbuf_complete(&real_pattern, '/');
> +		strbuf_complete(normalized_pattern, '/');
>  		/* No need to check for '*', there is none. */
> -		strbuf_addch(&real_pattern, '*');
> +		strbuf_addch(normalized_pattern, '*');
>  	}
> +}
> +
> +int for_each_glob_ref_in(each_ref_fn fn, const char *pattern,
> +	const char *prefix, void *cb_data)
> +{
> +	struct strbuf normalized_pattern = STRBUF_INIT;
> +	struct ref_filter filter;
> +	int ret;
> +
> +	normalize_glob_ref(&normalized_pattern, prefix, pattern, ENSURE_GLOB);
>  
> -	filter.pattern = real_pattern.buf;
> +	filter.pattern = normalized_pattern.buf;
>  	filter.fn = fn;
>  	filter.cb_data = cb_data;
>  	ret = for_each_ref(filter_refs, &filter);
>  
> -	strbuf_release(&real_pattern);
> +	strbuf_release(&normalized_pattern);
>  	return ret;
>  }
>  
> diff --git a/refs.h b/refs.h
> index a02b628c8..9f9a8bb27 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -312,6 +312,22 @@ 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 for_each_rawref(each_ref_fn fn, void *cb_data);
>  
> +/*
> + * Normalizes partial refs to their full qualified form.
> + * If prefix is NULL, will prepend 'refs/' to the pattern if it doesn't start
> + * with 'refs/'. Results in refs/<pattern>
> + *
> + * If prefix is not NULL will result in <prefix>/<pattern>
> + *
> + * If ENSURE_GLOB is set and no glob characters are found in the
> + * pattern, a trailing </><*> will be appended to the result.
> + * (<> characters to avoid breaking C comment syntax)
> + */
> +
> +#define ENSURE_GLOB 1
> +void normalize_glob_ref (struct strbuf *normalized_pattern, const char *prefix,
> +				const char *pattern, int flags);

There shouldn't be a space between the function name and the open
parenthesis.

You have complicated the interface by allowing an `ENSURE_BLOB` flag.
This would make sense if the logic for normalizing the prefix were
tangled up with the logic for adding the suffix. But in fact they are
almost entirely orthogonal [1].

So the interface might be simplified by having two functions,

    void normalize_glob_ref(normalized_pattern, prefix, pattern);
    void ensure_blob(struct strbuf *pattern);

The caller in this patch would call the functions one after the other
(or the `ensure_blob` behavior could be inlined in
`for_each_glob_ref_in()`, since it doesn't yet have any callers). And
the callers introduced in patch 2 would only need to call the first
function.

>  static inline const char *has_glob_specials(const char *pattern)
>  {
>  	return strpbrk(pattern, "?*[");
> 

Michael

[1] I say "almost entirely" because putting them in one function means
that only `pattern` needs to be scanned for glob characters. But that is
an unimportant detail.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-05 13:42   ` Michael Haggerty
@ 2017-11-06  1:23     ` Junio C Hamano
  2017-11-06  2:37       ` Rafael Ascensão
  2017-11-06  7:00       ` Michael Haggerty
  0 siblings, 2 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-06  1:23 UTC (permalink / raw)
  To: Michael Haggerty
  Cc: Rafael Ascensão, git, me, hjemli, pclouds, ilari.liusvaara

Michael Haggerty <mhagger@alum.mit.edu> writes:

> [1] I say "almost entirely" because putting them in one function means
> that only `pattern` needs to be scanned for glob characters. But that is
> an unimportant detail.

That could actually be an important detail, in that even if prefix
has wildcard, we'd still append the trailing "/*" as long as the
pattern does not, right?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-06  1:23     ` Junio C Hamano
@ 2017-11-06  2:37       ` Rafael Ascensão
  2017-11-06  7:00       ` Michael Haggerty
  1 sibling, 0 replies; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-06  2:37 UTC (permalink / raw)
  To: Junio C Hamano, Michael Haggerty
  Cc: git, me, hjemli, pclouds, ilari.liusvaara

On 06/11/17 01:23, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> [1] I say "almost entirely" because putting them in one function means
>> that only `pattern` needs to be scanned for glob characters. But that is
>> an unimportant detail.
> 
> That could actually be an important detail, in that even if prefix
> has wildcard, we'd still append the trailing "/*" as long as the
> pattern does not, right?
> 

> So the interface might be simplified by having two functions,
> 
>     void normalize_glob_ref(normalized_pattern, prefix, pattern);
>     void ensure_blob(struct strbuf *pattern);

I think that flag no longer makes sense. I added it just to allow
'--decorate-refs' work with "exact patterns". And since that has the
ugly side effect of losing the ability to use "shortcut patterns" like
'tags' to refer to 'refs/tags/*', I believe it's a good idea to remove it.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-05  6:17         ` Junio C Hamano
@ 2017-11-06  3:24           ` Rafael Ascensão
  2017-11-06  3:51             ` Junio C Hamano
  2017-11-06  7:09           ` Michael Haggerty
  1 sibling, 1 reply; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-06  3:24 UTC (permalink / raw)
  To: Junio C Hamano, git; +Cc: me, hjemli, mhagger, pclouds, ilari.liusvaara

Would checking the output of ref_exists() make sense here?
By that I mean, only add a trailing '/*' if the ref doesn't exist.

Unless I am missing something obvious this would allow us to keep both
shortcuts and exact patterns.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-06  3:24           ` Rafael Ascensão
@ 2017-11-06  3:51             ` Junio C Hamano
  0 siblings, 0 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-06  3:51 UTC (permalink / raw)
  To: Rafael Ascensão; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

> Would checking the output of ref_exists() make sense here?
> By that I mean, only add a trailing '/*' if the ref doesn't exist.

I do not think it would hurt, but it is not immediately obvious if
the benefit of doing so outweighs the cost of having to make an
extra call to ref_exists().



^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 1/2] refs: extract function to normalize partial refs
  2017-11-06  1:23     ` Junio C Hamano
  2017-11-06  2:37       ` Rafael Ascensão
@ 2017-11-06  7:00       ` Michael Haggerty
  1 sibling, 0 replies; 24+ messages in thread
From: Michael Haggerty @ 2017-11-06  7:00 UTC (permalink / raw)
  To: Junio C Hamano
  Cc: Rafael Ascensão, git, me, hjemli, pclouds, ilari.liusvaara

On 11/06/2017 02:23 AM, Junio C Hamano wrote:
> Michael Haggerty <mhagger@alum.mit.edu> writes:
> 
>> [1] I say "almost entirely" because putting them in one function means
>> that only `pattern` needs to be scanned for glob characters. But that is
>> an unimportant detail.
> 
> That could actually be an important detail, in that even if prefix
> has wildcard, we'd still append the trailing "/*" as long as the
> pattern does not, right?

That's correct, but I was assuming that the prefix would always be a
hard-coded string like "refs/tags/" or maybe "refs/". (That is the case
now.) It doesn't seem very useful to use a prefix like "refs/*/".

Michael

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-05  6:17         ` Junio C Hamano
  2017-11-06  3:24           ` Rafael Ascensão
@ 2017-11-06  7:09           ` Michael Haggerty
  1 sibling, 0 replies; 24+ messages in thread
From: Michael Haggerty @ 2017-11-06  7:09 UTC (permalink / raw)
  To: Junio C Hamano, Rafael Ascensão, git
  Cc: me, hjemli, pclouds, ilari.liusvaara

On 11/05/2017 07:17 AM, Junio C Hamano wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>> Rafael Ascensão <rafa.almas@gmail.com> writes:
>> ...
>>> Because changing the default behavior of that function has
>>> implications on multiple commands which I think shouldn't change. But
>>> at the same time, would be nice to have the logic that deals with
>>> glob-ref patterns all in one place.
>>>
>>> What's the sane way to do this?
>>
>> Learn to type "--decorate-refs="refs/heads/[m]aster", and not twewak
>> the code at all, perhaps.  The users of existing "with no globbing,
>> /* is appended" interface are already used to that way and they do
>> not have to learn a new and inconsistent interface.
>>
>> After all, "I only want to see 'git log' output with 'master'
>> decorated" (i.e. not specifying "this class of refs I can glob by
>> using the naming convention I am using" and instead enumerating the
>> ones you care about) does not sound like a sensible thing people
>> often want to do, so making it follow the other codepath so that
>> people can say "refs/tags" to get "refs/tags/*", while still allowing
>> such a rare but specific and exact one possible, may not sound too
>> bad to me.
> 
> Having said all that, I can imagine another way out might be to
> change the behaviour of this "normalize" thing to add two patterns,
> the original pattern in addition to the original pattern plus "/*",
> when it sees a pattern without any glob.  Many users who relied on
> the current behaviour fed "refs/tags" knowing that it will match
> everything under "refs/tags" i.e. "refs/tags/*", and they cannot
> have a ref that is exactly "refs/tags", so adding the original
> pattern without an extra trailing "/*" would not hurt them.  And
> this will allow you to say "refs/heads/master" when you know you
> want that exact ref, and in such a repository where that original
> pattern without trailing "/*" would be useful, because you cannot
> have "refs/heads/master/one" at the same time, having an extra
> pattern that is the original plus "/*" would not hurt you, either.
> 
> This however needs a bit of thought to see if there are corner cases
> that may result in unexpected and unwanted fallout, and something I
> am reluctant to declare unilaterally that it is a better way to go.

There's some glob-matching code (somewhere? I don't know if it's allowed
everywhere) that allows "**" to mean "zero or one path components. If
"refs/tags" were massaged to be "refs/tags/**", then it would match not only

    refs/tags
    refs/tags/foo

but also

    refs/tags/foo/bar

, which is probably another thing that the user would expect to see.

There's at least some precedent for this kind of expansion: `git
for-each-ref refs/remotes` lists *all* references under that prefix,
even if they have multiple levels.

Michael

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-04  3:49   ` Junio C Hamano
  2017-11-04  7:34     ` Rafael Ascensão
@ 2017-11-06 20:10     ` Jacob Keller
  2017-11-07  0:18       ` Junio C Hamano
  1 sibling, 1 reply; 24+ messages in thread
From: Jacob Keller @ 2017-11-06 20:10 UTC (permalink / raw)
  To: Junio C Hamano, Rafael Ascensão
  Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara



On November 3, 2017 8:49:15 PM PDT, Junio C Hamano <gitster@pobox.com> wrote:
>Rafael Ascensão <rafa.almas@gmail.com> writes:
>
>Why should this be a special case that burdens users to remember one
>more rule?  Wouldn't users find "--decorate-refs=refs/tags" useful
>and it woulld be shorter and nicer than having to say "refs/tags/*"?
>

Actually, I would expect these to behave more like git describes match and exclude which don't have an extra /*. It seems natural to me that glob would always add an extra glob, but.. I don't recall if match and exlude do so.

That being said, if we think the extra glob would not cause problems and generally do what people mean... I guess consistent with --glob would be good... But it's definitely not what I'd expect at first glance.
-- 
Sent from my Android device with K-9 Mail. Please excuse my brevity.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-06 20:10     ` Jacob Keller
@ 2017-11-07  0:18       ` Junio C Hamano
  2017-11-10 13:38         ` Rafael Ascensão
  0 siblings, 1 reply; 24+ messages in thread
From: Junio C Hamano @ 2017-11-07  0:18 UTC (permalink / raw)
  To: Jacob Keller
  Cc: Rafael Ascensão, git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Jacob Keller <jacob.keller@gmail.com> writes:

> On November 3, 2017 8:49:15 PM PDT, Junio C Hamano <gitster@pobox.com> wrote:
>>Rafael Ascensão <rafa.almas@gmail.com> writes:
>>
>>Why should this be a special case that burdens users to remember one
>>more rule?  Wouldn't users find "--decorate-refs=refs/tags" useful
>>and it woulld be shorter and nicer than having to say "refs/tags/*"?
>
> Actually, I would expect these to behave more like git describes
> match and exclude which don't have an extra /*. It seems natural
> to me that glob would always add an extra glob, but.. I don't
> recall if match and exlude do so.

I would have to say that the describe's one is wrong if it does not
match what for_each_glob_ref() does for the log family of commands'
"--branches=<pattern>" etc.  describe.c::get_name() uses positive
and negative patterns, just like log-tree.c::add_ref_decoration()
would with the patch we are discussing, so perhaps the items in
these lists should get the same "normalize" treatment the patch 1/2
of this series brings in to make things consistent?

> That being said, if we think the extra glob would not cause
> problems and generally do what people mean... I guess consistent
> with --glob would be good... But it's definitely not what I'd
> expect at first glance.

FWIW, what describe --match/--exclude do is not what I'd have
expected ;-)  In any case, we spotted an existing inconsistency that
we would want to resolve (the resolution could be "leave it as-is";
I do not think we have thought this through enough yet), which is
good.

Thanks.


^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-07  0:18       ` Junio C Hamano
@ 2017-11-10 13:38         ` Rafael Ascensão
  2017-11-10 17:42           ` Junio C Hamano
  0 siblings, 1 reply; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-10 13:38 UTC (permalink / raw)
  To: Junio C Hamano, Jacob Keller
  Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

On 07/11/17 00:18, Junio C Hamano wrote:
> Jacob Keller <jacob.keller@gmail.com> writes:
> 
> I would have to say that the describe's one is wrong if it does not
> match what for_each_glob_ref() does for the log family of commands'
> "--branches=<pattern>" etc.  describe.c::get_name() uses positive
> and negative patterns, just like log-tree.c::add_ref_decoration()
> would with the patch we are discussing, so perhaps the items in
> these lists should get the same "normalize" treatment the patch 1/2
> of this series brings in to make things consistent?
> 

I agree that describe should receive the "normalize" treatment. However,
and following the same reasoning, why should describe users adopt the
rules imposed by --glob? I could argue they're also used to the way it
works now.

That being said, the suggestion I mentioned earlier would allow to keep
both current behaviors consistent at the expense of the extra call to
refs.c::ref_exists().

+if (!has_glob_specials(pattern) && !ref_exists(normalized_pattern->buf)) {
+        /* Append implied '/' '*' if not present. */
+        strbuf_complete(normalized_pattern, '/');
+        /* No need to check for '*', there is none. */
+        strbuf_addch(normalized_pattern, '*');
+}

But I don't have enough expertise to decide if this consistency is worth 
the extra call to refs.c::ref_exists() or if there are other side-effects
I am not considering.

>> That being said, if we think the extra glob would not cause
>> problems and generally do what people mean... I guess consistent
>> with --glob would be good... But it's definitely not what I'd
>> expect at first glance.

My position is that consistency is good, but the "first glance
expectation" is definitely something important we should take into
consideration.

^ permalink raw reply	[flat|nested] 24+ messages in thread

* Re: [PATCH v1 2/2] log: add option to choose which refs to decorate
  2017-11-10 13:38         ` Rafael Ascensão
@ 2017-11-10 17:42           ` Junio C Hamano
  0 siblings, 0 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-10 17:42 UTC (permalink / raw)
  To: Rafael Ascensão
  Cc: Jacob Keller, git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

> I agree that describe should receive the "normalize" treatment. However,
> and following the same reasoning, why should describe users adopt the
> rules imposed by --glob? I could argue they're also used to the way it
> works now.
>
> That being said, the suggestion I mentioned earlier would allow to keep
> both current behaviors consistent at the expense of the extra call to
> refs.c::ref_exists().

In any case, updating the "describe" for consistency is something we
can and should leave for later, to be done as a separate topic.

While I agree with you that the consistent behaviour between
commands is desirable, and also I agree with you that given a
pattern $X that does not have any glob char, trying to match $X when
a ref whose name exactly is $X exists and trying to match $X/*
otherwise would give us a consistent semantics without hurting any
existing uses, I do not think you need to pay any extra expense of
calling ref_exists() at all to achieve that.

That is because when $X exists, you already know $X/otherthing does
not exist.  And when $X does not exist, $X/otherthing might.  So a
naive implementation would be just to add two patterns $X and $X/*
to the filter list and be done with it.  If you exactly have
refs/heads/master, even with the naive logic may throw both
refs/heads/master and refs/heads/master/* to the filter list,
nothing will match the latter to contaminate your result (and vice
versa).

A bit more clever implementation "just throw in two items" would go
like this.  It is not all that involved:

 - In load_ref_decorations(), before running add_ref_decoration for
   each ref and head ref, iterate over the elements in the refname
   filter list.  For each element:

   - if item->string has a trailing '/', trim that.

   - store NULL in the item->util field for item whose string field
     has a glob char.

   - store something non-NULL (e.g. item->string) for item whose
     string field does not have a glob char.

 - In add_ref_decoration(), where your previous round iterates over
   filter->{include,exclude}, get rid of normalize_glob_ref() and
   use of real_pattern.  Instead do something like:

	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;
	}
	if (matched)
		...

   Of course, you would probably want to encapsulate the logic to
   set matched = 1/0 in a helper function, e.g.

	static int match_ref_pattern(const char *refname,
				     const struct string_list_item *item) {
		int matched = 0;
		... do either wildmatch or head match with tail validation
		... depending on the item->util's NULLness (see above)
		return matched;
	}

   and call that from the two loops for exclude and include list.

Hmm?

^ permalink raw reply	[flat|nested] 24+ messages in thread

* [PATCH v2] log: add option to choose which refs to decorate
  2017-11-04  0:41 [PATCH v1 0/2] Add option to git log to choose which refs receive decoration Rafael Ascensão
  2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
  2017-11-04  0:41 ` [PATCH v1 2/2] log: add option to choose which refs to decorate Rafael Ascensão
@ 2017-11-21 21:33 ` Rafael Ascensão
  2017-11-22  4:18   ` Junio C Hamano
  2 siblings, 1 reply; 24+ messages in thread
From: Rafael Ascensão @ 2017-11-21 21:33 UTC (permalink / raw)
  To: git
  Cc: me, gitster, hjemli, mhagger, pclouds, ilari.liusvaara,
	Rafael Ascensão

When `log --decorate` is used, git will decorate commits with all
available refs. While in most cases this the desired effect, under some
conditions it can lead to excessively verbose output.

Introduce two command line options, `--decorate-refs=<pattern>` and
`--decorate-refs-exclude=<pattern>` to allow the user to select which
refs are used in decoration.

When "--decorate-refs=<pattern>" is given, only the refs that match the
pattern are used in decoration. The refs that match the pattern when
"--decorate-refs-exclude=<pattern>" is given, are never used in
decoration.

These options follow the same convention for mixing negative and
positive patterns across the system, assuming that the inclusive default
is to match all refs available.

 (1) if there is no positive pattern given, pretend as if an
     inclusive default positive pattern was given;

 (2) for each candidate, reject it if it matches no positive
     pattern, or if it matches any one of the negative patterns.

The rules for what is considered a match are slightly different from the
rules used elsewhere.

Commands like `log --glob` assume a trailing '/*' when glob chars are
not present in the pattern. This makes it difficult to specify a single
ref.  On the other hand, commands like `describe --match --all` allow
specifying exact refs, but do not have the convenience of allowing
"shorthand refs" like 'refs/heads' or 'heads' to refer to
'refs/heads/*'.

The commands introduced in this patch consider a match if:

  (a) the pattern contains globs chars,
	and regular pattern matching returns a match.

  (b) the pattern does not contain glob chars,
         and ref '<pattern>' exists, or if ref exists under '<pattern>/'

This allows both behaviours (allowing single refs and shorthand refs)
yet remaining compatible with existent commands.

Helped-by: Kevin Daudt <me@ikke.info>
Helped-by: Junio C Hamano <gitster@pobox.com>
Signed-off-by: Rafael Ascensão <rafa.almas@gmail.com>
---

Notable changes since v1:

  * Do not change refs.c:for_each_glob_ref. Those changes were meant
  to address inconsistencies between commands, but they should be done
  in a separate topic.

  * Use new matching behaviour suggested by Junio for '--decorate-refs*'
  and change documentation/comments to reflect that.

  * Change help strings to clarify the commands expects a pattern
  instead of 'ref'.

  * Fix small inconsistencies on tests, and issues pointed by the
  feedback on the previous version.


 Documentation/git-log.txt |   7 ++++
 builtin/log.c             |  10 ++++-
 log-tree.c                |  24 ++++++++---
 log-tree.h                |   6 ++-
 pretty.c                  |   4 +-
 refs.c                    |  65 +++++++++++++++++++++++++++++
 refs.h                    |  24 +++++++++++
 revision.c                |   2 +-
 t/t4202-log.sh            | 101 ++++++++++++++++++++++++++++++++++++++++++++++
 9 files changed, 232 insertions(+), 11 deletions(-)

diff --git a/Documentation/git-log.txt b/Documentation/git-log.txt
index 32246fdb0..5437f8b0f 100644
--- a/Documentation/git-log.txt
+++ b/Documentation/git-log.txt
@@ -38,6 +38,13 @@ OPTIONS
 	are shown as if 'short' were given, otherwise no ref names are
 	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::
 	Print out the ref name given on the command line by which each
 	commit was reached.
diff --git a/builtin/log.c b/builtin/log.c
index 6c1fa896a..14fdf3916 100644
--- a/builtin/log.c
+++ b/builtin/log.c
@@ -142,11 +142,19 @@ static void cmd_log_init_finish(int argc, const char **argv, const char *prefix,
 	struct userformat_want w;
 	int quiet = 0, source = 0, mailmap = 0;
 	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[] = {
 		OPT__QUIET(&quiet, N_("suppress diff output")),
 		OPT_BOOL(0, "source", &source, N_("show source")),
 		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"),
 		  PARSE_OPT_OPTARG, decorate_callback},
 		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) {
 		rev->show_decorations = 1;
-		load_ref_decorations(decoration_style);
+		load_ref_decorations(&decoration_filter, decoration_style);
 	}
 
 	if (rev->line_level_traverse)
diff --git a/log-tree.c b/log-tree.c
index 3b904f037..fca29d479 100644
--- a/log-tree.c
+++ b/log-tree.c
@@ -94,8 +94,12 @@ static int add_ref_decoration(const char *refname, const struct object_id *oid,
 {
 	struct object *obj;
 	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)) {
 		struct object_id original_oid;
@@ -148,15 +152,23 @@ static int add_graft_decoration(const struct commit_graft *graft, void *cb_data)
 	return 0;
 }
 
-void load_ref_decorations(int flags)
+void load_ref_decorations(struct decoration_filter *filter, int flags)
 {
 	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_flags = flags;
-		for_each_ref(add_ref_decoration, NULL);
-		head_ref(add_ref_decoration, NULL);
-		for_each_commit_graft(add_graft_decoration, NULL);
+		for_each_ref(add_ref_decoration, filter);
+		head_ref(add_ref_decoration, filter);
+		for_each_commit_graft(add_graft_decoration, filter);
 	}
 }
 
diff --git a/log-tree.h b/log-tree.h
index 48f11fb74..deba03518 100644
--- a/log-tree.h
+++ b/log-tree.h
@@ -7,6 +7,10 @@ struct log_info {
 	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);
 void init_log_tree_opt(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,
 			     const char **extra_headers_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
 void fmt_output_commit(struct strbuf *, struct commit *, struct rev_info *);
diff --git a/pretty.c b/pretty.c
index 2f6b0ae6c..f7ce49023 100644
--- a/pretty.c
+++ b/pretty.c
@@ -1186,11 +1186,11 @@ static size_t format_commit_one(struct strbuf *sb, /* in UTF-8 */
 		strbuf_addstr(sb, get_revision_mark(NULL, commit));
 		return 1;
 	case 'd':
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(NULL, DECORATE_SHORT_REFS);
 		format_decorations(sb, commit, c->auto_color);
 		return 1;
 	case 'D':
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(NULL, DECORATE_SHORT_REFS);
 		format_decorations_extended(sb, commit, c->auto_color, "", ", ", "");
 		return 1;
 	case 'g':		/* reflog info */
diff --git a/refs.c b/refs.c
index 339d4318e..20ba82b43 100644
--- a/refs.c
+++ b/refs.c
@@ -242,6 +242,50 @@ int ref_exists(const char *refname)
 	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,
 			   int flags, void *data)
 {
@@ -369,6 +413,27 @@ int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 	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,
 	const char *prefix, void *cb_data)
 {
diff --git a/refs.h b/refs.h
index 18582a408..01be5ae32 100644
--- a/refs.h
+++ b/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 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)
 {
 	return strpbrk(pattern, "?*[");
diff --git a/revision.c b/revision.c
index e2e691dd5..f6a3da5cd 100644
--- a/revision.c
+++ b/revision.c
@@ -1832,7 +1832,7 @@ static int handle_revision_opt(struct rev_info *revs, int argc, const char **arg
 		revs->simplify_by_decoration = 1;
 		revs->limited = 1;
 		revs->prune = 1;
-		load_ref_decorations(DECORATE_SHORT_REFS);
+		load_ref_decorations(NULL, DECORATE_SHORT_REFS);
 	} else if (!strcmp(arg, "--date-order")) {
 		revs->sort_order = REV_SORT_BY_COMMIT_DATE;
 		revs->topo_order = 1;
diff --git a/t/t4202-log.sh b/t/t4202-log.sh
index 8f155da7a..25b1f8cc7 100755
--- a/t/t4202-log.sh
+++ b/t/t4202-log.sh
@@ -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' '
 	git log --oneline --decorate=full >expect.full &&
 	git log --oneline --decorate=short >expect.short &&
-- 
2.15.0


^ permalink raw reply related	[flat|nested] 24+ messages in thread

* Re: [PATCH v2] log: add option to choose which refs to decorate
  2017-11-21 21:33 ` [PATCH v2] " Rafael Ascensão
@ 2017-11-22  4:18   ` Junio C Hamano
  0 siblings, 0 replies; 24+ messages in thread
From: Junio C Hamano @ 2017-11-22  4:18 UTC (permalink / raw)
  To: Rafael Ascensão; +Cc: git, me, hjemli, mhagger, pclouds, ilari.liusvaara

Rafael Ascensão <rafa.almas@gmail.com> writes:

> When `log --decorate` is used, git will decorate commits with all
> available refs. While in most cases this the desired effect, under some

Missing verb.  s/this the/this may give the/; perhaps.

> conditions it can lead to excessively verbose output.

Other than that, I didn't find anything questionable in the
implementation, tests or doc updates.  Nicely done.

Will queue.

Thanks.

^ permalink raw reply	[flat|nested] 24+ messages in thread

end of thread, other threads:[~2017-11-22  4:19 UTC | newest]

Thread overview: 24+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2017-11-04  0:41 [PATCH v1 0/2] Add option to git log to choose which refs receive decoration Rafael Ascensão
2017-11-04  0:41 ` [PATCH v1 1/2] refs: extract function to normalize partial refs Rafael Ascensão
2017-11-04  2:27   ` Junio C Hamano
2017-11-04  7:33     ` Rafael Ascensão
2017-11-04 22:45     ` Kevin Daudt
2017-11-05 13:21       ` Michael Haggerty
2017-11-05 13:42   ` Michael Haggerty
2017-11-06  1:23     ` Junio C Hamano
2017-11-06  2:37       ` Rafael Ascensão
2017-11-06  7:00       ` Michael Haggerty
2017-11-04  0:41 ` [PATCH v1 2/2] log: add option to choose which refs to decorate Rafael Ascensão
2017-11-04  3:49   ` Junio C Hamano
2017-11-04  7:34     ` Rafael Ascensão
2017-11-05  2:00       ` Junio C Hamano
2017-11-05  6:17         ` Junio C Hamano
2017-11-06  3:24           ` Rafael Ascensão
2017-11-06  3:51             ` Junio C Hamano
2017-11-06  7:09           ` Michael Haggerty
2017-11-06 20:10     ` Jacob Keller
2017-11-07  0:18       ` Junio C Hamano
2017-11-10 13:38         ` Rafael Ascensão
2017-11-10 17:42           ` Junio C Hamano
2017-11-21 21:33 ` [PATCH v2] " Rafael Ascensão
2017-11-22  4:18   ` Junio C Hamano

This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.