All of lore.kernel.org
 help / color / mirror / Atom feed
* [PATCH v13 00/12] port tag.c to use ref-filter APIs
@ 2015-08-22  3:39 Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 01/12] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
                   ` (13 more replies)
  0 siblings, 14 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

Part of my GSoC project to port tag.c to use ref-filter APIs. This is
a follow up to porting for-each-ref to use ref-filter APIs.

Version 12 can be found here:
thread.gmane.org/gmane.comp.version-control.git/276133

Changes since v12:
* %(align)...%(end) now quote formats everything in between the atoms
even if they are string literals. 
* Changet the structure of ref_formatting_state to hold ref_formatting_stack
as the stack and quote_value, this ensures that we do not need to copy the 
quote_value to each element of the stack.
* While checking for %(align) atom, use strbuf_split_str().
* In tag.c support usage of --format with -n<num>.

Karthik Nayak (12):
  ref-filter: move `struct atom_value` to ref-filter.c
  ref-filter: introduce ref_formatting_state and ref_formatting_stack
  utf8: add function to align a string into given strbuf
  ref-filter: implement an `align` atom
  ref-filter: add option to filter out tags, branches and remotes
  ref-filter: support printing N lines from tag annotation
  ref-filter: add support to sort by version
  ref-filter: add option to match literal pattern
  tag.c: use 'ref-filter' data structures
  tag.c: use 'ref-filter' APIs
  tag.c: implement '--format' option
  tag.c: implement '--merged' and '--no-merged' options

 Documentation/git-for-each-ref.txt |  12 ++
 Documentation/git-tag.txt          |  27 ++-
 builtin/for-each-ref.c             |   3 +-
 builtin/tag.c                      | 365 +++++++------------------------------
 ref-filter.c                       | 350 +++++++++++++++++++++++++++++++----
 ref-filter.h                       |  32 +++-
 refs.c                             |   9 +
 refs.h                             |   1 +
 t/t6302-for-each-ref-filter.sh     | 105 +++++++++++
 t/t7004-tag.sh                     |  47 ++++-
 utf8.c                             |  21 +++
 utf8.h                             |  15 ++
 12 files changed, 626 insertions(+), 361 deletions(-)


Interdiff:

diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 1997657..06d468e 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -133,7 +133,8 @@ align::
 	`<position>` is either left, right or middle and `<width>` is
 	the total length of the content with alignment. If the
 	contents length is more than the width then no alignment is
-	performed.
+	performed. If used with '--quote' everything in between %(align:..)
+	and %(end) is quoted.
 
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index c2785d9..3803bf7 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -161,22 +161,15 @@ This option is only applicable when listing tags without annotation lines.
 
 <format>::
 	A string that interpolates `%(fieldname)` from the object
-	pointed at by a ref being shown.  If `fieldname` is prefixed
-	with an asterisk (`*`) and the ref points at a tag object, the
-	value for the field in the object tag refers is used.  When
-	unspecified, defaults to `%(refname:short)`.  It also
-	interpolates `%%` to `%`, and `%xx` where `xx` are hex digits
-	interpolates to character with hex code `xx`; for example
-	`%00` interpolates to `\0` (NUL), `%09` to `\t` (TAB) and
-	`%0a` to `\n` (LF).  The fields are same as those in `git
-	for-each-ref`.
+	pointed at by a ref being shown.  The format is the same as
+	that of linkgit:git-for-each-ref[1].  When unspecified,
+	defaults to `%(refname:short)`.
 
 --[no-]merged [<commit>]::
 	Only list tags whose tips are reachable, or not reachable
 	if '--no-merged' is used, from the specified commit ('HEAD'
 	if not specified).
 
-
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
diff --git a/ref-filter.c b/ref-filter.c
index 665221b..f8b8fb7 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -65,18 +65,24 @@ struct align {
 	unsigned int width;
 };
 
-struct ref_formatting_state {
-	struct ref_formatting_state *prev;
+#define REF_FORMATTING_STATE_INIT  { 0, NULL }
+
+struct ref_formatting_stack {
+	struct ref_formatting_stack *prev;
 	struct strbuf output;
-	void (*at_end)(struct ref_formatting_state *state);
+	void (*at_end)(struct ref_formatting_stack *stack);
 	void *cb_data;
+};
+
+struct ref_formatting_state {
 	int quote_style;
+	struct ref_formatting_stack *stack;
 };
 
 struct atom_value {
 	const char *s;
 	struct align *align;
-	void (*handler)(struct atom_value *atomv, struct ref_formatting_state **state);
+	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
 	unsigned long ul; /* used for sorting when not FIELD_STR */
 };
 
@@ -149,19 +155,19 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
 	return at;
 }
 
-static void push_new_state(struct ref_formatting_state **stack)
+static void push_new_stack_element(struct ref_formatting_stack **stack)
 {
-	struct ref_formatting_state *s = xcalloc(1, sizeof(struct ref_formatting_state));
+	struct ref_formatting_stack *s = xcalloc(1, sizeof(struct ref_formatting_stack));
 
 	strbuf_init(&s->output, 0);
 	s->prev = *stack;
 	*stack = s;
 }
 
-static void pop_state(struct ref_formatting_state **stack)
+static void pop_stack_element(struct ref_formatting_stack **stack)
 {
-	struct ref_formatting_state *current = *stack;
-	struct ref_formatting_state *prev = current->prev;
+	struct ref_formatting_stack *current = *stack;
+	struct ref_formatting_stack *prev = current->prev;
 
 	if (prev)
 		strbuf_addbuf(&prev->output, &current->output);
@@ -640,34 +646,49 @@ static inline char *copy_advance(char *dst, const char *src)
 	return dst;
 }
 
-static void align_handler(struct ref_formatting_state *state)
+static void align_handler(struct ref_formatting_stack *stack)
 {
-	struct align *align = (struct align *)state->cb_data;
+	struct align *align = (struct align *)stack->cb_data;
 	struct strbuf s = STRBUF_INIT;
 
-	strbuf_utf8_align(&s, align->position, align->width, state->output.buf);
-	strbuf_reset(&state->output);
-	strbuf_addbuf(&state->output, &s);
+	strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
+	strbuf_swap(&stack->output, &s);
+	strbuf_release(&s);
 	free(align);
 }
 
-static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state **state)
+static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
 {
-	struct ref_formatting_state *new;
+	struct ref_formatting_stack *new;
 
-	push_new_state(state);
-	new = *state;
+	push_new_stack_element(&state->stack);
+	new = state->stack;
 	new->at_end = align_handler;
 	new->cb_data = atomv->align;
 }
 
-static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state **state)
+static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style);
+
+static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
 {
-	struct ref_formatting_state *current = *state;
+	struct ref_formatting_stack *current = state->stack;
+	struct strbuf s = STRBUF_INIT;
+
 	if (!current->at_end)
 		die(_("format: `end` atom used without a supporting atom"));
 	current->at_end(current);
-	pop_state(state);
+	/*
+	 * Whenever we have more than one stack element that means we
+	 * are using a certain modifier atom. In that case we need to
+	 * perform quote formatting.
+	 */
+	if (!state->stack->prev->prev) {
+		perform_quote_formatting(&s, current->output.buf, state->quote_style);
+		strbuf_reset(&current->output);
+		strbuf_addbuf(&current->output, &s);
+	}
+	strbuf_release(&s);
+	pop_stack_element(&state->stack);
 }
 
 /*
@@ -766,25 +787,26 @@ static void populate_value(struct ref_array_item *ref)
 			continue;
 		} else if (skip_prefix(name, "align:", &valp)) {
 			struct align *align = xmalloc(sizeof(struct align));
-			char *ep = strchr(valp, ',');
+			struct strbuf **s = strbuf_split_str(valp, ',', 0);
 
-			if (ep)
-				*ep = '\0';
+			/* If the position is given trim the ',' from the first strbuf */
+			if (s[1])
+				strbuf_remove(s[0], s[0]->len - 1, 1);
 
-			if (strtoul_ui(valp, 10, &align->width))
-				die(_("positive width expected align:%s"), valp);
+			if (strtoul_ui(s[0]->buf, 10, &align->width))
+				die(_("positive width expected align:%s"), s[0]->buf);
 
-			if (!ep || !strcmp(ep + 1, "left"))
+			/* If no position is given, default to ALIGN_LEFT */
+			if (!s[1] || !strcmp(s[1]->buf, "left"))
 				align->position = ALIGN_LEFT;
-			else if (!strcmp(ep + 1, "right"))
+			else if (!strcmp(s[1]->buf, "right"))
 				align->position = ALIGN_RIGHT;
-			else if (!strcmp(ep + 1, "middle"))
+			else if (!strcmp(s[1]->buf, "middle"))
 				align->position = ALIGN_MIDDLE;
 			else
-				die(_("improper format entered align:%s"), ep + 1);
+				die(_("improper format entered align:%s"), s[1]->buf);
 
-			if (ep)
-				*ep = ',';
+			strbuf_list_free(s);
 
 			v->align = align;
 			v->handler = align_atom_handler;
@@ -1378,27 +1400,33 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 	qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
 }
 
-static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style)
 {
-	switch (state->quote_style) {
+	switch (quote_style) {
 	case QUOTE_NONE:
-		strbuf_addstr(&state->output, v->s);
+		strbuf_addstr(s, str);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(&state->output, v->s);
+		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(&state->output, v->s);
+		perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(&state->output, v->s);
+		python_quote_buf(s, str);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(&state->output, v->s);
+		tcl_quote_buf(s, str);
 		break;
 	}
 }
 
+static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+{
+	struct strbuf *s = &state->stack->output;
+	perform_quote_formatting(s, v->s, state->quote_style);
+}
+
 static int hex1(char ch)
 {
 	if ('0' <= ch && ch <= '9')
@@ -1419,6 +1447,8 @@ static int hex2(const char *cp)
 
 static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
 {
+	struct strbuf *s = &state->stack->output;
+
 	while (*cp && (!ep || cp < ep)) {
 		if (*cp == '%') {
 			if (cp[1] == '%')
@@ -1426,13 +1456,13 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
 			else {
 				int ch = hex2(cp + 1);
 				if (0 <= ch) {
-					strbuf_addch(&state->output, ch);
+					strbuf_addch(s, ch);
 					cp += 3;
 					continue;
 				}
 			}
 		}
-		strbuf_addch(&state->output, *cp);
+		strbuf_addch(s, *cp);
 		cp++;
 	}
 }
@@ -1485,25 +1515,34 @@ void show_ref_array_item(struct ref_array_item *info, const char *format,
 {
 	const char *cp, *sp, *ep;
 	struct strbuf *final_buf;
-	struct ref_formatting_state *state = NULL;
+	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
 
-	push_new_state(&state);
-	state->quote_style = quote_style;
+	state.quote_style = quote_style;
+	push_new_stack_element(&state.stack);
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 		struct atom_value *atomv;
 
 		ep = strchr(sp, ')');
 		if (cp < sp)
-			append_literal(cp, sp, state);
+			append_literal(cp, sp, &state);
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
+		/*
+		 * If the atom is a modifier atom, then call the handler function.
+		 * Else, if this is the first element on the stack, then we need to
+		 * format the atom as per the given quote. Else we just add the atom value
+		 * to the current stack element and handle quote formatting at the end.
+		 */
 		if (atomv->handler)
 			atomv->handler(atomv, &state);
-		append_atom(atomv, state);
+		else if (!state.stack->prev)
+			append_atom(atomv, &state);
+		else
+			strbuf_addstr(&state.stack->output, atomv->s);
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
-		append_literal(cp, sp, state);
+		append_literal(cp, sp, &state);
 	}
 	if (need_color_reset_at_eol) {
 		struct atom_value resetv;
@@ -1512,12 +1551,13 @@ void show_ref_array_item(struct ref_array_item *info, const char *format,
 		if (color_parse("reset", color) < 0)
 			die("BUG: couldn't parse 'reset' as a color");
 		resetv.s = color;
-		append_atom(&resetv, state);
+		append_atom(&resetv, &state);
 	}
-	final_buf = &state->output;
+	if (state.stack->prev)
+		die(_("format: `end` atom missing"));
+	final_buf = &state.stack->output;
 	fwrite(final_buf->buf, 1, final_buf->len, stdout);
-	pop_state(&state);
-
+	pop_stack_element(&state.stack);
 	if (lines > 0) {
 		struct object_id oid;
 		hashcpy(oid.hash, info->objectname);
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 1c56879..38c99c9 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -129,6 +129,27 @@ test_expect_success 'right alignment' '
 	test_cmp expect actual
 '
 
+# Everything in between the %(align)...%(end) atom must be quoted, hence we test this by
+# introducing single quote's in %(align)...%(end), which must not be escaped.
+
+sq="'"
+
+test_expect_success 'alignment with format quote' '
+	cat >expect <<-EOF &&
+	refname is ${sq}           ${sq}\\${sq}${sq}master${sq}\\${sq}${sq}           ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}side${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}          ${sq}\\${sq}${sq}odd/spot${sq}\\${sq}${sq}          ${sq}|
+	refname is ${sq}         ${sq}\\${sq}${sq}double-tag${sq}\\${sq}${sq}         ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}four${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}one${sq}\\${sq}${sq}             ${sq}|
+	refname is ${sq}         ${sq}\\${sq}${sq}signed-tag${sq}\\${sq}${sq}         ${sq}|
+	refname is ${sq}           ${sq}\\${sq}${sq}three${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}two${sq}\\${sq}${sq}             ${sq}|
+	EOF
+	git for-each-ref --shell --format="refname is %(align:30,middle)${sq}%(refname:short)${sq}%(end)|" >actual &&
+	test_cmp expect actual
+'
+
 test_expect_success 'setup for version sort' '
 	test_commit foo1.3 &&
 	test_commit foo1.6 &&
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 335396e..3dd2f51 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1519,10 +1519,6 @@ EOF"
 	test_cmp expect actual
 '
 
-test_expect_success '--format cannot be used with -n' '
-	test_must_fail git tag -l -n4 --format="%(refname)"
-'
-
 test_expect_success '--format should list tags as per format given' '
 	cat >expect <<-\EOF &&
 	refname : refs/tags/foo1.10

diff --git a/builtin/tag.c b/builtin/tag.c
index 781d3e5..bbbcaed 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,7 +23,7 @@ static const char * const git_tag_usage[] = {
        N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
        N_("git tag -d <tagname>..."),
        N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
-               "\n\t\t[--[no-]merged [<commit>]] [<pattern>...]"),
+               "\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
        N_("git tag -v <tagname>..."),
        NULL
 };
@@ -40,10 +40,12 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
        if (filter->lines == -1)
                filter->lines = 0;
 
-       if (filter->lines)
-               format = "%(align:16,left)%(refname:short)%(end)";
-       else if (!format)
-               format = "%(refname:short)";
+       if (!format) {
+               if (filter->lines)
+                       format = "%(align:16,left)%(refname:short)%(end)";
+               else
+                       format = "%(refname:short)";
+       }
 
        verify_ref_format(format);
        filter_refs(&array, filter, FILTER_REFS_TAGS);
@@ -401,8 +403,6 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
                        copts.padding = 2;
                        run_column_filter(colopts, &copts);
                }
-               if (format && (filter.lines != -1))
-                       die(_("--format and -n are incompatible"));
                filter.name_patterns = argv;
                ret = list_tags(&filter, sorting, format);
                if (column_active(colopts))

-- 
2.5.0

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

* [PATCH v13 01/12] ref-filter: move `struct atom_value` to ref-filter.c
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack Karthik Nayak
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Since atom_value is only required for the internal working of
ref-filter it doesn't belong in the public header.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 ref-filter.c | 5 +++++
 ref-filter.h | 5 +----
 2 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 46963a5..e53c77e 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -55,6 +55,11 @@ static struct {
 	{ "color" },
 };
 
+struct atom_value {
+	const char *s;
+	unsigned long ul; /* used for sorting when not FIELD_STR */
+};
+
 /*
  * An atom is a valid field atom listed above, possibly prefixed with
  * a "*" to denote deref_tag().
diff --git a/ref-filter.h b/ref-filter.h
index 6bf27d8..45026d0 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -16,10 +16,7 @@
 #define FILTER_REFS_INCLUDE_BROKEN 0x1
 #define FILTER_REFS_ALL 0x2
 
-struct atom_value {
-	const char *s;
-	unsigned long ul; /* used for sorting when not FIELD_STR */
-};
+struct atom_value;
 
 struct ref_sorting {
 	struct ref_sorting *next;
-- 
2.5.0

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

* [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 01/12] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-24 21:56   ` Junio C Hamano
  2015-08-22  3:39 ` [PATCH v13 03/12] utf8: add function to align a string into given strbuf Karthik Nayak
                   ` (11 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Introduce ref_formatting_state which will hold the formatted output
strbuf instead of directly printing to stdout. This will help us in
creating modifier atoms which modify the format specified before
printing to stdout.

Implement a stack machinery for ref_formatting_state, this allows us
to push and pop elements onto the stack. Whenever we pop an element
from the stack, the strbuf from that element is appended to the strbuf
of the next element on the stack, this will allow us to support
nesting of modifier atoms.

Rename some functions to reflect the changes made:
print_value() -> append_atom()
emit()        -> append_literal()

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 ref-filter.c | 78 +++++++++++++++++++++++++++++++++++++++++++++---------------
 1 file changed, 59 insertions(+), 19 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index e53c77e..d0d8df0 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -55,6 +55,18 @@ static struct {
 	{ "color" },
 };
 
+#define REF_FORMATTING_STATE_INIT  { 0, NULL }
+
+struct ref_formatting_stack {
+	struct ref_formatting_stack *prev;
+	struct strbuf output;
+};
+
+struct ref_formatting_state {
+	int quote_style;
+	struct ref_formatting_stack *stack;
+};
+
 struct atom_value {
 	const char *s;
 	unsigned long ul; /* used for sorting when not FIELD_STR */
@@ -129,6 +141,27 @@ int parse_ref_filter_atom(const char *atom, const char *ep)
 	return at;
 }
 
+static void push_new_stack_element(struct ref_formatting_stack **stack)
+{
+	struct ref_formatting_stack *s = xcalloc(1, sizeof(struct ref_formatting_stack));
+
+	strbuf_init(&s->output, 0);
+	s->prev = *stack;
+	*stack = s;
+}
+
+static void pop_stack_element(struct ref_formatting_stack **stack)
+{
+	struct ref_formatting_stack *current = *stack;
+	struct ref_formatting_stack *prev = current->prev;
+
+	if (prev)
+		strbuf_addbuf(&prev->output, &current->output);
+	strbuf_release(&current->output);
+	free(current);
+	*stack = prev;
+}
+
 /*
  * In a format string, find the next occurrence of %(atom).
  */
@@ -1195,30 +1228,27 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 	qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
 }
 
-static void print_value(struct atom_value *v, int quote_style)
+static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
 {
-	struct strbuf sb = STRBUF_INIT;
-	switch (quote_style) {
+	struct strbuf *s = &state->stack->output;
+
+	switch (state->quote_style) {
 	case QUOTE_NONE:
-		fputs(v->s, stdout);
+		strbuf_addstr(s, v->s);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(&sb, v->s);
+		sq_quote_buf(s, v->s);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(&sb, v->s);
+		perl_quote_buf(s, v->s);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(&sb, v->s);
+		python_quote_buf(s, v->s);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(&sb, v->s);
+		tcl_quote_buf(s, v->s);
 		break;
 	}
-	if (quote_style != QUOTE_NONE) {
-		fputs(sb.buf, stdout);
-		strbuf_release(&sb);
-	}
 }
 
 static int hex1(char ch)
@@ -1239,8 +1269,10 @@ static int hex2(const char *cp)
 		return -1;
 }
 
-static void emit(const char *cp, const char *ep)
+static void append_literal(const char *cp, const char *ep, struct ref_formatting_state *state)
 {
+	struct strbuf *s = &state->stack->output;
+
 	while (*cp && (!ep || cp < ep)) {
 		if (*cp == '%') {
 			if (cp[1] == '%')
@@ -1248,13 +1280,13 @@ static void emit(const char *cp, const char *ep)
 			else {
 				int ch = hex2(cp + 1);
 				if (0 <= ch) {
-					putchar(ch);
+					strbuf_addch(s, ch);
 					cp += 3;
 					continue;
 				}
 			}
 		}
-		putchar(*cp);
+		strbuf_addch(s, *cp);
 		cp++;
 	}
 }
@@ -1262,19 +1294,24 @@ static void emit(const char *cp, const char *ep)
 void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
 {
 	const char *cp, *sp, *ep;
+	struct strbuf *final_buf;
+	struct ref_formatting_state state = REF_FORMATTING_STATE_INIT;
+
+	state.quote_style = quote_style;
+	push_new_stack_element(&state.stack);
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 		struct atom_value *atomv;
 
 		ep = strchr(sp, ')');
 		if (cp < sp)
-			emit(cp, sp);
+			append_literal(cp, sp, &state);
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
-		print_value(atomv, quote_style);
+		append_atom(atomv, &state);
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
-		emit(cp, sp);
+		append_literal(cp, sp, &state);
 	}
 	if (need_color_reset_at_eol) {
 		struct atom_value resetv;
@@ -1283,8 +1320,11 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 		if (color_parse("reset", color) < 0)
 			die("BUG: couldn't parse 'reset' as a color");
 		resetv.s = color;
-		print_value(&resetv, quote_style);
+		append_atom(&resetv, &state);
 	}
+	final_buf = &state.stack->output;
+	fwrite(final_buf->buf, 1, final_buf->len, stdout);
+	pop_stack_element(&state.stack);
 	putchar('\n');
 }
 
-- 
2.5.0

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

* [PATCH v13 03/12] utf8: add function to align a string into given strbuf
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 01/12] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 04/12] ref-filter: implement an `align` atom Karthik Nayak
                   ` (10 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Add strbuf_utf8_align() which will align a given string into a strbuf
as per given align_type and width. If the width is greater than the
string length then no alignment is performed.

Helped-by: Eric Sunshine <sunshine@sunshineco.com>
Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 utf8.c | 21 +++++++++++++++++++++
 utf8.h | 15 +++++++++++++++
 2 files changed, 36 insertions(+)

diff --git a/utf8.c b/utf8.c
index 28e6d76..00e10c8 100644
--- a/utf8.c
+++ b/utf8.c
@@ -644,3 +644,24 @@ int skip_utf8_bom(char **text, size_t len)
 	*text += strlen(utf8_bom);
 	return 1;
 }
+
+void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
+		       const char *s)
+{
+	int slen = strlen(s);
+	int display_len = utf8_strnwidth(s, slen, 0);
+	int utf8_compensation = slen - display_len;
+
+	if (display_len >= width) {
+		strbuf_addstr(buf, s);
+		return;
+	}
+
+	if (position == ALIGN_LEFT)
+		strbuf_addf(buf, "%-*s", width + utf8_compensation, s);
+	else if (position == ALIGN_MIDDLE) {
+		int left = (width - display_len) / 2;
+		strbuf_addf(buf, "%*s%-*s", left, "", width - left + utf8_compensation, s);
+	} else if (position == ALIGN_RIGHT)
+		strbuf_addf(buf, "%*s", width + utf8_compensation, s);
+}
diff --git a/utf8.h b/utf8.h
index 5a9e94b..7930b44 100644
--- a/utf8.h
+++ b/utf8.h
@@ -55,4 +55,19 @@ int mbs_chrlen(const char **text, size_t *remainder_p, const char *encoding);
  */
 int is_hfs_dotgit(const char *path);
 
+typedef enum {
+	ALIGN_LEFT,
+	ALIGN_MIDDLE,
+	ALIGN_RIGHT
+} align_type;
+
+/*
+ * Align the string given and store it into a strbuf as per the
+ * 'position' and 'width'. If the given string length is larger than
+ * 'width' than then the input string is not truncated and no
+ * alignment is done.
+ */
+void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
+		       const char *s);
+
 #endif
-- 
2.5.0

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

* [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (2 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 03/12] utf8: add function to align a string into given strbuf Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-24 22:13   ` Junio C Hamano
  2015-08-22  3:39 ` [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes Karthik Nayak
                   ` (9 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Implement an `align` atom which left-, middle-, or right-aligns the
content between %(align:..) and %(end).

It is followed by `:<width>,<position>`, where the `<position>` is
either left, right or middle and `<width>` is the size of the area
into which the content will be placed. If the content between
%(align:) and %(end) is more than the width then no alignment is
performed. e.g. to align a refname atom to the middle with a total
width of 40 we can do: --format="%(align:middle,40)%(refname)%(end)".

We now have a `handler()` for each atom_value which will be called
when that atom_value is being parsed, and similarly an `at_end`
function for each element of the stack which is to be called when the
`end` atom is encountered. Using this we implement the `align` atom
which aligns the given strbuf by calling `strbuf_utf8_align()` from
utf8.c.

Extract perform_quote_formatting() from append_atom(). Given a string
a quote_value and a strbuf, perform_quote_formatting() formats the
string based on the quote_value and stores it into the strbuf.

Ensure that quote formatting is performed on the whole of
%(align)...%(end) rather than individual atoms. We do this by skipping
individual quote formatting for atoms whenever the stack has more than
one element, and performing formatting for the entire stack element
when the `%(end)` atoms is encountered.

Add documentation and tests for the same.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-for-each-ref.txt |   9 +++
 ref-filter.c                       | 124 ++++++++++++++++++++++++++++++++++---
 t/t6302-for-each-ref-filter.sh     |  69 +++++++++++++++++++++
 3 files changed, 192 insertions(+), 10 deletions(-)

diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index e49d578..943975d 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -127,6 +127,15 @@ color::
 	Change output color.  Followed by `:<colorname>`, where names
 	are described in `color.branch.*`.
 
+align::
+	Left-, middle-, or right-align the content between %(align:..)
+	and %(end). Followed by `:<width>,<position>`, where the
+	`<position>` is either left, right or middle and `<width>` is
+	the total length of the content with alignment. If the
+	contents length is more than the width then no alignment is
+	performed. If used with '--quote' everything in between %(align:..)
+	and %(end) is quoted.
+
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
 be used to specify the value in the header field.
diff --git a/ref-filter.c b/ref-filter.c
index d0d8df0..ffec10a 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -10,6 +10,7 @@
 #include "quote.h"
 #include "ref-filter.h"
 #include "revision.h"
+#include "utf8.h"
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 
@@ -53,6 +54,13 @@ static struct {
 	{ "flag" },
 	{ "HEAD" },
 	{ "color" },
+	{ "align" },
+	{ "end" },
+};
+
+struct align {
+	align_type position;
+	unsigned int width;
 };
 
 #define REF_FORMATTING_STATE_INIT  { 0, NULL }
@@ -60,6 +68,8 @@ static struct {
 struct ref_formatting_stack {
 	struct ref_formatting_stack *prev;
 	struct strbuf output;
+	void (*at_end)(struct ref_formatting_stack *stack);
+	void *cb_data;
 };
 
 struct ref_formatting_state {
@@ -69,6 +79,8 @@ struct ref_formatting_state {
 
 struct atom_value {
 	const char *s;
+	struct align *align;
+	void (*handler)(struct atom_value *atomv, struct ref_formatting_state *state);
 	unsigned long ul; /* used for sorting when not FIELD_STR */
 };
 
@@ -632,6 +644,51 @@ static inline char *copy_advance(char *dst, const char *src)
 	return dst;
 }
 
+static void align_handler(struct ref_formatting_stack *stack)
+{
+	struct align *align = (struct align *)stack->cb_data;
+	struct strbuf s = STRBUF_INIT;
+
+	strbuf_utf8_align(&s, align->position, align->width, stack->output.buf);
+	strbuf_swap(&stack->output, &s);
+	strbuf_release(&s);
+	free(align);
+}
+
+static void align_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+	struct ref_formatting_stack *new;
+
+	push_new_stack_element(&state->stack);
+	new = state->stack;
+	new->at_end = align_handler;
+	new->cb_data = atomv->align;
+}
+
+static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style);
+
+static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+	struct ref_formatting_stack *current = state->stack;
+	struct strbuf s = STRBUF_INIT;
+
+	if (!current->at_end)
+		die(_("format: `end` atom used without a supporting atom"));
+	current->at_end(current);
+	/*
+	 * Whenever we have more than one stack element that means we
+	 * are using a certain modifier atom. In that case we need to
+	 * perform quote formatting.
+	 */
+	if (!state->stack->prev->prev) {
+		perform_quote_formatting(&s, current->output.buf, state->quote_style);
+		strbuf_reset(&current->output);
+		strbuf_addbuf(&current->output, &s);
+	}
+	strbuf_release(&s);
+	pop_stack_element(&state->stack);
+}
+
 /*
  * Parse the object referred by ref, and grab needed value.
  */
@@ -660,6 +717,7 @@ static void populate_value(struct ref_array_item *ref)
 		int deref = 0;
 		const char *refname;
 		const char *formatp;
+		const char *valp;
 		struct branch *branch = NULL;
 
 		if (*name == '*') {
@@ -725,6 +783,35 @@ static void populate_value(struct ref_array_item *ref)
 			else
 				v->s = " ";
 			continue;
+		} else if (skip_prefix(name, "align:", &valp)) {
+			struct align *align = xmalloc(sizeof(struct align));
+			struct strbuf **s = strbuf_split_str(valp, ',', 0);
+
+			/* If the position is given trim the ',' from the first strbuf */
+			if (s[1])
+				strbuf_remove(s[0], s[0]->len - 1, 1);
+
+			if (strtoul_ui(s[0]->buf, 10, &align->width))
+				die(_("positive width expected align:%s"), s[0]->buf);
+
+			/* If no position is given, default to ALIGN_LEFT */
+			if (!s[1] || !strcmp(s[1]->buf, "left"))
+				align->position = ALIGN_LEFT;
+			else if (!strcmp(s[1]->buf, "right"))
+				align->position = ALIGN_RIGHT;
+			else if (!strcmp(s[1]->buf, "middle"))
+				align->position = ALIGN_MIDDLE;
+			else
+				die(_("improper format entered align:%s"), s[1]->buf);
+
+			strbuf_list_free(s);
+
+			v->align = align;
+			v->handler = align_atom_handler;
+			continue;
+		} else if (!strcmp(name, "end")) {
+			v->handler = end_atom_handler;
+			continue;
 		} else
 			continue;
 
@@ -1228,29 +1315,33 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 	qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
 }
 
-static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style)
 {
-	struct strbuf *s = &state->stack->output;
-
-	switch (state->quote_style) {
+	switch (quote_style) {
 	case QUOTE_NONE:
-		strbuf_addstr(s, v->s);
+		strbuf_addstr(s, str);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(s, v->s);
+		sq_quote_buf(s, str);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(s, v->s);
+		perl_quote_buf(s, str);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(s, v->s);
+		python_quote_buf(s, str);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(s, v->s);
+		tcl_quote_buf(s, str);
 		break;
 	}
 }
 
+static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
+{
+	struct strbuf *s = &state->stack->output;
+	perform_quote_formatting(s, v->s, state->quote_style);
+}
+
 static int hex1(char ch)
 {
 	if ('0' <= ch && ch <= '9')
@@ -1307,7 +1398,18 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 		if (cp < sp)
 			append_literal(cp, sp, &state);
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
-		append_atom(atomv, &state);
+		/*
+		 * If the atom is a modifier atom, then call the handler function.
+		 * Else, if this is the first element on the stack, then we need to
+		 * format the atom as per the given quote. Else we just add the atom value
+		 * to the current stack element and handle quote formatting at the end.
+		 */
+		if (atomv->handler)
+			atomv->handler(atomv, &state);
+		else if (!state.stack->prev)
+			append_atom(atomv, &state);
+		else
+			strbuf_addstr(&state.stack->output, atomv->s);
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
@@ -1322,6 +1424,8 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 		resetv.s = color;
 		append_atom(&resetv, &state);
 	}
+	if (state.stack->prev)
+		die(_("format: `end` atom missing"));
 	final_buf = &state.stack->output;
 	fwrite(final_buf->buf, 1, final_buf->len, stdout);
 	pop_stack_element(&state.stack);
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 505a360..227992b 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -81,4 +81,73 @@ test_expect_success 'filtering with --contains' '
 	test_cmp expect actual
 '
 
+test_expect_success 'left alignment' '
+	cat >expect <<-\EOF &&
+	refname is refs/heads/master  |refs/heads/master
+	refname is refs/heads/side    |refs/heads/side
+	refname is refs/odd/spot      |refs/odd/spot
+	refname is refs/tags/double-tag|refs/tags/double-tag
+	refname is refs/tags/four     |refs/tags/four
+	refname is refs/tags/one      |refs/tags/one
+	refname is refs/tags/signed-tag|refs/tags/signed-tag
+	refname is refs/tags/three    |refs/tags/three
+	refname is refs/tags/two      |refs/tags/two
+	EOF
+	git for-each-ref --format="%(align:30,left)refname is %(refname)%(end)|%(refname)" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'middle alignment' '
+	cat >expect <<-\EOF &&
+	| refname is refs/heads/master |refs/heads/master
+	|  refname is refs/heads/side  |refs/heads/side
+	|   refname is refs/odd/spot   |refs/odd/spot
+	|refname is refs/tags/double-tag|refs/tags/double-tag
+	|  refname is refs/tags/four   |refs/tags/four
+	|   refname is refs/tags/one   |refs/tags/one
+	|refname is refs/tags/signed-tag|refs/tags/signed-tag
+	|  refname is refs/tags/three  |refs/tags/three
+	|   refname is refs/tags/two   |refs/tags/two
+	EOF
+	git for-each-ref --format="|%(align:30,middle)refname is %(refname)%(end)|%(refname)" >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success 'right alignment' '
+	cat >expect <<-\EOF &&
+	|  refname is refs/heads/master|refs/heads/master
+	|    refname is refs/heads/side|refs/heads/side
+	|      refname is refs/odd/spot|refs/odd/spot
+	|refname is refs/tags/double-tag|refs/tags/double-tag
+	|     refname is refs/tags/four|refs/tags/four
+	|      refname is refs/tags/one|refs/tags/one
+	|refname is refs/tags/signed-tag|refs/tags/signed-tag
+	|    refname is refs/tags/three|refs/tags/three
+	|      refname is refs/tags/two|refs/tags/two
+	EOF
+	git for-each-ref --format="|%(align:30,right)refname is %(refname)%(end)|%(refname)" >actual &&
+	test_cmp expect actual
+'
+
+# Everything in between the %(align)...%(end) atom must be quoted, hence we test this by
+# introducing single quote's in %(align)...%(end), which must not be escaped.
+
+sq="'"
+
+test_expect_success 'alignment with format quote' '
+	cat >expect <<-EOF &&
+	refname is ${sq}           ${sq}\\${sq}${sq}master${sq}\\${sq}${sq}           ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}side${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}          ${sq}\\${sq}${sq}odd/spot${sq}\\${sq}${sq}          ${sq}|
+	refname is ${sq}         ${sq}\\${sq}${sq}double-tag${sq}\\${sq}${sq}         ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}four${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}one${sq}\\${sq}${sq}             ${sq}|
+	refname is ${sq}         ${sq}\\${sq}${sq}signed-tag${sq}\\${sq}${sq}         ${sq}|
+	refname is ${sq}           ${sq}\\${sq}${sq}three${sq}\\${sq}${sq}            ${sq}|
+	refname is ${sq}            ${sq}\\${sq}${sq}two${sq}\\${sq}${sq}             ${sq}|
+	EOF
+	git for-each-ref --shell --format="refname is %(align:30,middle)${sq}%(refname:short)${sq}%(end)|" >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.5.0

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

* [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (3 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 04/12] ref-filter: implement an `align` atom Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-24 22:24   ` Junio C Hamano
  2015-08-26 16:10   ` Michael Haggerty
  2015-08-22  3:39 ` [PATCH v13 06/12] ref-filter: support printing N lines from tag annotation Karthik Nayak
                   ` (8 subsequent siblings)
  13 siblings, 2 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

Add a function called 'for_each_reftype_fullpath()' to refs.{c,h}
which iterates through each ref for the given path without trimming
the path and also accounting for broken refs, if mentioned.

Add 'filter_ref_kind()' in ref-filter.c to check the kind of ref being
handled and return the kind to 'ref_filter_handler()', where we
discard refs which we do not need and assign the kind to needed refs.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 ref-filter.h | 12 ++++++++++--
 refs.c       |  9 +++++++++
 refs.h       |  1 +
 4 files changed, 74 insertions(+), 7 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index ffec10a..d5fae1a 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1123,6 +1123,36 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
 	return ref;
 }
 
+static int filter_ref_kind(struct ref_filter *filter, const char *refname)
+{
+	unsigned int i;
+
+	static struct {
+		const char *prefix;
+		unsigned int kind;
+	} ref_kind[] = {
+		{ "refs/heads/" , FILTER_REFS_BRANCHES },
+		{ "refs/remotes/" , FILTER_REFS_REMOTES },
+		{ "refs/tags/", FILTER_REFS_TAGS}
+	};
+
+	if (filter->kind == FILTER_REFS_BRANCHES)
+		return FILTER_REFS_BRANCHES;
+	else if (filter->kind == FILTER_REFS_REMOTES)
+		return FILTER_REFS_REMOTES;
+	else if (filter->kind == FILTER_REFS_TAGS)
+		return FILTER_REFS_TAGS;
+	else if (!strcmp(refname, "HEAD"))
+		return FILTER_REFS_DETACHED_HEAD;
+
+	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
+		if (starts_with(refname, ref_kind[i].prefix))
+			return ref_kind[i].kind;
+	}
+
+	return FILTER_REFS_OTHERS;
+}
+
 /*
  * A call-back given to for_each_ref().  Filter refs and keep them for
  * later object processing.
@@ -1133,6 +1163,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	struct ref_filter *filter = ref_cbdata->filter;
 	struct ref_array_item *ref;
 	struct commit *commit = NULL;
+	unsigned int kind;
 
 	if (flag & REF_BAD_NAME) {
 		warning("ignoring ref with broken name %s", refname);
@@ -1144,6 +1175,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 		return 0;
 	}
 
+	kind = filter_ref_kind(filter, refname);
+	if (!(kind & filter->kind))
+		return 0;
+
 	if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
 		return 0;
 
@@ -1175,6 +1210,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 
 	REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
 	ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
+	ref->kind = kind;
 	return 0;
 }
 
@@ -1251,16 +1287,29 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
 {
 	struct ref_filter_cbdata ref_cbdata;
 	int ret = 0;
+	unsigned int broken = 0;
 
 	ref_cbdata.array = array;
 	ref_cbdata.filter = filter;
 
 	/*  Simple per-ref filtering */
-	if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
-		ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
-	else if (type & FILTER_REFS_ALL)
-		ret = for_each_ref(ref_filter_handler, &ref_cbdata);
-	else if (type)
+	if (type & FILTER_REFS_INCLUDE_BROKEN) {
+		type &= ~FILTER_REFS_INCLUDE_BROKEN;
+		broken = 1;
+	}
+
+	filter->kind = type;
+	if (type == FILTER_REFS_BRANCHES)
+		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
+	else if (type == FILTER_REFS_REMOTES)
+		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
+	else if (type == FILTER_REFS_TAGS)
+		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
+	else if (type & FILTER_REFS_ALL) {
+		ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
+		if (type & FILTER_REFS_DETACHED_HEAD)
+			head_ref(ref_filter_handler, &ref_cbdata);
+	} else
 		die("filter_refs: invalid type");
 
 	/*  Filters that need revision walking */
diff --git a/ref-filter.h b/ref-filter.h
index 45026d0..99f081b 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -13,8 +13,14 @@
 #define QUOTE_PYTHON 4
 #define QUOTE_TCL 8
 
-#define FILTER_REFS_INCLUDE_BROKEN 0x1
-#define FILTER_REFS_ALL 0x2
+#define FILTER_REFS_INCLUDE_BROKEN 0x0001
+#define FILTER_REFS_TAGS           0x0002
+#define FILTER_REFS_BRANCHES       0x0004
+#define FILTER_REFS_REMOTES        0x0008
+#define FILTER_REFS_OTHERS         0x0010
+#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
+				    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
+#define FILTER_REFS_DETACHED_HEAD  0x0020
 
 struct atom_value;
 
@@ -27,6 +33,7 @@ struct ref_sorting {
 struct ref_array_item {
 	unsigned char objectname[20];
 	int flag;
+	unsigned int kind;
 	const char *symref;
 	struct commit *commit;
 	struct atom_value *value;
@@ -51,6 +58,7 @@ struct ref_filter {
 	struct commit *merge_commit;
 
 	unsigned int with_commit_tag_algo : 1;
+	unsigned int kind;
 };
 
 struct ref_filter_cbdata {
diff --git a/refs.c b/refs.c
index 4e15f60..3266617 100644
--- a/refs.c
+++ b/refs.c
@@ -2150,6 +2150,15 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
 			       strlen(git_replace_ref_base), 0, cb_data);
 }
 
+int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data)
+{
+	unsigned int flag = 0;
+
+	if (broken)
+		flag = DO_FOR_EACH_INCLUDE_BROKEN;
+	return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
+}
+
 int head_ref_namespaced(each_ref_fn fn, void *cb_data)
 {
 	struct strbuf buf = STRBUF_INIT;
diff --git a/refs.h b/refs.h
index e9a5f32..6e913ee 100644
--- a/refs.h
+++ b/refs.h
@@ -179,6 +179,7 @@ extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
 extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
+extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data);
 
 extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
 extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
-- 
2.5.0

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

* [PATCH v13 06/12] ref-filter: support printing N lines from tag annotation
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (4 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 07/12] ref-filter: add support to sort by version Karthik Nayak
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

In 'tag.c' we can print N lines from the annotation of the tag using
the '-n<num>' option. Copy code from 'tag.c' to 'ref-filter' and
modify 'ref-filter' to support printing of N lines from the annotation
of tags.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 builtin/for-each-ref.c |  2 +-
 builtin/tag.c          |  4 ++++
 ref-filter.c           | 51 +++++++++++++++++++++++++++++++++++++++++++++++++-
 ref-filter.h           | 11 ++++++++---
 4 files changed, 63 insertions(+), 5 deletions(-)

diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 40f343b..e4a4f8a 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -74,7 +74,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 	if (!maxcount || array.nr < maxcount)
 		maxcount = array.nr;
 	for (i = 0; i < maxcount; i++)
-		show_ref_array_item(array.items[i], format, quote_style);
+		show_ref_array_item(array.items[i], format, quote_style, 0);
 	ref_array_clear(&array);
 	return 0;
 }
diff --git a/builtin/tag.c b/builtin/tag.c
index 471d6b1..0fc7557 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -185,6 +185,10 @@ static enum contains_result contains(struct commit *candidate,
 	return contains_test(candidate, want);
 }
 
+/*
+ * Currently duplicated in ref-filter, will eventually be removed as
+ * we port tag.c to use ref-filter APIs.
+ */
 static void show_tag_lines(const struct object_id *oid, int lines)
 {
 	int i;
diff --git a/ref-filter.c b/ref-filter.c
index d5fae1a..515147b 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1431,7 +1431,51 @@ static void append_literal(const char *cp, const char *ep, struct ref_formatting
 	}
 }
 
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
+/*
+ * If 'lines' is greater than 0, print that many lines from the given
+ * object_id 'oid'.
+ */
+static void show_tag_lines(const struct object_id *oid, int lines)
+{
+	int i;
+	unsigned long size;
+	enum object_type type;
+	char *buf, *sp, *eol;
+	size_t len;
+
+	buf = read_sha1_file(oid->hash, &type, &size);
+	if (!buf)
+		die_errno("unable to read object %s", oid_to_hex(oid));
+	if (type != OBJ_COMMIT && type != OBJ_TAG)
+		goto free_return;
+	if (!size)
+		die("an empty %s object %s?",
+		    typename(type), oid_to_hex(oid));
+
+	/* skip header */
+	sp = strstr(buf, "\n\n");
+	if (!sp)
+		goto free_return;
+
+	/* only take up to "lines" lines, and strip the signature from a tag */
+	if (type == OBJ_TAG)
+		size = parse_signature(buf, size);
+	for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
+		if (i)
+			printf("\n    ");
+		eol = memchr(sp, '\n', size - (sp - buf));
+		len = eol ? eol - sp : size - (sp - buf);
+		fwrite(sp, len, 1, stdout);
+		if (!eol)
+			break;
+		sp = eol + 1;
+	}
+free_return:
+	free(buf);
+}
+
+void show_ref_array_item(struct ref_array_item *info, const char *format,
+			 int quote_style, unsigned int lines)
 {
 	const char *cp, *sp, *ep;
 	struct strbuf *final_buf;
@@ -1478,6 +1522,11 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 	final_buf = &state.stack->output;
 	fwrite(final_buf->buf, 1, final_buf->len, stdout);
 	pop_stack_element(&state.stack);
+	if (lines > 0) {
+		struct object_id oid;
+		hashcpy(oid.hash, info->objectname);
+		show_tag_lines(&oid, lines);
+	}
 	putchar('\n');
 }
 
diff --git a/ref-filter.h b/ref-filter.h
index 99f081b..c599ea2 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -58,7 +58,8 @@ struct ref_filter {
 	struct commit *merge_commit;
 
 	unsigned int with_commit_tag_algo : 1;
-	unsigned int kind;
+	unsigned int kind,
+		lines;
 };
 
 struct ref_filter_cbdata {
@@ -90,8 +91,12 @@ int parse_ref_filter_atom(const char *atom, const char *ep);
 int verify_ref_format(const char *format);
 /*  Sort the given ref_array as per the ref_sorting provided */
 void ref_array_sort(struct ref_sorting *sort, struct ref_array *array);
-/*  Print the ref using the given format and quote_style */
-void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style);
+/*
+ * Print the ref using the given format and quote_style. If 'lines' > 0,
+ * print that many lines of the the given ref.
+ */
+void show_ref_array_item(struct ref_array_item *info, const char *format,
+			 int quote_style, unsigned int lines);
 /*  Callback function for parsing the sort option */
 int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset);
 /*  Default sort option based on refname */
-- 
2.5.0

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

* [PATCH v13 07/12] ref-filter: add support to sort by version
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (5 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 06/12] ref-filter: support printing N lines from tag annotation Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 08/12] ref-filter: add option to match literal pattern Karthik Nayak
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

Add support to sort by version using the "v:refname" and
"version:refname" option. This is achieved by using the 'versioncmp()'
function as the comparing function for qsort.

This option is included to support sorting by versions in `git tag -l`
which will eventually be ported to use ref-filter APIs.

Add documentation and tests for the same.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-for-each-ref.txt |  3 +++
 ref-filter.c                       | 15 ++++++++++-----
 ref-filter.h                       |  3 ++-
 t/t6302-for-each-ref-filter.sh     | 36 ++++++++++++++++++++++++++++++++++++
 4 files changed, 51 insertions(+), 6 deletions(-)

diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 943975d..06d468e 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -154,6 +154,9 @@ For sorting purposes, fields with numeric values sort in numeric
 order (`objectsize`, `authordate`, `committerdate`, `taggerdate`).
 All other fields are used to sort in their byte-value order.
 
+There is also an option to sort by versions, this can be done by using
+the fieldname `version:refname` or its alias `v:refname`.
+
 In any case, a field name that refers to a field inapplicable to
 the object referred by the ref does not cause an error.  It
 returns an empty string instead.
diff --git a/ref-filter.c b/ref-filter.c
index 515147b..b4a7d72 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -11,6 +11,8 @@
 #include "ref-filter.h"
 #include "revision.h"
 #include "utf8.h"
+#include "git-compat-util.h"
+#include "version.h"
 
 typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;
 
@@ -1327,19 +1329,19 @@ static int cmp_ref_sorting(struct ref_sorting *s, struct ref_array_item *a, stru
 
 	get_ref_atom_value(a, s->atom, &va);
 	get_ref_atom_value(b, s->atom, &vb);
-	switch (cmp_type) {
-	case FIELD_STR:
+	if (s->version)
+		cmp = versioncmp(va->s, vb->s);
+	else if (cmp_type == FIELD_STR)
 		cmp = strcmp(va->s, vb->s);
-		break;
-	default:
+	else {
 		if (va->ul < vb->ul)
 			cmp = -1;
 		else if (va->ul == vb->ul)
 			cmp = 0;
 		else
 			cmp = 1;
-		break;
 	}
+
 	return (s->reverse) ? -cmp : cmp;
 }
 
@@ -1559,6 +1561,9 @@ int parse_opt_ref_sorting(const struct option *opt, const char *arg, int unset)
 		s->reverse = 1;
 		arg++;
 	}
+	if (skip_prefix(arg, "version:", &arg) ||
+	    skip_prefix(arg, "v:", &arg))
+		s->version = 1;
 	len = strlen(arg);
 	s->atom = parse_ref_filter_atom(arg, arg+len);
 	return 0;
diff --git a/ref-filter.h b/ref-filter.h
index c599ea2..5aa2f40 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -27,7 +27,8 @@ struct atom_value;
 struct ref_sorting {
 	struct ref_sorting *next;
 	int atom; /* index into used_atom array (internal) */
-	unsigned reverse : 1;
+	unsigned reverse : 1,
+		version : 1;
 };
 
 struct ref_array_item {
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 227992b..38c99c9 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -150,4 +150,40 @@ test_expect_success 'alignment with format quote' '
 	test_cmp expect actual
 '
 
+test_expect_success 'setup for version sort' '
+	test_commit foo1.3 &&
+	test_commit foo1.6 &&
+	test_commit foo1.10
+'
+
+test_expect_success 'version sort' '
+	git for-each-ref --sort=version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+	cat >expect <<-\EOF &&
+	foo1.3
+	foo1.6
+	foo1.10
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'version sort (shortened)' '
+	git for-each-ref --sort=v:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+	cat >expect <<-\EOF &&
+	foo1.3
+	foo1.6
+	foo1.10
+	EOF
+	test_cmp expect actual
+'
+
+test_expect_success 'reverse version sort' '
+	git for-each-ref --sort=-version:refname --format="%(refname:short)" refs/tags/ | grep "foo" >actual &&
+	cat >expect <<-\EOF &&
+	foo1.10
+	foo1.6
+	foo1.3
+	EOF
+	test_cmp expect actual
+'
+
 test_done
-- 
2.5.0

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

* [PATCH v13 08/12] ref-filter: add option to match literal pattern
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (6 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 07/12] ref-filter: add support to sort by version Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 09/12] tag.c: use 'ref-filter' data structures Karthik Nayak
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

Since 'ref-filter' only has an option to match path names add an
option for plain fnmatch pattern-matching.

This is to support the pattern matching options which are used in `git
tag -l` and `git branch -l` where we can match patterns like `git tag
-l foo*` which would match all tags which has a "foo*" pattern.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 builtin/for-each-ref.c |  1 +
 ref-filter.c           | 40 +++++++++++++++++++++++++++++++++++++---
 ref-filter.h           |  3 ++-
 3 files changed, 40 insertions(+), 4 deletions(-)

diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e4a4f8a..3ad6a64 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -68,6 +68,7 @@ int cmd_for_each_ref(int argc, const char **argv, const char *prefix)
 	git_config(git_default_config, NULL);
 
 	filter.name_patterns = argv;
+	filter.match_as_path = 1;
 	filter_refs(&array, &filter, FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN);
 	ref_array_sort(sorting, &array);
 
diff --git a/ref-filter.c b/ref-filter.c
index b4a7d72..f8b8fb7 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1055,9 +1055,33 @@ static int commit_contains(struct ref_filter *filter, struct commit *commit)
 
 /*
  * Return 1 if the refname matches one of the patterns, otherwise 0.
+ * A pattern can be a literal prefix (e.g. a refname "refs/heads/master"
+ * matches a pattern "refs/heads/mas") or a wildcard (e.g. the same ref
+ * matches "refs/heads/mas*", too).
+ */
+static int match_pattern(const char **patterns, const char *refname)
+{
+	/*
+	 * When no '--format' option is given we need to skip the prefix
+	 * for matching refs of tags and branches.
+	 */
+	(void)(skip_prefix(refname, "refs/tags/", &refname) ||
+	       skip_prefix(refname, "refs/heads/", &refname) ||
+	       skip_prefix(refname, "refs/remotes/", &refname) ||
+	       skip_prefix(refname, "refs/", &refname));
+
+	for (; *patterns; patterns++) {
+		if (!wildmatch(*patterns, refname, 0, NULL))
+			return 1;
+	}
+	return 0;
+}
+
+/*
+ * Return 1 if the refname matches one of the patterns, otherwise 0.
  * A pattern can be path prefix (e.g. a refname "refs/heads/master"
- * matches a pattern "refs/heads/") or a wildcard (e.g. the same ref
- * matches "refs/heads/m*",too).
+ * matches a pattern "refs/heads/" but not "refs/heads/m") or a
+ * wildcard (e.g. the same ref matches "refs/heads/m*", too).
  */
 static int match_name_as_path(const char **pattern, const char *refname)
 {
@@ -1078,6 +1102,16 @@ static int match_name_as_path(const char **pattern, const char *refname)
 	return 0;
 }
 
+/* Return 1 if the refname matches one of the patterns, otherwise 0. */
+static int filter_pattern_match(struct ref_filter *filter, const char *refname)
+{
+	if (!*filter->name_patterns)
+		return 1; /* No pattern always matches */
+	if (filter->match_as_path)
+		return match_name_as_path(filter->name_patterns, refname);
+	return match_pattern(filter->name_patterns, refname);
+}
+
 /*
  * Given a ref (sha1, refname), check if the ref belongs to the array
  * of sha1s. If the given ref is a tag, check if the given tag points
@@ -1181,7 +1215,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 	if (!(kind & filter->kind))
 		return 0;
 
-	if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
+	if (!filter_pattern_match(filter, refname))
 		return 0;
 
 	if (filter->points_at.nr && !match_points_at(&filter->points_at, oid->hash, refname))
diff --git a/ref-filter.h b/ref-filter.h
index 5aa2f40..8241066 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -58,7 +58,8 @@ struct ref_filter {
 	} merge;
 	struct commit *merge_commit;
 
-	unsigned int with_commit_tag_algo : 1;
+	unsigned int with_commit_tag_algo : 1,
+		match_as_path : 1;
 	unsigned int kind,
 		lines;
 };
-- 
2.5.0

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

* [PATCH v13 09/12] tag.c: use 'ref-filter' data structures
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (7 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 08/12] ref-filter: add option to match literal pattern Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 10/12] tag.c: use 'ref-filter' APIs Karthik Nayak
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

Make 'tag.c' use 'ref-filter' data structures and make changes to
support the new data structures. This is a part of the process
of porting 'tag.c' to use 'ref-filter' APIs.

This is a temporary step before porting 'tag.c' to use 'ref-filter'
completely. As this is a temporary step, most of the code
introduced here will be removed when 'tag.c' is ported over to use
'ref-filter' APIs.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 builtin/tag.c | 106 +++++++++++++++++++++++++++++++---------------------------
 1 file changed, 57 insertions(+), 49 deletions(-)

diff --git a/builtin/tag.c b/builtin/tag.c
index 0fc7557..e96bae2 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -17,6 +17,7 @@
 #include "gpg-interface.h"
 #include "sha1-array.h"
 #include "column.h"
+#include "ref-filter.h"
 
 static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
@@ -34,15 +35,6 @@ static const char * const git_tag_usage[] = {
 
 static int tag_sort;
 
-struct tag_filter {
-	const char **patterns;
-	int lines;
-	int sort;
-	struct string_list tags;
-	struct commit_list *with_commit;
-};
-
-static struct sha1_array points_at;
 static unsigned int colopts;
 
 static int match_pattern(const char **patterns, const char *ref)
@@ -61,19 +53,20 @@ static int match_pattern(const char **patterns, const char *ref)
  * removed as we port tag.c to use the ref-filter APIs.
  */
 static const unsigned char *match_points_at(const char *refname,
-					    const unsigned char *sha1)
+					    const unsigned char *sha1,
+					    struct sha1_array *points_at)
 {
 	const unsigned char *tagged_sha1 = NULL;
 	struct object *obj;
 
-	if (sha1_array_lookup(&points_at, sha1) >= 0)
+	if (sha1_array_lookup(points_at, sha1) >= 0)
 		return sha1;
 	obj = parse_object(sha1);
 	if (!obj)
 		die(_("malformed object at '%s'"), refname);
 	if (obj->type == OBJ_TAG)
 		tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
-	if (tagged_sha1 && sha1_array_lookup(&points_at, tagged_sha1) >= 0)
+	if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
 		return tagged_sha1;
 	return NULL;
 }
@@ -228,12 +221,24 @@ free_return:
 	free(buf);
 }
 
+static void ref_array_append(struct ref_array *array, const char *refname)
+{
+	size_t len = strlen(refname);
+	struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
+	memcpy(ref->refname, refname, len);
+	ref->refname[len] = '\0';
+	REALLOC_ARRAY(array->items, array->nr + 1);
+	array->items[array->nr++] = ref;
+}
+
 static int show_reference(const char *refname, const struct object_id *oid,
 			  int flag, void *cb_data)
 {
-	struct tag_filter *filter = cb_data;
+	struct ref_filter_cbdata *data = cb_data;
+	struct ref_array *array = data->array;
+	struct ref_filter *filter = data->filter;
 
-	if (match_pattern(filter->patterns, refname)) {
+	if (match_pattern(filter->name_patterns, refname)) {
 		if (filter->with_commit) {
 			struct commit *commit;
 
@@ -244,12 +249,12 @@ static int show_reference(const char *refname, const struct object_id *oid,
 				return 0;
 		}
 
-		if (points_at.nr && !match_points_at(refname, oid->hash))
+		if (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))
 			return 0;
 
 		if (!filter->lines) {
-			if (filter->sort)
-				string_list_append(&filter->tags, refname);
+			if (tag_sort)
+				ref_array_append(array, refname);
 			else
 				printf("%s\n", refname);
 			return 0;
@@ -264,36 +269,36 @@ static int show_reference(const char *refname, const struct object_id *oid,
 
 static int sort_by_version(const void *a_, const void *b_)
 {
-	const struct string_list_item *a = a_;
-	const struct string_list_item *b = b_;
-	return versioncmp(a->string, b->string);
+	const struct ref_array_item *a = *((struct ref_array_item **)a_);
+	const struct ref_array_item *b = *((struct ref_array_item **)b_);
+	return versioncmp(a->refname, b->refname);
 }
 
-static int list_tags(const char **patterns, int lines,
-		     struct commit_list *with_commit, int sort)
+static int list_tags(struct ref_filter *filter, int sort)
 {
-	struct tag_filter filter;
+	struct ref_array array;
+	struct ref_filter_cbdata data;
+
+	memset(&array, 0, sizeof(array));
+	data.array = &array;
+	data.filter = filter;
 
-	filter.patterns = patterns;
-	filter.lines = lines;
-	filter.sort = sort;
-	filter.with_commit = with_commit;
-	memset(&filter.tags, 0, sizeof(filter.tags));
-	filter.tags.strdup_strings = 1;
+	if (filter->lines == -1)
+		filter->lines = 0;
 
-	for_each_tag_ref(show_reference, (void *)&filter);
+	for_each_tag_ref(show_reference, &data);
 	if (sort) {
 		int i;
 		if ((sort & SORT_MASK) == VERCMP_SORT)
-			qsort(filter.tags.items, filter.tags.nr,
-			      sizeof(struct string_list_item), sort_by_version);
+			qsort(array.items, array.nr,
+			      sizeof(struct ref_array_item *), sort_by_version);
 		if (sort & REVERSE_SORT)
-			for (i = filter.tags.nr - 1; i >= 0; i--)
-				printf("%s\n", filter.tags.items[i].string);
+			for (i = array.nr - 1; i >= 0; i--)
+				printf("%s\n", array.items[i]->refname);
 		else
-			for (i = 0; i < filter.tags.nr; i++)
-				printf("%s\n", filter.tags.items[i].string);
-		string_list_clear(&filter.tags, 0);
+			for (i = 0; i < array.nr; i++)
+				printf("%s\n", array.items[i]->refname);
+		ref_array_clear(&array);
 	}
 	return 0;
 }
@@ -574,17 +579,17 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	const char *object_ref, *tag;
 	struct create_tag_options opt;
 	char *cleanup_arg = NULL;
-	int annotate = 0, force = 0, lines = -1;
 	int create_reflog = 0;
+	int annotate = 0, force = 0;
 	int cmdmode = 0;
 	const char *msgfile = NULL, *keyid = NULL;
 	struct msg_arg msg = { 0, STRBUF_INIT };
-	struct commit_list *with_commit = NULL;
 	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
+	struct ref_filter filter;
 	struct option options[] = {
 		OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
-		{ OPTION_INTEGER, 'n', NULL, &lines, N_("n"),
+		{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
 				N_("print <n> lines of each tag message"),
 				PARSE_OPT_OPTARG, NULL, 1 },
 		OPT_CMDMODE('d', "delete", &cmdmode, N_("delete tags"), 'd'),
@@ -606,14 +611,14 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 
 		OPT_GROUP(N_("Tag listing options")),
 		OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
-		OPT_CONTAINS(&with_commit, N_("print only tags that contain the commit")),
-		OPT_WITH(&with_commit, N_("print only tags that contain the commit")),
+		OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
+		OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
 		{
 			OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
 			PARSE_OPT_NONEG, parse_opt_sort
 		},
 		{
-			OPTION_CALLBACK, 0, "points-at", &points_at, N_("object"),
+			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 			N_("print only tags of the object"), 0, parse_opt_object_name
 		},
 		OPT_END()
@@ -622,6 +627,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	git_config(git_tag_config, NULL);
 
 	memset(&opt, 0, sizeof(opt));
+	memset(&filter, 0, sizeof(filter));
+	filter.lines = -1;
 
 	argc = parse_options(argc, argv, prefix, options, git_tag_usage, 0);
 
@@ -638,7 +645,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		usage_with_options(git_tag_usage, options);
 
 	finalize_colopts(&colopts, -1);
-	if (cmdmode == 'l' && lines != -1) {
+	if (cmdmode == 'l' && filter.lines != -1) {
 		if (explicitly_enable_column(colopts))
 			die(_("--column and -n are incompatible"));
 		colopts = 0;
@@ -651,18 +658,19 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			copts.padding = 2;
 			run_column_filter(colopts, &copts);
 		}
-		if (lines != -1 && tag_sort)
+		if (filter.lines != -1 && tag_sort)
 			die(_("--sort and -n are incompatible"));
-		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, tag_sort);
+		filter.name_patterns = argv;
+		ret = list_tags(&filter, tag_sort);
 		if (column_active(colopts))
 			stop_column_filter();
 		return ret;
 	}
-	if (lines != -1)
+	if (filter.lines != -1)
 		die(_("-n option is only allowed with -l."));
-	if (with_commit)
+	if (filter.with_commit)
 		die(_("--contains option is only allowed with -l."));
-	if (points_at.nr)
+	if (filter.points_at.nr)
 		die(_("--points-at option is only allowed with -l."));
 	if (cmdmode == 'd')
 		return for_each_tag_name(argv, delete_tag);
-- 
2.5.0

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

* [PATCH v13 10/12] tag.c: use 'ref-filter' APIs
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (8 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 09/12] tag.c: use 'ref-filter' data structures Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-22  3:39 ` [PATCH v13 11/12] tag.c: implement '--format' option Karthik Nayak
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

From: Karthik Nayak <karthik.188@gmail.com>

Make 'tag.c' use 'ref-filter' APIs for iterating through refs, sorting
and printing of refs. This removes most of the code used in 'tag.c'
replacing it with calls to the 'ref-filter' library.

Make 'tag.c' use the 'filter_refs()' function provided by 'ref-filter'
to filter out tags based on the options set.

For printing tags we use 'show_ref_array_item()' function provided by
'ref-filter'.

We improve the sorting option provided by 'tag.c' by using the sorting
options provided by 'ref-filter'. This causes the test 'invalid sort
parameter on command line' in t7004 to fail, as 'ref-filter' throws an
error for all sorting fields which are incorrect. The test is changed
to reflect the same.

Modify documentation for the same.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-tag.txt |  16 ++-
 builtin/tag.c             | 342 ++++++----------------------------------------
 t/t7004-tag.sh            |   8 +-
 3 files changed, 50 insertions(+), 316 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 84f6496..3ac4a96 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -13,7 +13,7 @@ SYNOPSIS
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
-	[--column[=<options>] | --no-column] [--create-reflog] [<pattern>...]
+	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -94,14 +94,16 @@ OPTIONS
 	using fnmatch(3)).  Multiple patterns may be given; if any of
 	them matches, the tag is shown.
 
---sort=<type>::
-	Sort in a specific order. Supported type is "refname"
-	(lexicographic order), "version:refname" or "v:refname" (tag
+--sort=<key>::
+	Sort based on the key given.  Prefix `-` to sort in
+	descending order of the value. You may use the --sort=<key> option
+	multiple times, in which case the last key becomes the primary
+	key. Also supports "version:refname" or "v:refname" (tag
 	names are treated as versions). The "version:refname" sort
 	order can also be affected by the
-	"versionsort.prereleaseSuffix" configuration variable. Prepend
-	"-" to reverse sort order. When this option is not given, the
-	sort order defaults to the value configured for the 'tag.sort'
+	"versionsort.prereleaseSuffix" configuration variable.
+	The keys supported are the same as those in `git for-each-ref`.
+	Sort order defaults to the value configured for the 'tag.sort'
 	variable if it exists, or lexicographic order otherwise. See
 	linkgit:git-config[1].
 
diff --git a/builtin/tag.c b/builtin/tag.c
index e96bae2..501fc52 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -28,278 +28,32 @@ static const char * const git_tag_usage[] = {
 	NULL
 };
 
-#define STRCMP_SORT     0	/* must be zero */
-#define VERCMP_SORT     1
-#define SORT_MASK       0x7fff
-#define REVERSE_SORT    0x8000
-
-static int tag_sort;
-
 static unsigned int colopts;
 
-static int match_pattern(const char **patterns, const char *ref)
-{
-	/* no pattern means match everything */
-	if (!*patterns)
-		return 1;
-	for (; *patterns; patterns++)
-		if (!wildmatch(*patterns, ref, 0, NULL))
-			return 1;
-	return 0;
-}
-
-/*
- * This is currently duplicated in ref-filter.c, and will eventually be
- * removed as we port tag.c to use the ref-filter APIs.
- */
-static const unsigned char *match_points_at(const char *refname,
-					    const unsigned char *sha1,
-					    struct sha1_array *points_at)
-{
-	const unsigned char *tagged_sha1 = NULL;
-	struct object *obj;
-
-	if (sha1_array_lookup(points_at, sha1) >= 0)
-		return sha1;
-	obj = parse_object(sha1);
-	if (!obj)
-		die(_("malformed object at '%s'"), refname);
-	if (obj->type == OBJ_TAG)
-		tagged_sha1 = ((struct tag *)obj)->tagged->sha1;
-	if (tagged_sha1 && sha1_array_lookup(points_at, tagged_sha1) >= 0)
-		return tagged_sha1;
-	return NULL;
-}
-
-static int in_commit_list(const struct commit_list *want, struct commit *c)
-{
-	for (; want; want = want->next)
-		if (!hashcmp(want->item->object.sha1, c->object.sha1))
-			return 1;
-	return 0;
-}
-
-/*
- * The entire code segment for supporting the --contains option has been
- * copied over to ref-filter.{c,h}. This will be deleted evetually when
- * we port tag.c to use ref-filter APIs.
- */
-enum contains_result {
-	CONTAINS_UNKNOWN = -1,
-	CONTAINS_NO = 0,
-	CONTAINS_YES = 1
-};
-
-/*
- * Test whether the candidate or one of its parents is contained in the list.
- * Do not recurse to find out, though, but return -1 if inconclusive.
- */
-static enum contains_result contains_test(struct commit *candidate,
-			    const struct commit_list *want)
-{
-	/* was it previously marked as containing a want commit? */
-	if (candidate->object.flags & TMP_MARK)
-		return 1;
-	/* or marked as not possibly containing a want commit? */
-	if (candidate->object.flags & UNINTERESTING)
-		return 0;
-	/* or are we it? */
-	if (in_commit_list(want, candidate)) {
-		candidate->object.flags |= TMP_MARK;
-		return 1;
-	}
-
-	if (parse_commit(candidate) < 0)
-		return 0;
-
-	return -1;
-}
-
-/*
- * Mimicking the real stack, this stack lives on the heap, avoiding stack
- * overflows.
- *
- * At each recursion step, the stack items points to the commits whose
- * ancestors are to be inspected.
- */
-struct stack {
-	int nr, alloc;
-	struct stack_entry {
-		struct commit *commit;
-		struct commit_list *parents;
-	} *stack;
-};
-
-static void push_to_stack(struct commit *candidate, struct stack *stack)
-{
-	int index = stack->nr++;
-	ALLOC_GROW(stack->stack, stack->nr, stack->alloc);
-	stack->stack[index].commit = candidate;
-	stack->stack[index].parents = candidate->parents;
-}
-
-static enum contains_result contains(struct commit *candidate,
-		const struct commit_list *want)
-{
-	struct stack stack = { 0, 0, NULL };
-	int result = contains_test(candidate, want);
-
-	if (result != CONTAINS_UNKNOWN)
-		return result;
-
-	push_to_stack(candidate, &stack);
-	while (stack.nr) {
-		struct stack_entry *entry = &stack.stack[stack.nr - 1];
-		struct commit *commit = entry->commit;
-		struct commit_list *parents = entry->parents;
-
-		if (!parents) {
-			commit->object.flags |= UNINTERESTING;
-			stack.nr--;
-		}
-		/*
-		 * If we just popped the stack, parents->item has been marked,
-		 * therefore contains_test will return a meaningful 0 or 1.
-		 */
-		else switch (contains_test(parents->item, want)) {
-		case CONTAINS_YES:
-			commit->object.flags |= TMP_MARK;
-			stack.nr--;
-			break;
-		case CONTAINS_NO:
-			entry->parents = parents->next;
-			break;
-		case CONTAINS_UNKNOWN:
-			push_to_stack(parents->item, &stack);
-			break;
-		}
-	}
-	free(stack.stack);
-	return contains_test(candidate, want);
-}
-
-/*
- * Currently duplicated in ref-filter, will eventually be removed as
- * we port tag.c to use ref-filter APIs.
- */
-static void show_tag_lines(const struct object_id *oid, int lines)
-{
-	int i;
-	unsigned long size;
-	enum object_type type;
-	char *buf, *sp, *eol;
-	size_t len;
-
-	buf = read_sha1_file(oid->hash, &type, &size);
-	if (!buf)
-		die_errno("unable to read object %s", oid_to_hex(oid));
-	if (type != OBJ_COMMIT && type != OBJ_TAG)
-		goto free_return;
-	if (!size)
-		die("an empty %s object %s?",
-		    typename(type), oid_to_hex(oid));
-
-	/* skip header */
-	sp = strstr(buf, "\n\n");
-	if (!sp)
-		goto free_return;
-
-	/* only take up to "lines" lines, and strip the signature from a tag */
-	if (type == OBJ_TAG)
-		size = parse_signature(buf, size);
-	for (i = 0, sp += 2; i < lines && sp < buf + size; i++) {
-		if (i)
-			printf("\n    ");
-		eol = memchr(sp, '\n', size - (sp - buf));
-		len = eol ? eol - sp : size - (sp - buf);
-		fwrite(sp, len, 1, stdout);
-		if (!eol)
-			break;
-		sp = eol + 1;
-	}
-free_return:
-	free(buf);
-}
-
-static void ref_array_append(struct ref_array *array, const char *refname)
-{
-	size_t len = strlen(refname);
-	struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1);
-	memcpy(ref->refname, refname, len);
-	ref->refname[len] = '\0';
-	REALLOC_ARRAY(array->items, array->nr + 1);
-	array->items[array->nr++] = ref;
-}
-
-static int show_reference(const char *refname, const struct object_id *oid,
-			  int flag, void *cb_data)
-{
-	struct ref_filter_cbdata *data = cb_data;
-	struct ref_array *array = data->array;
-	struct ref_filter *filter = data->filter;
-
-	if (match_pattern(filter->name_patterns, refname)) {
-		if (filter->with_commit) {
-			struct commit *commit;
-
-			commit = lookup_commit_reference_gently(oid->hash, 1);
-			if (!commit)
-				return 0;
-			if (!contains(commit, filter->with_commit))
-				return 0;
-		}
-
-		if (filter->points_at.nr && !match_points_at(refname, oid->hash, &filter->points_at))
-			return 0;
-
-		if (!filter->lines) {
-			if (tag_sort)
-				ref_array_append(array, refname);
-			else
-				printf("%s\n", refname);
-			return 0;
-		}
-		printf("%-15s ", refname);
-		show_tag_lines(oid, filter->lines);
-		putchar('\n');
-	}
-
-	return 0;
-}
-
-static int sort_by_version(const void *a_, const void *b_)
-{
-	const struct ref_array_item *a = *((struct ref_array_item **)a_);
-	const struct ref_array_item *b = *((struct ref_array_item **)b_);
-	return versioncmp(a->refname, b->refname);
-}
-
-static int list_tags(struct ref_filter *filter, int sort)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)
 {
 	struct ref_array array;
-	struct ref_filter_cbdata data;
+	char *format;
+	int i;
 
 	memset(&array, 0, sizeof(array));
-	data.array = &array;
-	data.filter = filter;
 
 	if (filter->lines == -1)
 		filter->lines = 0;
 
-	for_each_tag_ref(show_reference, &data);
-	if (sort) {
-		int i;
-		if ((sort & SORT_MASK) == VERCMP_SORT)
-			qsort(array.items, array.nr,
-			      sizeof(struct ref_array_item *), sort_by_version);
-		if (sort & REVERSE_SORT)
-			for (i = array.nr - 1; i >= 0; i--)
-				printf("%s\n", array.items[i]->refname);
-		else
-			for (i = 0; i < array.nr; i++)
-				printf("%s\n", array.items[i]->refname);
-		ref_array_clear(&array);
-	}
+	if (filter->lines)
+		format = "%(align:16,left)%(refname:short)%(end)";
+	else
+		format = "%(refname:short)";
+
+	verify_ref_format(format);
+	filter_refs(&array, filter, FILTER_REFS_TAGS);
+	ref_array_sort(sorting, &array);
+
+	for (i = 0; i < array.nr; i++)
+		show_ref_array_item(array.items[i], format, QUOTE_NONE, filter->lines);
+	ref_array_clear(&array);
+
 	return 0;
 }
 
@@ -366,35 +120,26 @@ static const char tag_template_nocleanup[] =
 	"Lines starting with '%c' will be kept; you may remove them"
 	" yourself if you want to.\n");
 
-/*
- * Parse a sort string, and return 0 if parsed successfully. Will return
- * non-zero when the sort string does not parse into a known type. If var is
- * given, the error message becomes a warning and includes information about
- * the configuration value.
- */
-static int parse_sort_string(const char *var, const char *arg, int *sort)
+/* Parse arg given and add it the ref_sorting array */
+static int parse_sorting_string(const char *arg, struct ref_sorting **sorting_tail)
 {
-	int type = 0, flags = 0;
-
-	if (skip_prefix(arg, "-", &arg))
-		flags |= REVERSE_SORT;
+	struct ref_sorting *s;
+	int len;
 
-	if (skip_prefix(arg, "version:", &arg) || skip_prefix(arg, "v:", &arg))
-		type = VERCMP_SORT;
-	else
-		type = STRCMP_SORT;
+	s = xcalloc(1, sizeof(*s));
+	s->next = *sorting_tail;
+	*sorting_tail = s;
 
-	if (strcmp(arg, "refname")) {
-		if (!var)
-			return error(_("unsupported sort specification '%s'"), arg);
-		else {
-			warning(_("unsupported sort specification '%s' in variable '%s'"),
-				var, arg);
-			return -1;
-		}
+	if (*arg == '-') {
+		s->reverse = 1;
+		arg++;
 	}
+	if (skip_prefix(arg, "version:", &arg) ||
+	    skip_prefix(arg, "v:", &arg))
+		s->version = 1;
 
-	*sort = (type | flags);
+	len = strlen(arg);
+	s->atom = parse_ref_filter_atom(arg, arg+len);
 
 	return 0;
 }
@@ -402,11 +147,12 @@ static int parse_sort_string(const char *var, const char *arg, int *sort)
 static int git_tag_config(const char *var, const char *value, void *cb)
 {
 	int status;
+	struct ref_sorting **sorting_tail = (struct ref_sorting **)cb;
 
 	if (!strcmp(var, "tag.sort")) {
 		if (!value)
 			return config_error_nonbool(var);
-		parse_sort_string(var, value, &tag_sort);
+		parse_sorting_string(value, sorting_tail);
 		return 0;
 	}
 
@@ -564,13 +310,6 @@ static int strbuf_check_tag_ref(struct strbuf *sb, const char *name)
 	return check_refname_format(sb->buf, 0);
 }
 
-static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
-{
-	int *sort = opt->value;
-
-	return parse_sort_string(NULL, arg, sort);
-}
-
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -587,6 +326,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct ref_transaction *transaction;
 	struct strbuf err = STRBUF_INIT;
 	struct ref_filter filter;
+	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
 	struct option options[] = {
 		OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
 		{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -613,10 +353,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
 		OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
 		OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
-		{
-			OPTION_CALLBACK, 0, "sort", &tag_sort, N_("type"), N_("sort tags"),
-			PARSE_OPT_NONEG, parse_opt_sort
-		},
+		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
+			     N_("field name to sort on"), &parse_opt_ref_sorting),
 		{
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 			N_("print only tags of the object"), 0, parse_opt_object_name
@@ -624,7 +362,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_END()
 	};
 
-	git_config(git_tag_config, NULL);
+	git_config(git_tag_config, sorting_tail);
 
 	memset(&opt, 0, sizeof(opt));
 	memset(&filter, 0, sizeof(filter));
@@ -650,6 +388,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			die(_("--column and -n are incompatible"));
 		colopts = 0;
 	}
+	if (!sorting)
+		sorting = ref_default_sorting();
 	if (cmdmode == 'l') {
 		int ret;
 		if (column_active(colopts)) {
@@ -658,10 +398,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			copts.padding = 2;
 			run_column_filter(colopts, &copts);
 		}
-		if (filter.lines != -1 && tag_sort)
-			die(_("--sort and -n are incompatible"));
 		filter.name_patterns = argv;
-		ret = list_tags(&filter, tag_sort);
+		ret = list_tags(&filter, sorting);
 		if (column_active(colopts))
 			stop_column_filter();
 		return ret;
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index d31788c..84153ef 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1462,13 +1462,7 @@ test_expect_success 'invalid sort parameter on command line' '
 
 test_expect_success 'invalid sort parameter in configuratoin' '
 	git config tag.sort "v:notvalid" &&
-	git tag -l "foo*" >actual &&
-	cat >expect <<-\EOF &&
-	foo1.10
-	foo1.3
-	foo1.6
-	EOF
-	test_cmp expect actual
+	test_must_fail git tag -l "foo*"
 '
 
 test_expect_success 'version sort with prerelease reordering' '
-- 
2.5.0

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

* [PATCH v13 11/12] tag.c: implement '--format' option
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (9 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 10/12] tag.c: use 'ref-filter' APIs Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-23 19:56   ` Matthieu Moy
  2015-08-22  3:39 ` [PATCH v13 12/12] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak
                   ` (2 subsequent siblings)
  13 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Implement the '--format' option provided by 'ref-filter'.
This lets the user list tags as per desired format similar
to the implementation in 'git for-each-ref'.

Add tests and documentation for the same.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-tag.txt |  8 +++++++-
 builtin/tag.c             | 19 +++++++++++--------
 t/t7004-tag.sh            | 12 ++++++++++++
 3 files changed, 30 insertions(+), 9 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 3ac4a96..0c7f4e6 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -13,7 +13,8 @@ SYNOPSIS
 	<tagname> [<commit> | <object>]
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
-	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
+	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
+	[--format=<format>] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -158,6 +159,11 @@ This option is only applicable when listing tags without annotation lines.
 	The object that the new tag will refer to, usually a commit.
 	Defaults to HEAD.
 
+<format>::
+	A string that interpolates `%(fieldname)` from the object
+	pointed at by a ref being shown.  The format is the same as
+	that of linkgit:git-for-each-ref[1].  When unspecified,
+	defaults to `%(refname:short)`.
 
 CONFIGURATION
 -------------
diff --git a/builtin/tag.c b/builtin/tag.c
index 501fc52..4b8d6df 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,17 +23,16 @@ static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
 	N_("git tag -d <tagname>..."),
 	N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
-		"\n\t\t[<pattern>...]"),
+		"\n\t\t[--format=<format>] [<pattern>...]"),
 	N_("git tag -v <tagname>..."),
 	NULL
 };
 
 static unsigned int colopts;
 
-static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)
+static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, const char *format)
 {
 	struct ref_array array;
-	char *format;
 	int i;
 
 	memset(&array, 0, sizeof(array));
@@ -41,10 +40,12 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)
 	if (filter->lines == -1)
 		filter->lines = 0;
 
-	if (filter->lines)
-		format = "%(align:16,left)%(refname:short)%(end)";
-	else
-		format = "%(refname:short)";
+	if (!format) {
+		if (filter->lines)
+			format = "%(align:16,left)%(refname:short)%(end)";
+		else
+			format = "%(refname:short)";
+	}
 
 	verify_ref_format(format);
 	filter_refs(&array, filter, FILTER_REFS_TAGS);
@@ -327,6 +328,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct strbuf err = STRBUF_INIT;
 	struct ref_filter filter;
 	static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting;
+	const char *format = NULL;
 	struct option options[] = {
 		OPT_CMDMODE('l', "list", &cmdmode, N_("list tag names"), 'l'),
 		{ OPTION_INTEGER, 'n', NULL, &filter.lines, N_("n"),
@@ -359,6 +361,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"),
 			N_("print only tags of the object"), 0, parse_opt_object_name
 		},
+		OPT_STRING(  0 , "format", &format, N_("format"), N_("format to use for the output")),
 		OPT_END()
 	};
 
@@ -399,7 +402,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			run_column_filter(colopts, &copts);
 		}
 		filter.name_patterns = argv;
-		ret = list_tags(&filter, sorting);
+		ret = list_tags(&filter, sorting, format);
 		if (column_active(colopts))
 			stop_column_filter();
 		return ret;
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 84153ef..8987fb1 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1519,4 +1519,16 @@ EOF"
 	test_cmp expect actual
 '
 
+test_expect_success '--format should list tags as per format given' '
+	cat >expect <<-\EOF &&
+	refname : refs/tags/foo1.10
+	refname : refs/tags/foo1.3
+	refname : refs/tags/foo1.6
+	refname : refs/tags/foo1.6-rc1
+	refname : refs/tags/foo1.6-rc2
+	EOF
+	git tag -l --format="refname : %(refname)" "foo*" >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.5.0

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

* [PATCH v13 12/12] tag.c: implement '--merged' and '--no-merged' options
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (10 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 11/12] tag.c: implement '--format' option Karthik Nayak
@ 2015-08-22  3:39 ` Karthik Nayak
  2015-08-23 20:00 ` [PATCH v13 00/12] port tag.c to use ref-filter APIs Matthieu Moy
  2015-08-24 17:27 ` Junio C Hamano
  13 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-22  3:39 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak, Karthik Nayak

Use 'ref-filter' APIs to implement the '--merged' and '--no-merged'
options into 'tag.c'. The '--merged' option lets the user to only list
tags merged into the named commit. The '--no-merged' option lets the
user to only list tags not merged into the named commit.  If no object
is provided it assumes HEAD as the object.

Add documentation and tests for the same.

Mentored-by: Christian Couder <christian.couder@gmail.com>
Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
---
 Documentation/git-tag.txt |  7 ++++++-
 builtin/tag.c             |  6 +++++-
 t/t7004-tag.sh            | 27 +++++++++++++++++++++++++++
 3 files changed, 38 insertions(+), 2 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 0c7f4e6..3803bf7 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -14,7 +14,7 @@ SYNOPSIS
 'git tag' -d <tagname>...
 'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
 	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
-	[--format=<format>] [<pattern>...]
+	[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]
 'git tag' -v <tagname>...
 
 DESCRIPTION
@@ -165,6 +165,11 @@ This option is only applicable when listing tags without annotation lines.
 	that of linkgit:git-for-each-ref[1].  When unspecified,
 	defaults to `%(refname:short)`.
 
+--[no-]merged [<commit>]::
+	Only list tags whose tips are reachable, or not reachable
+	if '--no-merged' is used, from the specified commit ('HEAD'
+	if not specified).
+
 CONFIGURATION
 -------------
 By default, 'git tag' in sign-with-default mode (-s) will use your
diff --git a/builtin/tag.c b/builtin/tag.c
index 4b8d6df..bbbcaed 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -23,7 +23,7 @@ static const char * const git_tag_usage[] = {
 	N_("git tag [-a | -s | -u <key-id>] [-f] [-m <msg> | -F <file>] <tagname> [<head>]"),
 	N_("git tag -d <tagname>..."),
 	N_("git tag -l [-n[<num>]] [--contains <commit>] [--points-at <object>]"
-		"\n\t\t[--format=<format>] [<pattern>...]"),
+		"\n\t\t[--format=<format>] [--[no-]merged [<commit>]] [<pattern>...]"),
 	N_("git tag -v <tagname>..."),
 	NULL
 };
@@ -355,6 +355,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
 		OPT_CONTAINS(&filter.with_commit, N_("print only tags that contain the commit")),
 		OPT_WITH(&filter.with_commit, N_("print only tags that contain the commit")),
+		OPT_MERGED(&filter, N_("print only tags that are merged")),
+		OPT_NO_MERGED(&filter, N_("print only tags that are not merged")),
 		OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"),
 			     N_("field name to sort on"), &parse_opt_ref_sorting),
 		{
@@ -413,6 +415,8 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 		die(_("--contains option is only allowed with -l."));
 	if (filter.points_at.nr)
 		die(_("--points-at option is only allowed with -l."));
+	if (filter.merge_commit)
+		die(_("--merged and --no-merged option are only allowed with -l"));
 	if (cmdmode == 'd')
 		return for_each_tag_name(argv, delete_tag);
 	if (cmdmode == 'v')
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index 8987fb1..3dd2f51 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1531,4 +1531,31 @@ test_expect_success '--format should list tags as per format given' '
 	test_cmp expect actual
 '
 
+test_expect_success 'setup --merged test tags' '
+	git tag mergetest-1 HEAD~2 &&
+	git tag mergetest-2 HEAD~1 &&
+	git tag mergetest-3 HEAD
+'
+
+test_expect_success '--merged cannot be used in non-list mode' '
+	test_must_fail git tag --merged=mergetest-2 foo
+'
+
+test_expect_success '--merged shows merged tags' '
+	cat >expect <<-\EOF &&
+	mergetest-1
+	mergetest-2
+	EOF
+	git tag -l --merged=mergetest-2 mergetest-* >actual &&
+	test_cmp expect actual
+'
+
+test_expect_success '--no-merged show unmerged tags' '
+	cat >expect <<-\EOF &&
+	mergetest-3
+	EOF
+	git tag -l --no-merged=mergetest-2 mergetest-* >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.5.0

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

* Re: [PATCH v13 11/12] tag.c: implement '--format' option
  2015-08-22  3:39 ` [PATCH v13 11/12] tag.c: implement '--format' option Karthik Nayak
@ 2015-08-23 19:56   ` Matthieu Moy
  2015-08-24 15:07     ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Matthieu Moy @ 2015-08-23 19:56 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, gitster

Karthik Nayak <karthik.188@gmail.com> writes:

> --- a/Documentation/git-tag.txt
> +++ b/Documentation/git-tag.txt
> @@ -13,7 +13,8 @@ SYNOPSIS
>  	<tagname> [<commit> | <object>]
>  'git tag' -d <tagname>...
>  'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
> -	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
> +	[--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
> +	[--format=<format>] [<pattern>...]
>  'git tag' -v <tagname>...
>  
>  DESCRIPTION
> @@ -158,6 +159,11 @@ This option is only applicable when listing tags without annotation lines.
>  	The object that the new tag will refer to, usually a commit.
>  	Defaults to HEAD.
>  
> +<format>::

Shouldn't this be --format <format>, not just <format>? We usually use
the named argument in the SYNOPSIS for positional arguments, but not for
arguments following an option.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (11 preceding siblings ...)
  2015-08-22  3:39 ` [PATCH v13 12/12] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak
@ 2015-08-23 20:00 ` Matthieu Moy
  2015-08-24 15:09   ` Karthik Nayak
  2015-08-24 22:34   ` Junio C Hamano
  2015-08-24 17:27 ` Junio C Hamano
  13 siblings, 2 replies; 60+ messages in thread
From: Matthieu Moy @ 2015-08-23 20:00 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, gitster

Karthik Nayak <karthik.188@gmail.com> writes:

> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
> index 1997657..06d468e 100644
> --- a/Documentation/git-for-each-ref.txt
> +++ b/Documentation/git-for-each-ref.txt
> @@ -133,7 +133,8 @@ align::
>  	`<position>` is either left, right or middle and `<width>` is
>  	the total length of the content with alignment. If the
>  	contents length is more than the width then no alignment is
> -	performed.
> +	performed. If used with '--quote' everything in between %(align:..)
> +	and %(end) is quoted.

There's no --quote, there are --shell, --python, ... (well, actually, I
would have prefered to have a single --quote=language option, but that's
not how it is now).

I had already commented on a preliminary version of this series
off-list. I think all my previous comments have been taken into account.

Thanks,

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 11/12] tag.c: implement '--format' option
  2015-08-23 19:56   ` Matthieu Moy
@ 2015-08-24 15:07     ` Karthik Nayak
  2015-08-24 15:14       ` Matthieu Moy
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-24 15:07 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Git, Christian Couder, Junio C Hamano

On Mon, Aug 24, 2015 at 1:26 AM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> --- a/Documentation/git-tag.txt
>> +++ b/Documentation/git-tag.txt
>> @@ -13,7 +13,8 @@ SYNOPSIS
>>       <tagname> [<commit> | <object>]
>>  'git tag' -d <tagname>...
>>  'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
>> -     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
>> +     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
>> +     [--format=<format>] [<pattern>...]
>>  'git tag' -v <tagname>...
>>
>>  DESCRIPTION
>> @@ -158,6 +159,11 @@ This option is only applicable when listing tags without annotation lines.
>>       The object that the new tag will refer to, usually a commit.
>>       Defaults to HEAD.
>>
>> +<format>::
>
> Shouldn't this be --format <format>, not just <format>? We usually use
> the named argument in the SYNOPSIS for positional arguments, but not for
> arguments following an option.
>

This is how it was in for-each-ref Documentation, hence to keep it similar I
just put <format>.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-23 20:00 ` [PATCH v13 00/12] port tag.c to use ref-filter APIs Matthieu Moy
@ 2015-08-24 15:09   ` Karthik Nayak
  2015-08-24 15:16     ` Matthieu Moy
  2015-08-24 22:34   ` Junio C Hamano
  1 sibling, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-24 15:09 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Git, Christian Couder, Junio C Hamano

On Mon, Aug 24, 2015 at 1:30 AM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index 1997657..06d468e 100644
>> --- a/Documentation/git-for-each-ref.txt
>> +++ b/Documentation/git-for-each-ref.txt
>> @@ -133,7 +133,8 @@ align::
>>       `<position>` is either left, right or middle and `<width>` is
>>       the total length of the content with alignment. If the
>>       contents length is more than the width then no alignment is
>> -     performed.
>> +     performed. If used with '--quote' everything in between %(align:..)
>> +     and %(end) is quoted.
>
> There's no --quote, there are --shell, --python, ... (well, actually, I
> would have prefered to have a single --quote=language option, but that's
> not how it is now).

That'd be easy to implement, but I didn't because `git tag -l` is
human readable and
I didn't see the necessity for having something like `--<quote_type>` here.

It'd be better to just use `git for-each-ref refs/tags/` for that.

>
> I had already commented on a preliminary version of this series
> off-list. I think all my previous comments have been taken into account.
>
> Thanks,
>

Thanks for the review :)


-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 11/12] tag.c: implement '--format' option
  2015-08-24 15:07     ` Karthik Nayak
@ 2015-08-24 15:14       ` Matthieu Moy
  2015-08-24 15:21         ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Matthieu Moy @ 2015-08-24 15:14 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Junio C Hamano

Karthik Nayak <karthik.188@gmail.com> writes:

> On Mon, Aug 24, 2015 at 1:26 AM, Matthieu Moy
> <Matthieu.Moy@grenoble-inp.fr> wrote:
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> --- a/Documentation/git-tag.txt
>>> +++ b/Documentation/git-tag.txt
>>> @@ -13,7 +13,8 @@ SYNOPSIS
>>>       <tagname> [<commit> | <object>]
>>>  'git tag' -d <tagname>...
>>>  'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
>>> -     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
>>> +     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
>>> +     [--format=<format>] [<pattern>...]
>>>  'git tag' -v <tagname>...
>>>
>>>  DESCRIPTION
>>> @@ -158,6 +159,11 @@ This option is only applicable when listing tags without annotation lines.
>>>       The object that the new tag will refer to, usually a commit.
>>>       Defaults to HEAD.
>>>
>>> +<format>::
>>
>> Shouldn't this be --format <format>, not just <format>? We usually use
>> the named argument in the SYNOPSIS for positional arguments, but not for
>> arguments following an option.
>
> This is how it was in for-each-ref Documentation, hence to keep it similar I
> just put <format>.

"It's wrong in another place" sounds like an argument to fix the other
place, not to get it wrong here too ;-).

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 15:09   ` Karthik Nayak
@ 2015-08-24 15:16     ` Matthieu Moy
  2015-08-24 15:22       ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Matthieu Moy @ 2015-08-24 15:16 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Junio C Hamano

Karthik Nayak <karthik.188@gmail.com> writes:

> On Mon, Aug 24, 2015 at 1:30 AM, Matthieu Moy
> <Matthieu.Moy@grenoble-inp.fr> wrote:
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>>> index 1997657..06d468e 100644
>>> --- a/Documentation/git-for-each-ref.txt
>>> +++ b/Documentation/git-for-each-ref.txt
>>> @@ -133,7 +133,8 @@ align::
>>>       `<position>` is either left, right or middle and `<width>` is
>>>       the total length of the content with alignment. If the
>>>       contents length is more than the width then no alignment is
>>> -     performed.
>>> +     performed. If used with '--quote' everything in between %(align:..)
>>> +     and %(end) is quoted.
>>
>> There's no --quote, there are --shell, --python, ... (well, actually, I
>> would have prefered to have a single --quote=language option, but that's
>> not how it is now).
>
> That'd be easy to implement, but I didn't because `git tag -l` is
> human readable and
> I didn't see the necessity for having something like `--<quote_type>` here.

Agreed: tag does not have --shell, --python & so and does not need it.

But that's not my point: you write "if used with '--quote'", and the
option name is not --quote. It should be "if used with `--shell`,
`--python`, ... then everything ...".

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 11/12] tag.c: implement '--format' option
  2015-08-24 15:14       ` Matthieu Moy
@ 2015-08-24 15:21         ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-24 15:21 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Git, Christian Couder, Junio C Hamano

On Mon, Aug 24, 2015 at 8:44 PM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Mon, Aug 24, 2015 at 1:26 AM, Matthieu Moy
>> <Matthieu.Moy@grenoble-inp.fr> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>>> --- a/Documentation/git-tag.txt
>>>> +++ b/Documentation/git-tag.txt
>>>> @@ -13,7 +13,8 @@ SYNOPSIS
>>>>       <tagname> [<commit> | <object>]
>>>>  'git tag' -d <tagname>...
>>>>  'git tag' [-n[<num>]] -l [--contains <commit>] [--points-at <object>]
>>>> -     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>] [<pattern>...]
>>>> +     [--column[=<options>] | --no-column] [--create-reflog] [--sort=<key>]
>>>> +     [--format=<format>] [<pattern>...]
>>>>  'git tag' -v <tagname>...
>>>>
>>>>  DESCRIPTION
>>>> @@ -158,6 +159,11 @@ This option is only applicable when listing tags without annotation lines.
>>>>       The object that the new tag will refer to, usually a commit.
>>>>       Defaults to HEAD.
>>>>
>>>> +<format>::
>>>
>>> Shouldn't this be --format <format>, not just <format>? We usually use
>>> the named argument in the SYNOPSIS for positional arguments, but not for
>>> arguments following an option.
>>
>> This is how it was in for-each-ref Documentation, hence to keep it similar I
>> just put <format>.
>
> "It's wrong in another place" sounds like an argument to fix the other
> place, not to get it wrong here too ;-).
>

Of course! That was just me justifying my action. I agree, it should
be corrected both places.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 15:16     ` Matthieu Moy
@ 2015-08-24 15:22       ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-24 15:22 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Git, Christian Couder, Junio C Hamano

On Mon, Aug 24, 2015 at 8:46 PM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Mon, Aug 24, 2015 at 1:30 AM, Matthieu Moy
>> <Matthieu.Moy@grenoble-inp.fr> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>>>> index 1997657..06d468e 100644
>>>> --- a/Documentation/git-for-each-ref.txt
>>>> +++ b/Documentation/git-for-each-ref.txt
>>>> @@ -133,7 +133,8 @@ align::
>>>>       `<position>` is either left, right or middle and `<width>` is
>>>>       the total length of the content with alignment. If the
>>>>       contents length is more than the width then no alignment is
>>>> -     performed.
>>>> +     performed. If used with '--quote' everything in between %(align:..)
>>>> +     and %(end) is quoted.
>>>
>>> There's no --quote, there are --shell, --python, ... (well, actually, I
>>> would have prefered to have a single --quote=language option, but that's
>>> not how it is now).
>>
>> That'd be easy to implement, but I didn't because `git tag -l` is
>> human readable and
>> I didn't see the necessity for having something like `--<quote_type>` here.
>
> Agreed: tag does not have --shell, --python & so and does not need it.
>
> But that's not my point: you write "if used with '--quote'", and the
> option name is not --quote. It should be "if used with `--shell`,
> `--python`, ... then everything ...".
>

Oops! misread what you sent, maybe' --<quote_type>' instead.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (12 preceding siblings ...)
  2015-08-23 20:00 ` [PATCH v13 00/12] port tag.c to use ref-filter APIs Matthieu Moy
@ 2015-08-24 17:27 ` Junio C Hamano
  2015-08-24 18:09   ` Karthik Nayak
  13 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 17:27 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

Karthik Nayak <karthik.188@gmail.com> writes:

> Interdiff:
>
> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
> index 1997657..06d468e 100644
> --- a/Documentation/git-for-each-ref.txt
> +++ b/Documentation/git-for-each-ref.txt
> @@ -133,7 +133,8 @@ align::
>  	`<position>` is either left, right or middle and `<width>` is
>  	the total length of the content with alignment. If the
>  	contents length is more than the width then no alignment is
> -	performed.
> +	performed. If used with '--quote' everything in between %(align:..)
> +	and %(end) is quoted.

That sounds like a buggy behaviour that we may want to fix later,
though.  Perhaps document it as a known bug, e.g. "Currently it does
not work well when used with language-specific quoting like --shell,
etc." (while punting on fixing the implementation for now)?

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 17:27 ` Junio C Hamano
@ 2015-08-24 18:09   ` Karthik Nayak
  2015-08-24 18:53     ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-24 18:09 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> Interdiff:
>>
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index 1997657..06d468e 100644
>> --- a/Documentation/git-for-each-ref.txt
>> +++ b/Documentation/git-for-each-ref.txt
>> @@ -133,7 +133,8 @@ align::
>>       `<position>` is either left, right or middle and `<width>` is
>>       the total length of the content with alignment. If the
>>       contents length is more than the width then no alignment is
>> -     performed.
>> +     performed. If used with '--quote' everything in between %(align:..)
>> +     and %(end) is quoted.
>
> That sounds like a buggy behaviour that we may want to fix later,
> though.  Perhaps document it as a known bug, e.g. "Currently it does
> not work well when used with language-specific quoting like --shell,
> etc." (while punting on fixing the implementation for now)?
>

I might have misunderstood you, But based on the discussion held here
(thread.gmane.org/gmane.comp.version-control.git/276140)
I thought we wanted everything inside the %(align) .... %(end) atoms
to be quoted.
So I'm a little confused by what you're trying to say.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 18:09   ` Karthik Nayak
@ 2015-08-24 18:53     ` Junio C Hamano
  2015-08-24 22:35       ` Junio C Hamano
  2015-08-25 13:25       ` Karthik Nayak
  0 siblings, 2 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 18:53 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

Karthik Nayak <karthik.188@gmail.com> writes:

> On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Karthik Nayak <karthik.188@gmail.com> writes:
>> ...
>>> +     performed. If used with '--quote' everything in between %(align:..)
>>> +     and %(end) is quoted.
> ...
> I might have misunderstood you, But based on the discussion held here
> (thread.gmane.org/gmane.comp.version-control.git/276140)
> I thought we wanted everything inside the %(align) .... %(end) atoms
> to be quoted.

Perhaps I misunderstood your intention in the doc.
	
I took "everything in between %(align:...) and %(end) is quoted" to
mean that

	%(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)

can never satisfy %(if:empty), because %(align)%(end) would expand
to a string that has two single-quotes, that is not an empty string.

If that is not what would happen in the "branch --list" enhancement,
then the proposed behaviour is good, but the above documentation would
need to be updated when it happens, I think.  It at least is misleading.

Thanks.

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

* Re: [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack
  2015-08-22  3:39 ` [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack Karthik Nayak
@ 2015-08-24 21:56   ` Junio C Hamano
  2015-08-25 10:26     ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 21:56 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

Karthik Nayak <karthik.188@gmail.com> writes:

> +static void push_new_stack_element(struct ref_formatting_stack **stack)
> +{

Micronit.  Perhaps s/_new//;, as you do not call the other function
pop_old_stack_element().

The remainder of this step looks pretty straight-forward and was a
pleasant read.

Thanks.

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-22  3:39 ` [PATCH v13 04/12] ref-filter: implement an `align` atom Karthik Nayak
@ 2015-08-24 22:13   ` Junio C Hamano
  2015-08-24 22:15     ` Junio C Hamano
                       ` (2 more replies)
  0 siblings, 3 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:13 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

Karthik Nayak <karthik.188@gmail.com> writes:

> +static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style);
> +
> +static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
> +{
> +	struct ref_formatting_stack *current = state->stack;
> +	struct strbuf s = STRBUF_INIT;
> +
> +	if (!current->at_end)
> +		die(_("format: `end` atom used without a supporting atom"));
> +	current->at_end(current);
> +	/*
> +	 * Whenever we have more than one stack element that means we
> +	 * are using a certain modifier atom. In that case we need to
> +	 * perform quote formatting.
> +	 */
> +	if (!state->stack->prev->prev) {

The comment and the condition seem to be saying opposite things.
The code says "If the stack only has one prev that is the very
initial one, then we do the quoting, i.e. the result of expanding
the enclosed string in %(start-something)...%(end) is quoted only
when that appears at the top level", which feels more correct than
the comment that says "if we are about to pop after seeing the
first %(end) in %(start)...%(another)...%(end)...%(end) sequence,
we quote what is between %(another)...%(end)".

> +		perform_quote_formatting(&s, current->output.buf, state->quote_style);
> +		strbuf_reset(&current->output);
> +		strbuf_addbuf(&current->output, &s);
> +	}
> +	strbuf_release(&s);
> +	pop_stack_element(&state->stack);
> +}
> +

> @@ -1228,29 +1315,33 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
>  	qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
>  }
>  
> -static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
> +static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style)
>  {
> -	struct strbuf *s = &state->stack->output;
> -
> -	switch (state->quote_style) {
> +	switch (quote_style) {
>  	case QUOTE_NONE:
> -		strbuf_addstr(s, v->s);
> +		strbuf_addstr(s, str);
>  		break;
>  	case QUOTE_SHELL:
> -		sq_quote_buf(s, v->s);
> +		sq_quote_buf(s, str);
>  		break;
>  	case QUOTE_PERL:
> -		perl_quote_buf(s, v->s);
> +		perl_quote_buf(s, str);
>  		break;
>  	case QUOTE_PYTHON:
> -		python_quote_buf(s, v->s);
> +		python_quote_buf(s, str);
>  		break;
>  	case QUOTE_TCL:
> -		tcl_quote_buf(s, v->s);
> +		tcl_quote_buf(s, str);
>  		break;
>  	}
>  }
>  
> +static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
> +{
> +	struct strbuf *s = &state->stack->output;
> +	perform_quote_formatting(s, v->s, state->quote_style);

Hmmm, do we want to unconditionally do the quote here, or only when
we are not being captured by upcoming %(end) to be consistent with
the behaviour of end_atom_handler() above?

> @@ -1307,7 +1398,18 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
>  		if (cp < sp)
>  			append_literal(cp, sp, &state);
>  		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
> -		append_atom(atomv, &state);
> +		/*
> +		 * If the atom is a modifier atom, then call the handler function.
> +		 * Else, if this is the first element on the stack, then we need to
> +		 * format the atom as per the given quote. Else we just add the atom value
> +		 * to the current stack element and handle quote formatting at the end.
> +		 */
> +		if (atomv->handler)
> +			atomv->handler(atomv, &state);
> +		else if (!state.stack->prev)
> +			append_atom(atomv, &state);
> +		else
> +			strbuf_addstr(&state.stack->output, atomv->s);

Ahh, this explains why you are not doing it above, but I do not
think if this is a good division of labor.

You can see that I expected that "if !state.stack->prev" check to be
inside append_atom(), and I would imagine future readers would have
the same expectation when reading this code.  I.e.

	append_atom(struct atom_value *v, struct ref_f_s *state)
        {
		if (state->stack->prev)
			strbuf_addstr(&state->stack->output, v->s);
		else
                	quote_format(&state->stack->output, v->s, state->quote_style);
	}

The end result may be the same, but I do think "append_atom is to
always quote, so we do an unquoted appending by hand" is a bad way
to do this.

Moreover, notice that the function signature of append_atom() is
exactly the same as atomv->handler's.  I wonder if it would be
easier to understand if you made append_atom() the handler for a
non-magic atoms, which would let you do the above without any if/else
and just a single unconditional

	atomv->handler(atomv, &state);

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-24 22:13   ` Junio C Hamano
@ 2015-08-24 22:15     ` Junio C Hamano
  2015-08-25 13:28       ` Karthik Nayak
  2015-08-25  6:47     ` Matthieu Moy
  2015-08-25 13:28     ` Karthik Nayak
  2 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:15 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

Junio C Hamano <gitster@pobox.com> writes:

> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> +static void end_atom_handler(struct atom_value *atomv, struct
>> ref_formatting_state *state)
>> +{
>> +	struct ref_formatting_stack *current = state->stack;
>> +	struct strbuf s = STRBUF_INIT;
>> +
>> +	if (!current->at_end)
>> +		die(_("format: `end` atom used without a supporting atom"));
>> +	current->at_end(current);
>> +	/*
>> +	 * Whenever we have more than one stack element that means we
>> +	 * are using a certain modifier atom. In that case we need to
>> +	 * perform quote formatting.
>> +	 */
>> +	if (!state->stack->prev->prev) {
>
> The comment and the condition seem to be saying opposite things.
> The code says "If the stack only has one prev that is the very
> initial one, then we do the quoting, i.e. the result of expanding
> the enclosed string in %(start-something)...%(end) is quoted only
> when that appears at the top level", which feels more correct...

As this already allows us to use nested construct, I think we would
want to have test that uses

    %(align:30,left)%(align:20,right)%(refname:short)%(end)%(end)

or something like that ;-)

Very nice.

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-22  3:39 ` [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes Karthik Nayak
@ 2015-08-24 22:24   ` Junio C Hamano
  2015-08-25 13:07     ` Karthik Nayak
  2015-08-26 16:10   ` Michael Haggerty
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:24 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Karthik Nayak, git, christian.couder, Matthieu.Moy

Karthik Nayak <karthik.188@gmail.com> writes:

> From: Karthik Nayak <karthik.188@gmail.com>
>
> Add a function called 'for_each_reftype_fullpath()' to refs.{c,h}
> which iterates through each ref for the given path without trimming
> the path and also accounting for broken refs, if mentioned.

For this part, I think you would want to borrow an extra pair of
eyes from Michael.

>
> Add 'filter_ref_kind()' in ref-filter.c to check the kind of ref being
> handled and return the kind to 'ref_filter_handler()', where we
> discard refs which we do not need and assign the kind to needed refs.
>
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
>  ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>  ref-filter.h | 12 ++++++++++--
>  refs.c       |  9 +++++++++
>  refs.h       |  1 +
>  4 files changed, 74 insertions(+), 7 deletions(-)
>
> diff --git a/ref-filter.c b/ref-filter.c
> index ffec10a..d5fae1a 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -1123,6 +1123,36 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
>  	return ref;
>  }
>  
> +static int filter_ref_kind(struct ref_filter *filter, const char *refname)
> +{
> +	unsigned int i;
> +
> +	static struct {
> +		const char *prefix;
> +		unsigned int kind;
> +	} ref_kind[] = {
> +		{ "refs/heads/" , FILTER_REFS_BRANCHES },
> +		{ "refs/remotes/" , FILTER_REFS_REMOTES },
> +		{ "refs/tags/", FILTER_REFS_TAGS}
> +	};
> +
> +	if (filter->kind == FILTER_REFS_BRANCHES)
> +		return FILTER_REFS_BRANCHES;
> +	else if (filter->kind == FILTER_REFS_REMOTES)
> +		return FILTER_REFS_REMOTES;
> +	else if (filter->kind == FILTER_REFS_TAGS)
> +		return FILTER_REFS_TAGS;
> +	else if (!strcmp(refname, "HEAD"))
> +		return FILTER_REFS_DETACHED_HEAD;
> +
> +	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
> +		if (starts_with(refname, ref_kind[i].prefix))
> +			return ref_kind[i].kind;
> +	}
> +
> +	return FILTER_REFS_OTHERS;
> +}
> +
>  /*
>   * A call-back given to for_each_ref().  Filter refs and keep them for
>   * later object processing.
> @@ -1133,6 +1163,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  	struct ref_filter *filter = ref_cbdata->filter;
>  	struct ref_array_item *ref;
>  	struct commit *commit = NULL;
> +	unsigned int kind;
>  
>  	if (flag & REF_BAD_NAME) {
>  		warning("ignoring ref with broken name %s", refname);
> @@ -1144,6 +1175,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  		return 0;
>  	}
>  
> +	kind = filter_ref_kind(filter, refname);
> +	if (!(kind & filter->kind))
> +		return 0;
> +

When filter->kind is set to some single-bit thing (e.g.
FILTER_REFS_BRANCHES) by the caller of ref_filter_handler(), then
this call of filter_ref_kind() will just parrot that without even
looking at refname.  And then the if statement says "yes, they have
common bit(s)".  Even when refname is "refs/tags/v1.0" or "HEAD".

And if this code knows that "refs/tags/v1.0" or "HEAD" will never
come when filter->kind is exactly FILTER_REFS_BRANCHES, then I do
not see the point of calling filter_ref_kind().

I am not sure what this is trying to do.  Can you clarify the
thinking behind this as a comment to filter_ref_kind()?

> @@ -1175,6 +1210,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  
>  	REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
>  	ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
> +	ref->kind = kind;
>  	return 0;
>  }
>  
> @@ -1251,16 +1287,29 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
>  {
>  	struct ref_filter_cbdata ref_cbdata;
>  	int ret = 0;
> +	unsigned int broken = 0;
>  
>  	ref_cbdata.array = array;
>  	ref_cbdata.filter = filter;
>  
>  	/*  Simple per-ref filtering */
> -	if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
> -		ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
> -	else if (type & FILTER_REFS_ALL)
> -		ret = for_each_ref(ref_filter_handler, &ref_cbdata);
> -	else if (type)
> +	if (type & FILTER_REFS_INCLUDE_BROKEN) {
> +		type &= ~FILTER_REFS_INCLUDE_BROKEN;
> +		broken = 1;
> +	}
> +
> +	filter->kind = type;
> +	if (type == FILTER_REFS_BRANCHES)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
> +	else if (type == FILTER_REFS_REMOTES)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
> +	else if (type == FILTER_REFS_TAGS)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
> +	else if (type & FILTER_REFS_ALL) {
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
> +		if (type & FILTER_REFS_DETACHED_HEAD)
> +			head_ref(ref_filter_handler, &ref_cbdata);
> +	} else
>  		die("filter_refs: invalid type");
>  
>  	/*  Filters that need revision walking */
> diff --git a/ref-filter.h b/ref-filter.h
> index 45026d0..99f081b 100644
> --- a/ref-filter.h
> +++ b/ref-filter.h
> @@ -13,8 +13,14 @@
>  #define QUOTE_PYTHON 4
>  #define QUOTE_TCL 8
>  
> -#define FILTER_REFS_INCLUDE_BROKEN 0x1
> -#define FILTER_REFS_ALL 0x2
> +#define FILTER_REFS_INCLUDE_BROKEN 0x0001
> +#define FILTER_REFS_TAGS           0x0002
> +#define FILTER_REFS_BRANCHES       0x0004
> +#define FILTER_REFS_REMOTES        0x0008
> +#define FILTER_REFS_OTHERS         0x0010
> +#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
> +				    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
> +#define FILTER_REFS_DETACHED_HEAD  0x0020
>  
>  struct atom_value;
>  
> @@ -27,6 +33,7 @@ struct ref_sorting {
>  struct ref_array_item {
>  	unsigned char objectname[20];
>  	int flag;
> +	unsigned int kind;
>  	const char *symref;
>  	struct commit *commit;
>  	struct atom_value *value;
> @@ -51,6 +58,7 @@ struct ref_filter {
>  	struct commit *merge_commit;
>  
>  	unsigned int with_commit_tag_algo : 1;
> +	unsigned int kind;
>  };
>  
>  struct ref_filter_cbdata {
> diff --git a/refs.c b/refs.c
> index 4e15f60..3266617 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -2150,6 +2150,15 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
>  			       strlen(git_replace_ref_base), 0, cb_data);
>  }
>  
> +int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data)
> +{
> +	unsigned int flag = 0;
> +
> +	if (broken)
> +		flag = DO_FOR_EACH_INCLUDE_BROKEN;
> +	return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
> +}
> +
>  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>  {
>  	struct strbuf buf = STRBUF_INIT;
> diff --git a/refs.h b/refs.h
> index e9a5f32..6e913ee 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -179,6 +179,7 @@ extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
>  extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
>  extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
>  extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
> +extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data);
>  
>  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
>  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-23 20:00 ` [PATCH v13 00/12] port tag.c to use ref-filter APIs Matthieu Moy
  2015-08-24 15:09   ` Karthik Nayak
@ 2015-08-24 22:34   ` Junio C Hamano
  2015-08-24 22:58     ` Junio C Hamano
  2015-08-25 13:23     ` Karthik Nayak
  1 sibling, 2 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:34 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Karthik Nayak, git, christian.couder

Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:

> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index 1997657..06d468e 100644
>> --- a/Documentation/git-for-each-ref.txt
>> +++ b/Documentation/git-for-each-ref.txt
>> @@ -133,7 +133,8 @@ align::
>>  	`<position>` is either left, right or middle and `<width>` is
>>  	the total length of the content with alignment. If the
>>  	contents length is more than the width then no alignment is
>> -	performed.
>> +	performed. If used with '--quote' everything in between %(align:..)
>> +	and %(end) is quoted.
>
> There's no --quote, there are --shell, --python, ... (well, actually, I
> would have prefered to have a single --quote=language option, but that's
> not how it is now).
>
> I had already commented on a preliminary version of this series
> off-list. I think all my previous comments have been taken into account.

Thanks, both.  I think this is pretty close to being satisfactory
;-)  There may probably be a handful of minor nits like the above
that need to be addressed, but I do not think I saw anything
glaringly wrong that makes the series unsalvageable.  It was a very
pleasant read.

It's almost there, and I am very happy to see how this and other
series evolved so far ;-)

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 18:53     ` Junio C Hamano
@ 2015-08-24 22:35       ` Junio C Hamano
  2015-08-26 10:07         ` Karthik Nayak
  2015-08-25 13:25       ` Karthik Nayak
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:35 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

Junio C Hamano <gitster@pobox.com> writes:

> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>> ...
>>>> +     performed. If used with '--quote' everything in between %(align:..)
>>>> +     and %(end) is quoted.
>> ...
>> I might have misunderstood you, But based on the discussion held here
>> (thread.gmane.org/gmane.comp.version-control.git/276140)
>> I thought we wanted everything inside the %(align) .... %(end) atoms
>> to be quoted.
>
> Perhaps I misunderstood your intention in the doc.
> 	
> I took "everything in between %(align:...) and %(end) is quoted" to
> mean that
>
> 	%(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>
> can never satisfy %(if:empty), because %(align)%(end) would expand
> to a string that has two single-quotes, that is not an empty string.
>
> If that is not what would happen in the "branch --list" enhancement,
> then the proposed behaviour is good, but the above documentation would
> need to be updated when it happens, I think.  It at least is misleading.

OK, now I checked the code, and I _think_ the recursive logic is
doing the right thing (modulo minor nits on comment-vs-code
discrepancy and code structure I sent separately).

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 22:34   ` Junio C Hamano
@ 2015-08-24 22:58     ` Junio C Hamano
  2015-08-25 13:26       ` Karthik Nayak
  2015-08-25 13:23     ` Karthik Nayak
  1 sibling, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-24 22:58 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Karthik Nayak, git, christian.couder

Junio C Hamano <gitster@pobox.com> writes:

> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>>> index 1997657..06d468e 100644
>>> --- a/Documentation/git-for-each-ref.txt
>>> +++ b/Documentation/git-for-each-ref.txt
>>> @@ -133,7 +133,8 @@ align::
>>>  	`<position>` is either left, right or middle and `<width>` is
>>>  	the total length of the content with alignment. If the
>>>  	contents length is more than the width then no alignment is
>>> -	performed.
>>> +	performed. If used with '--quote' everything in between %(align:..)
>>> +	and %(end) is quoted.
>>
>> There's no --quote, there are --shell, --python, ... (well, actually, I
>> would have prefered to have a single --quote=language option, but that's
>> not how it is now).
>>
>> I had already commented on a preliminary version of this series
>> off-list. I think all my previous comments have been taken into account.
>
> Thanks, both.  I think this is pretty close to being satisfactory
> ;-)  There may probably be a handful of minor nits like the above
> that need to be addressed, but I do not think I saw anything
> glaringly wrong that makes the series unsalvageable.  It was a very
> pleasant read.
>
> It's almost there, and I am very happy to see how this and other
> series evolved so far ;-)

Having said all that, it seems that there is some trivial typo or
thinko in the formatting code to break t7004.

Here is what I see...

ok 98 - verifying rfc1991 signature

expecting success:
        echo "rfc1991" >gpghome/gpg.conf &&
        echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
        git tag -l -n1 rfc1991-signed-tag >actual &&
        test_cmp expect actual &&
        git tag -l -n2 rfc1991-signed-tag >actual &&
        test_cmp expect actual &&
        git tag -l -n999 rfc1991-signed-tag >actual &&
        test_cmp expect actual

--- expect      2015-08-24 22:54:44.607272653 +0000
+++ actual      2015-08-24 22:54:44.611272643 +0000
@@ -1 +1 @@
-rfc1991-signed-tag RFC1991 signed tag
+rfc1991-signed-tagRFC1991 signed tag
not ok 99 - list tag with rfc1991 signature

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-24 22:13   ` Junio C Hamano
  2015-08-24 22:15     ` Junio C Hamano
@ 2015-08-25  6:47     ` Matthieu Moy
  2015-08-25 13:30       ` Karthik Nayak
  2015-08-25 17:52       ` Junio C Hamano
  2015-08-25 13:28     ` Karthik Nayak
  2 siblings, 2 replies; 60+ messages in thread
From: Matthieu Moy @ 2015-08-25  6:47 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Karthik Nayak, git, christian.couder

Junio C Hamano <gitster@pobox.com> writes:

> You can see that I expected that "if !state.stack->prev" check to be
> inside append_atom(), and I would imagine future readers would have
> the same expectation when reading this code.  I.e.
>
> 	append_atom(struct atom_value *v, struct ref_f_s *state)
>         {
> 		if (state->stack->prev)
> 			strbuf_addstr(&state->stack->output, v->s);
> 		else
>                 	quote_format(&state->stack->output, v->s, state->quote_style);
> 	}
>
> The end result may be the same,

There's another call to append_atom() when inserting the "reset color at
end of line if needed", so moving this if inside append_atom means we
would do the check also for the reset color. It would not change the
behavior (by construction, we insert it only when the stack has only the
initial element), so it's OK.

I agree that this is a good thing to do.

> Moreover, notice that the function signature of append_atom() is
> exactly the same as atomv->handler's.  I wonder if it would be
> easier to understand if you made append_atom() the handler for a
> non-magic atoms, which would let you do the above without any if/else
> and just a single unconditional

I can't decide between "ah, very elegant" and "no, too much magic" ;-).
I lean towards the former.

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack
  2015-08-24 21:56   ` Junio C Hamano
@ 2015-08-25 10:26     ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 10:26 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 3:26 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> +static void push_new_stack_element(struct ref_formatting_stack **stack)
>> +{
>
> Micronit.  Perhaps s/_new//;, as you do not call the other function
> pop_old_stack_element().
>
> The remainder of this step looks pretty straight-forward and was a
> pleasant read.
>
> Thanks.

Will do, thanks for reviewing :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-24 22:24   ` Junio C Hamano
@ 2015-08-25 13:07     ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Michael Haggerty, Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 3:54 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> From: Karthik Nayak <karthik.188@gmail.com>
>>
>> Add a function called 'for_each_reftype_fullpath()' to refs.{c,h}
>> which iterates through each ref for the given path without trimming
>> the path and also accounting for broken refs, if mentioned.
>
> For this part, I think you would want to borrow an extra pair of
> eyes from Michael.
>
>>
>> Add 'filter_ref_kind()' in ref-filter.c to check the kind of ref being
>> handled and return the kind to 'ref_filter_handler()', where we
>> discard refs which we do not need and assign the kind to needed refs.
>>
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>>  ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>>  ref-filter.h | 12 ++++++++++--
>>  refs.c       |  9 +++++++++
>>  refs.h       |  1 +
>>  4 files changed, 74 insertions(+), 7 deletions(-)
>>
>> diff --git a/ref-filter.c b/ref-filter.c
>> index ffec10a..d5fae1a 100644
>> --- a/ref-filter.c
>> +++ b/ref-filter.c
>> @@ -1123,6 +1123,36 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
>>       return ref;
>>  }
>>
>> +static int filter_ref_kind(struct ref_filter *filter, const char *refname)
>> +{
>> +     unsigned int i;
>> +
>> +     static struct {
>> +             const char *prefix;
>> +             unsigned int kind;
>> +     } ref_kind[] = {
>> +             { "refs/heads/" , FILTER_REFS_BRANCHES },
>> +             { "refs/remotes/" , FILTER_REFS_REMOTES },
>> +             { "refs/tags/", FILTER_REFS_TAGS}
>> +     };
>> +
>> +     if (filter->kind == FILTER_REFS_BRANCHES)
>> +             return FILTER_REFS_BRANCHES;
>> +     else if (filter->kind == FILTER_REFS_REMOTES)
>> +             return FILTER_REFS_REMOTES;
>> +     else if (filter->kind == FILTER_REFS_TAGS)
>> +             return FILTER_REFS_TAGS;
>> +     else if (!strcmp(refname, "HEAD"))
>> +             return FILTER_REFS_DETACHED_HEAD;
>> +
>> +     for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
>> +             if (starts_with(refname, ref_kind[i].prefix))
>> +                     return ref_kind[i].kind;
>> +     }
>> +
>> +     return FILTER_REFS_OTHERS;
>> +}
>> +
>>  /*
>>   * A call-back given to for_each_ref().  Filter refs and keep them for
>>   * later object processing.
>> @@ -1133,6 +1163,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>       struct ref_filter *filter = ref_cbdata->filter;
>>       struct ref_array_item *ref;
>>       struct commit *commit = NULL;
>> +     unsigned int kind;
>>
>>       if (flag & REF_BAD_NAME) {
>>               warning("ignoring ref with broken name %s", refname);
>> @@ -1144,6 +1175,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>               return 0;
>>       }
>>
>> +     kind = filter_ref_kind(filter, refname);
>> +     if (!(kind & filter->kind))
>> +             return 0;
>> +
>
> When filter->kind is set to some single-bit thing (e.g.
> FILTER_REFS_BRANCHES) by the caller of ref_filter_handler(), then
> this call of filter_ref_kind() will just parrot that without even
> looking at refname.  And then the if statement says "yes, they have
> common bit(s)".  Even when refname is "refs/tags/v1.0" or "HEAD".
>

This happens for branches remotes and tags, and this is intentional so as to
skip the uncessary checking to be done, if you see filter_refs() if it's called
with a single bit (branches, tags or remotes) then it calls
for_each_reftype_fullpath()
with the corresponding path, which would only give us the refs which we needed
hence checking them is not really needed. But the call to
filter_ref_kind() would just
give us the kind of the ref.

> And if this code knows that "refs/tags/v1.0" or "HEAD" will never
> come when filter->kind is exactly FILTER_REFS_BRANCHES, then I do
> not see the point of calling filter_ref_kind().
>

The point is to have a common function which returns the current ref kind,
Even if we know that the current ref kind is the same as the filter->kind.

> I am not sure what this is trying to do.  Can you clarify the
> thinking behind this as a comment to filter_ref_kind()?

I should added a comment here, I will do so now.

>
>> @@ -1175,6 +1210,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>
>>       REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
>>       ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
>> +     ref->kind = kind;
>>       return 0;
>>  }
>>
>> @@ -1251,16 +1287,29 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
>>  {
>>       struct ref_filter_cbdata ref_cbdata;
>>       int ret = 0;
>> +     unsigned int broken = 0;
>>
>>       ref_cbdata.array = array;
>>       ref_cbdata.filter = filter;
>>
>>       /*  Simple per-ref filtering */
>> -     if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
>> -             ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
>> -     else if (type & FILTER_REFS_ALL)
>> -             ret = for_each_ref(ref_filter_handler, &ref_cbdata);
>> -     else if (type)
>> +     if (type & FILTER_REFS_INCLUDE_BROKEN) {
>> +             type &= ~FILTER_REFS_INCLUDE_BROKEN;
>> +             broken = 1;
>> +     }
>> +
>> +     filter->kind = type;
>> +     if (type == FILTER_REFS_BRANCHES)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
>> +     else if (type == FILTER_REFS_REMOTES)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
>> +     else if (type == FILTER_REFS_TAGS)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
>> +     else if (type & FILTER_REFS_ALL) {
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>> +             if (type & FILTER_REFS_DETACHED_HEAD)
>> +                     head_ref(ref_filter_handler, &ref_cbdata);
>> +     } else
>>               die("filter_refs: invalid type");
>>
>>       /*  Filters that need revision walking */
>> diff --git a/ref-filter.h b/ref-filter.h
>> index 45026d0..99f081b 100644
>> --- a/ref-filter.h
>> +++ b/ref-filter.h
>> @@ -13,8 +13,14 @@
>>  #define QUOTE_PYTHON 4
>>  #define QUOTE_TCL 8
>>
>> -#define FILTER_REFS_INCLUDE_BROKEN 0x1
>> -#define FILTER_REFS_ALL 0x2
>> +#define FILTER_REFS_INCLUDE_BROKEN 0x0001
>> +#define FILTER_REFS_TAGS           0x0002
>> +#define FILTER_REFS_BRANCHES       0x0004
>> +#define FILTER_REFS_REMOTES        0x0008
>> +#define FILTER_REFS_OTHERS         0x0010
>> +#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
>> +                                 FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
>> +#define FILTER_REFS_DETACHED_HEAD  0x0020
>>
>>  struct atom_value;
>>
>> @@ -27,6 +33,7 @@ struct ref_sorting {
>>  struct ref_array_item {
>>       unsigned char objectname[20];
>>       int flag;
>> +     unsigned int kind;
>>       const char *symref;
>>       struct commit *commit;
>>       struct atom_value *value;
>> @@ -51,6 +58,7 @@ struct ref_filter {
>>       struct commit *merge_commit;
>>
>>       unsigned int with_commit_tag_algo : 1;
>> +     unsigned int kind;
>>  };
>>
>>  struct ref_filter_cbdata {
>> diff --git a/refs.c b/refs.c
>> index 4e15f60..3266617 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -2150,6 +2150,15 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
>>                              strlen(git_replace_ref_base), 0, cb_data);
>>  }
>>
>> +int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data)
>> +{
>> +     unsigned int flag = 0;
>> +
>> +     if (broken)
>> +             flag = DO_FOR_EACH_INCLUDE_BROKEN;
>> +     return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
>> +}
>> +
>>  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>>  {
>>       struct strbuf buf = STRBUF_INIT;
>> diff --git a/refs.h b/refs.h
>> index e9a5f32..6e913ee 100644
>> --- a/refs.h
>> +++ b/refs.h
>> @@ -179,6 +179,7 @@ extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
>>  extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
>>  extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
>>  extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
>> +extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data);
>>
>>  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
>>  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);

Thanks for the review.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 22:34   ` Junio C Hamano
  2015-08-24 22:58     ` Junio C Hamano
@ 2015-08-25 13:23     ` Karthik Nayak
  1 sibling, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:23 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Tue, Aug 25, 2015 at 4:04 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>>> index 1997657..06d468e 100644
>>> --- a/Documentation/git-for-each-ref.txt
>>> +++ b/Documentation/git-for-each-ref.txt
>>> @@ -133,7 +133,8 @@ align::
>>>      `<position>` is either left, right or middle and `<width>` is
>>>      the total length of the content with alignment. If the
>>>      contents length is more than the width then no alignment is
>>> -    performed.
>>> +    performed. If used with '--quote' everything in between %(align:..)
>>> +    and %(end) is quoted.
>>
>> There's no --quote, there are --shell, --python, ... (well, actually, I
>> would have prefered to have a single --quote=language option, but that's
>> not how it is now).
>>
>> I had already commented on a preliminary version of this series
>> off-list. I think all my previous comments have been taken into account.
>
> Thanks, both.  I think this is pretty close to being satisfactory
> ;-)  There may probably be a handful of minor nits like the above
> that need to be addressed, but I do not think I saw anything
> glaringly wrong that makes the series unsalvageable.  It was a very
> pleasant read.
>
> It's almost there, and I am very happy to see how this and other
> series evolved so far ;-)

I like how it's totally different from what I started off with, it's
been a great learning curve
for me. Thanks for reviews.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 18:53     ` Junio C Hamano
  2015-08-24 22:35       ` Junio C Hamano
@ 2015-08-25 13:25       ` Karthik Nayak
  1 sibling, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:25 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 12:23 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>> ...
>>>> +     performed. If used with '--quote' everything in between %(align:..)
>>>> +     and %(end) is quoted.
>> ...
>> I might have misunderstood you, But based on the discussion held here
>> (thread.gmane.org/gmane.comp.version-control.git/276140)
>> I thought we wanted everything inside the %(align) .... %(end) atoms
>> to be quoted.
>
> Perhaps I misunderstood your intention in the doc.
>
> I took "everything in between %(align:...) and %(end) is quoted" to
> mean that
>
>         %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>
> can never satisfy %(if:empty), because %(align)%(end) would expand
> to a string that has two single-quotes, that is not an empty string.
>
> If that is not what would happen in the "branch --list" enhancement,
> then the proposed behaviour is good, but the above documentation would
> need to be updated when it happens, I think.  It at least is misleading.
>
> Thanks.

I'm not sure I checked this condition, will have a look at this, thanks for
pointing it out.

> OK, now I checked the code, and I _think_ the recursive logic is
> doing the right thing (modulo minor nits on comment-vs-code
> discrepancy and code structure I sent separately).

I'm not so sure about that, I'll get back on this. (I didn't think about empty
string alignment and its effect on %(if:empty) and so on).

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 22:58     ` Junio C Hamano
@ 2015-08-25 13:26       ` Karthik Nayak
  2015-08-25 19:16         ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:26 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Tue, Aug 25, 2015 at 4:28 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>>
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>>>> index 1997657..06d468e 100644
>>>> --- a/Documentation/git-for-each-ref.txt
>>>> +++ b/Documentation/git-for-each-ref.txt
>>>> @@ -133,7 +133,8 @@ align::
>>>>     `<position>` is either left, right or middle and `<width>` is
>>>>     the total length of the content with alignment. If the
>>>>     contents length is more than the width then no alignment is
>>>> -   performed.
>>>> +   performed. If used with '--quote' everything in between %(align:..)
>>>> +   and %(end) is quoted.
>>>
>>> There's no --quote, there are --shell, --python, ... (well, actually, I
>>> would have prefered to have a single --quote=language option, but that's
>>> not how it is now).
>>>
>>> I had already commented on a preliminary version of this series
>>> off-list. I think all my previous comments have been taken into account.
>>
>> Thanks, both.  I think this is pretty close to being satisfactory
>> ;-)  There may probably be a handful of minor nits like the above
>> that need to be addressed, but I do not think I saw anything
>> glaringly wrong that makes the series unsalvageable.  It was a very
>> pleasant read.
>>
>> It's almost there, and I am very happy to see how this and other
>> series evolved so far ;-)
>
> Having said all that, it seems that there is some trivial typo or
> thinko in the formatting code to break t7004.
>
> Here is what I see...
>
> ok 98 - verifying rfc1991 signature
>
> expecting success:
>         echo "rfc1991" >gpghome/gpg.conf &&
>         echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
>         git tag -l -n1 rfc1991-signed-tag >actual &&
>         test_cmp expect actual &&
>         git tag -l -n2 rfc1991-signed-tag >actual &&
>         test_cmp expect actual &&
>         git tag -l -n999 rfc1991-signed-tag >actual &&
>         test_cmp expect actual
>
> --- expect      2015-08-24 22:54:44.607272653 +0000
> +++ actual      2015-08-24 22:54:44.611272643 +0000
> @@ -1 +1 @@
> -rfc1991-signed-tag RFC1991 signed tag
> +rfc1991-signed-tagRFC1991 signed tag
> not ok 99 - list tag with rfc1991 signature
>

Thats weird, I just ran all tests, and nothing failed.
Is this mid-way of the patch series? I tried it on the entire
series and seems fine to me

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-24 22:13   ` Junio C Hamano
  2015-08-24 22:15     ` Junio C Hamano
  2015-08-25  6:47     ` Matthieu Moy
@ 2015-08-25 13:28     ` Karthik Nayak
  2 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 3:43 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> +static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style);
>> +
>> +static void end_atom_handler(struct atom_value *atomv, struct ref_formatting_state *state)
>> +{
>> +     struct ref_formatting_stack *current = state->stack;
>> +     struct strbuf s = STRBUF_INIT;
>> +
>> +     if (!current->at_end)
>> +             die(_("format: `end` atom used without a supporting atom"));
>> +     current->at_end(current);
>> +     /*
>> +      * Whenever we have more than one stack element that means we
>> +      * are using a certain modifier atom. In that case we need to
>> +      * perform quote formatting.
>> +      */
>> +     if (!state->stack->prev->prev) {
>
> The comment and the condition seem to be saying opposite things.
> The code says "If the stack only has one prev that is the very
> initial one, then we do the quoting, i.e. the result of expanding
> the enclosed string in %(start-something)...%(end) is quoted only
> when that appears at the top level", which feels more correct than
> the comment that says "if we are about to pop after seeing the
> first %(end) in %(start)...%(another)...%(end)...%(end) sequence,
> we quote what is between %(another)...%(end)".
>

That sounds misleading indeed will need to change that.

>> +             perform_quote_formatting(&s, current->output.buf, state->quote_style);
>> +             strbuf_reset(&current->output);
>> +             strbuf_addbuf(&current->output, &s);
>> +     }
>> +     strbuf_release(&s);
>> +     pop_stack_element(&state->stack);
>> +}
>> +
>
>> @@ -1228,29 +1315,33 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
>>       qsort(array->items, array->nr, sizeof(struct ref_array_item *), compare_refs);
>>  }
>>
>> -static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
>> +static void perform_quote_formatting(struct strbuf *s, const char *str, int quote_style)
>>  {
>> -     struct strbuf *s = &state->stack->output;
>> -
>> -     switch (state->quote_style) {
>> +     switch (quote_style) {
>>       case QUOTE_NONE:
>> -             strbuf_addstr(s, v->s);
>> +             strbuf_addstr(s, str);
>>               break;
>>       case QUOTE_SHELL:
>> -             sq_quote_buf(s, v->s);
>> +             sq_quote_buf(s, str);
>>               break;
>>       case QUOTE_PERL:
>> -             perl_quote_buf(s, v->s);
>> +             perl_quote_buf(s, str);
>>               break;
>>       case QUOTE_PYTHON:
>> -             python_quote_buf(s, v->s);
>> +             python_quote_buf(s, str);
>>               break;
>>       case QUOTE_TCL:
>> -             tcl_quote_buf(s, v->s);
>> +             tcl_quote_buf(s, str);
>>               break;
>>       }
>>  }
>>
>> +static void append_atom(struct atom_value *v, struct ref_formatting_state *state)
>> +{
>> +     struct strbuf *s = &state->stack->output;
>> +     perform_quote_formatting(s, v->s, state->quote_style);
>
> Hmmm, do we want to unconditionally do the quote here, or only when
> we are not being captured by upcoming %(end) to be consistent with
> the behaviour of end_atom_handler() above?
>
>> @@ -1307,7 +1398,18 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
>>               if (cp < sp)
>>                       append_literal(cp, sp, &state);
>>               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
>> -             append_atom(atomv, &state);
>> +             /*
>> +              * If the atom is a modifier atom, then call the handler function.
>> +              * Else, if this is the first element on the stack, then we need to
>> +              * format the atom as per the given quote. Else we just add the atom value
>> +              * to the current stack element and handle quote formatting at the end.
>> +              */
>> +             if (atomv->handler)
>> +                     atomv->handler(atomv, &state);
>> +             else if (!state.stack->prev)
>> +                     append_atom(atomv, &state);
>> +             else
>> +                     strbuf_addstr(&state.stack->output, atomv->s);
>
> Ahh, this explains why you are not doing it above, but I do not
> think if this is a good division of labor.
>
> You can see that I expected that "if !state.stack->prev" check to be
> inside append_atom(), and I would imagine future readers would have
> the same expectation when reading this code.  I.e.
>
>         append_atom(struct atom_value *v, struct ref_f_s *state)
>         {
>                 if (state->stack->prev)
>                         strbuf_addstr(&state->stack->output, v->s);
>                 else
>                         quote_format(&state->stack->output, v->s, state->quote_style);
>         }
>
> The end result may be the same, but I do think "append_atom is to
> always quote, so we do an unquoted appending by hand" is a bad way
> to do this.
>
> Moreover, notice that the function signature of append_atom() is
> exactly the same as atomv->handler's.  I wonder if it would be
> easier to understand if you made append_atom() the handler for a
> non-magic atoms, which would let you do the above without any if/else
> and just a single unconditional
>
>         atomv->handler(atomv, &state);

I like the atomv->handler() idea I think I'll work on that now.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-24 22:15     ` Junio C Hamano
@ 2015-08-25 13:28       ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:28 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 3:45 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> +static void end_atom_handler(struct atom_value *atomv, struct
>>> ref_formatting_state *state)
>>> +{
>>> +    struct ref_formatting_stack *current = state->stack;
>>> +    struct strbuf s = STRBUF_INIT;
>>> +
>>> +    if (!current->at_end)
>>> +            die(_("format: `end` atom used without a supporting atom"));
>>> +    current->at_end(current);
>>> +    /*
>>> +     * Whenever we have more than one stack element that means we
>>> +     * are using a certain modifier atom. In that case we need to
>>> +     * perform quote formatting.
>>> +     */
>>> +    if (!state->stack->prev->prev) {
>>
>> The comment and the condition seem to be saying opposite things.
>> The code says "If the stack only has one prev that is the very
>> initial one, then we do the quoting, i.e. the result of expanding
>> the enclosed string in %(start-something)...%(end) is quoted only
>> when that appears at the top level", which feels more correct...
>
> As this already allows us to use nested construct, I think we would
> want to have test that uses
>
>     %(align:30,left)%(align:20,right)%(refname:short)%(end)%(end)
>
> or something like that ;-)
>
> Very nice.

Ok, will do that :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-25  6:47     ` Matthieu Moy
@ 2015-08-25 13:30       ` Karthik Nayak
  2015-08-25 17:56         ` Junio C Hamano
  2015-08-25 17:52       ` Junio C Hamano
  1 sibling, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-25 13:30 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Junio C Hamano, Git, Christian Couder

On Tue, Aug 25, 2015 at 12:17 PM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> You can see that I expected that "if !state.stack->prev" check to be
>> inside append_atom(), and I would imagine future readers would have
>> the same expectation when reading this code.  I.e.
>>
>>       append_atom(struct atom_value *v, struct ref_f_s *state)
>>         {
>>               if (state->stack->prev)
>>                       strbuf_addstr(&state->stack->output, v->s);
>>               else
>>                       quote_format(&state->stack->output, v->s, state->quote_style);
>>       }
>>
>> The end result may be the same,
>
> There's another call to append_atom() when inserting the "reset color at
> end of line if needed", so moving this if inside append_atom means we
> would do the check also for the reset color. It would not change the
> behavior (by construction, we insert it only when the stack has only the
> initial element), so it's OK.
>
> I agree that this is a good thing to do.
>
>> Moreover, notice that the function signature of append_atom() is
>> exactly the same as atomv->handler's.  I wonder if it would be
>> easier to understand if you made append_atom() the handler for a
>> non-magic atoms, which would let you do the above without any if/else
>> and just a single unconditional
>
> I can't decide between "ah, very elegant" and "no, too much magic" ;-).
> I lean towards the former.
>

I like the idea of using atomv->handler() a lot, mostly cause this
would eventually
help us clean up populate_atom() which currently seems like a huge dump of code.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-25  6:47     ` Matthieu Moy
  2015-08-25 13:30       ` Karthik Nayak
@ 2015-08-25 17:52       ` Junio C Hamano
  1 sibling, 0 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-25 17:52 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Karthik Nayak, git, christian.couder

Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> You can see that I expected that "if !state.stack->prev" check to be
>> inside append_atom(), and I would imagine future readers would have
>> the same expectation when reading this code.  I.e.
>>
>> 	append_atom(struct atom_value *v, struct ref_f_s *state)
>>         {
>> 		if (state->stack->prev)
>> 			strbuf_addstr(&state->stack->output, v->s);
>> 		else
>>                 	quote_format(&state->stack->output, v->s, state->quote_style);
>> 	}
>>
>> The end result may be the same,
>
> There's another call to append_atom() when inserting the "reset color at
> end of line if needed",  so moving this if inside append_atom means we
> would do the check also for the reset color. It would not change the
> behavior (by construction, we insert it only when the stack has only the
> initial element), so it's OK.

Thanks for checking---I did overlook that other callsite and did not
check if the suggested change was sensible there.

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-25 13:30       ` Karthik Nayak
@ 2015-08-25 17:56         ` Junio C Hamano
  2015-08-26  6:41           ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-25 17:56 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Matthieu Moy, Git, Christian Couder

Karthik Nayak <karthik.188@gmail.com> writes:

> I like the idea of using atomv->handler() a lot, mostly cause this
> would eventually
> help us clean up populate_atom() which currently seems like a huge dump of code.

I think you already said that last time we had this discussion ;-)

http://thread.gmane.org/gmane.comp.version-control.git/275537/focus=275778

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-25 13:26       ` Karthik Nayak
@ 2015-08-25 19:16         ` Junio C Hamano
  2015-08-25 19:43           ` Junio C Hamano
  2015-08-26  5:56           ` Karthik Nayak
  0 siblings, 2 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-25 19:16 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Matthieu Moy, Git, Christian Couder

Karthik Nayak <karthik.188@gmail.com> writes:

>> Here is what I see...
>>
>> ok 98 - verifying rfc1991 signature
>>
>> expecting success:
>>         echo "rfc1991" >gpghome/gpg.conf &&
>>         echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
>>         git tag -l -n1 rfc1991-signed-tag >actual &&
>>         test_cmp expect actual &&
>>         git tag -l -n2 rfc1991-signed-tag >actual &&
>>         test_cmp expect actual &&
>>         git tag -l -n999 rfc1991-signed-tag >actual &&
>>         test_cmp expect actual
>>
>> --- expect      2015-08-24 22:54:44.607272653 +0000
>> +++ actual      2015-08-24 22:54:44.611272643 +0000
>> @@ -1 +1 @@
>> -rfc1991-signed-tag RFC1991 signed tag
>> +rfc1991-signed-tagRFC1991 signed tag
>> not ok 99 - list tag with rfc1991 signature
>
> Thats weird, I just ran all tests, and nothing failed.

You may be missing GPG or RFC1991 prerequisites and not running this
particular test, which could be why you missed it.

Your builtin/tag.c calls show_ref_array_item() with 

	"%(align:16,left)%(refname:short)%(end)"

as the format, and "rfc1991-signed-tag" is longer than 16.

And immediately after showing that, there is this hack at the end of
show_ref_array_item() function, which I _think_ should not be there
in ref-filter.c:show_ref_array_item() in the first place.

	if (lines > 0) {
		struct object_id oid;
		hashcpy(oid.hash, info->objectname);
		show_tag_lines(&oid, lines);
	}

This belongs to the caller who knows that it is dealing with a tag.

But that broken design aside, the reason why this breaks is because
you do not have a separating SP after the aligned short refs.

I didn't check how wide the original is supposed to be, but perhaps
changing builtin/tag.c this way

		if (filter->lines)
-			format = "%(align:16,left)%(refname:short)%(end)";
+			format = "%(align:15,left)%(refname:short)%(end) ";
		else
			format = "%(refname:short)";
	}

may be one way to fix it.  Check the original "tag -l" output for
tags whose names are 14, 15, 16 and 17 display-columns wide and try
to match it.

And then move the tag-specific code at the end of
show_ref_array_item() to here:

	verify_ref_format(format);
	filter_refs(&array, filter, FILTER_REFS_TAGS);
	ref_array_sort(sorting, &array);

-	for (i = 0; i < array.nr; i++)
+	for (i = 0; i < array.nr; i++) {
		show_ref_array_item(array.items[i], format, QUOTE_NONE, filter->lines);
+		if (lines) {
+			struct object_id oid;
+			hashcpy(oid.hash, info->objectname);
+			show_tag_lines(&oid, lines);
+		}
+	}

perhaps.

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-25 19:16         ` Junio C Hamano
@ 2015-08-25 19:43           ` Junio C Hamano
  2015-08-26  5:56           ` Karthik Nayak
  1 sibling, 0 replies; 60+ messages in thread
From: Junio C Hamano @ 2015-08-25 19:43 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Matthieu Moy, Git, Christian Couder

Junio C Hamano <gitster@pobox.com> writes:

> I didn't check how wide the original is supposed to be, but perhaps
> changing builtin/tag.c this way
>
> 		if (filter->lines)
> -			format = "%(align:16,left)%(refname:short)%(end)";
> +			format = "%(align:15,left)%(refname:short)%(end) ";
> 		else
> 			format = "%(refname:short)";
> 	}
>
> may be one way to fix it.  Check the original "tag -l" output for
> tags whose names are 14, 15, 16 and 17 display-columns wide and try
> to match it.

Heh, I did it myself.  %(align:15) with trailing whitespace is what
you want.

An alternative way to spell it would be

    "%(align:16,left)%(refname:short) %(end)"

I don't know which one is more readable, though.

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-25 19:16         ` Junio C Hamano
  2015-08-25 19:43           ` Junio C Hamano
@ 2015-08-26  5:56           ` Karthik Nayak
  2015-08-26 14:37             ` Junio C Hamano
  1 sibling, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-26  5:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Wed, Aug 26, 2015 at 12:46 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>>> Here is what I see...
>>>
>>> ok 98 - verifying rfc1991 signature
>>>
>>> expecting success:
>>>         echo "rfc1991" >gpghome/gpg.conf &&
>>>         echo "rfc1991-signed-tag RFC1991 signed tag" >expect &&
>>>         git tag -l -n1 rfc1991-signed-tag >actual &&
>>>         test_cmp expect actual &&
>>>         git tag -l -n2 rfc1991-signed-tag >actual &&
>>>         test_cmp expect actual &&
>>>         git tag -l -n999 rfc1991-signed-tag >actual &&
>>>         test_cmp expect actual
>>>
>>> --- expect      2015-08-24 22:54:44.607272653 +0000
>>> +++ actual      2015-08-24 22:54:44.611272643 +0000
>>> @@ -1 +1 @@
>>> -rfc1991-signed-tag RFC1991 signed tag
>>> +rfc1991-signed-tagRFC1991 signed tag
>>> not ok 99 - list tag with rfc1991 signature
>>
>> Thats weird, I just ran all tests, and nothing failed.
>
> You may be missing GPG or RFC1991 prerequisites and not running this
> particular test, which could be why you missed it.
>

That explains.

> Your builtin/tag.c calls show_ref_array_item() with
>
>         "%(align:16,left)%(refname:short)%(end)"
>
> as the format, and "rfc1991-signed-tag" is longer than 16.
>
> And immediately after showing that, there is this hack at the end of
> show_ref_array_item() function, which I _think_ should not be there
> in ref-filter.c:show_ref_array_item() in the first place.
>
>         if (lines > 0) {
>                 struct object_id oid;
>                 hashcpy(oid.hash, info->objectname);
>                 show_tag_lines(&oid, lines);
>         }
>
> This belongs to the caller who knows that it is dealing with a tag.
>

Explained the idea behind this below.

> But that broken design aside, the reason why this breaks is because
> you do not have a separating SP after the aligned short refs.
>

Makes sense.

> I didn't check how wide the original is supposed to be, but perhaps
> changing builtin/tag.c this way
>
>                 if (filter->lines)
> -                       format = "%(align:16,left)%(refname:short)%(end)";
> +                       format = "%(align:15,left)%(refname:short)%(end) ";
>                 else
>                         format = "%(refname:short)";
>         }
>
> may be one way to fix it.  Check the original "tag -l" output for
> tags whose names are 14, 15, 16 and 17 display-columns wide and try
> to match it.
>

That should be the fix, since it's a space problem.

> And then move the tag-specific code at the end of
> show_ref_array_item() to here:
>
>         verify_ref_format(format);
>         filter_refs(&array, filter, FILTER_REFS_TAGS);
>         ref_array_sort(sorting, &array);
>
> -       for (i = 0; i < array.nr; i++)
> +       for (i = 0; i < array.nr; i++) {
>                 show_ref_array_item(array.items[i], format, QUOTE_NONE, filter->lines);
> +               if (lines) {
> +                       struct object_id oid;
> +                       hashcpy(oid.hash, info->objectname);
> +                       show_tag_lines(&oid, lines);
> +               }
> +       }
>
> perhaps.

The problem with doing this is, the lines need to be displayed
immediately after  the refname,
followed by a newline, what you're suggesting breaks that flow.

We could pass a boolean to show_ref_array_item() and print a newline if no
lines are to be printed and probably print the newline in tag.c
itself, but seems confusing to me.


> Heh, I did it myself.  %(align:15) with trailing whitespace is what
> you want.
>
> An alternative way to spell it would be
>
>     "%(align:16,left)%(refname:short) %(end)"
>
> I don't know which one is more readable, though.

I find this more readable, since the space is clearly visible, unlike
format = "%(align:15,left)%(refname:short)%(end) "; in which the space
after %(end) is easily unnoticeable.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 04/12] ref-filter: implement an `align` atom
  2015-08-25 17:56         ` Junio C Hamano
@ 2015-08-26  6:41           ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-26  6:41 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Tue, Aug 25, 2015 at 11:26 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> I like the idea of using atomv->handler() a lot, mostly cause this
>> would eventually
>> help us clean up populate_atom() which currently seems like a huge dump of code.
>
> I think you already said that last time we had this discussion ;-)
>
> http://thread.gmane.org/gmane.comp.version-control.git/275537/focus=275778
>

Yes :) I really want to clean that up eventually, every since Duy
mentioned about it.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-24 22:35       ` Junio C Hamano
@ 2015-08-26 10:07         ` Karthik Nayak
  2015-08-26 11:54           ` Matthieu Moy
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-26 10:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 25, 2015 at 4:05 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>> ...
>>>>> +     performed. If used with '--quote' everything in between %(align:..)
>>>>> +     and %(end) is quoted.
>>> ...
>>> I might have misunderstood you, But based on the discussion held here
>>> (thread.gmane.org/gmane.comp.version-control.git/276140)
>>> I thought we wanted everything inside the %(align) .... %(end) atoms
>>> to be quoted.
>>
>> Perhaps I misunderstood your intention in the doc.
>>
>> I took "everything in between %(align:...) and %(end) is quoted" to
>> mean that
>>
>>       %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>
>> can never satisfy %(if:empty), because %(align)%(end) would expand
>> to a string that has two single-quotes, that is not an empty string.
>>
>> If that is not what would happen in the "branch --list" enhancement,
>> then the proposed behaviour is good, but the above documentation would
>> need to be updated when it happens, I think.  It at least is misleading.
>
> OK, now I checked the code, and I _think_ the recursive logic is
> doing the right thing (modulo minor nits on comment-vs-code
> discrepancy and code structure I sent separately).
>

For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
would print non-empty, I guess the documentation holds in that case.
Not sure if we require it to print non-empty.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 10:07         ` Karthik Nayak
@ 2015-08-26 11:54           ` Matthieu Moy
  2015-08-26 15:44             ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: Matthieu Moy @ 2015-08-26 11:54 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Junio C Hamano, Git, Christian Couder

Karthik Nayak <karthik.188@gmail.com> writes:

> On Tue, Aug 25, 2015 at 4:05 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>>> On Mon, Aug 24, 2015 at 10:57 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>>> ...
>>>>>> +     performed. If used with '--quote' everything in between %(align:..)
>>>>>> +     and %(end) is quoted.
>>>> ...
>>>> I might have misunderstood you, But based on the discussion held here
>>>> (thread.gmane.org/gmane.comp.version-control.git/276140)
>>>> I thought we wanted everything inside the %(align) .... %(end) atoms
>>>> to be quoted.
>>>
>>> Perhaps I misunderstood your intention in the doc.
>>>
>>> I took "everything in between %(align:...) and %(end) is quoted" to
>>> mean that
>>>
>>>       %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>>
>>> can never satisfy %(if:empty), because %(align)%(end) would expand
>>> to a string that has two single-quotes, that is not an empty string.
>>>
>>> If that is not what would happen in the "branch --list" enhancement,
>>> then the proposed behaviour is good, but the above documentation would
>>> need to be updated when it happens, I think.  It at least is misleading.
>>
>> OK, now I checked the code, and I _think_ the recursive logic is
>> doing the right thing (modulo minor nits on comment-vs-code
>> discrepancy and code structure I sent separately).
>>
>
> For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
> would print non-empty, I guess the documentation holds in that case.
> Not sure if we require it to print non-empty.

You don't want the %(if) condition to depend on whether
--shell/--python/... is used. Since %(if:empty)%(align)%(end)%(then)
holds when you don't use --shell, you also want it to hold when you
quote. IOW, you should check for emptyness before (or actually without)
doing the quoting. I guess this is what you're doing, and if so, I think
it's "The Right Thing".

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26  5:56           ` Karthik Nayak
@ 2015-08-26 14:37             ` Junio C Hamano
  2015-08-26 19:14               ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-26 14:37 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Matthieu Moy, Git, Christian Couder

Karthik Nayak <karthik.188@gmail.com> writes:

> On Wed, Aug 26, 2015 at 12:46 AM, Junio C Hamano <gitster@pobox.com> wrote:
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>> I didn't check how wide the original is supposed to be, but perhaps
>> changing builtin/tag.c this way
>>
>>                 if (filter->lines)
>> -                       format = "%(align:16,left)%(refname:short)%(end)";
>> +                       format = "%(align:15,left)%(refname:short)%(end) ";
>>                 else
>>                         format = "%(refname:short)";
>>         }
>>
>> may be one way to fix it.  Check the original "tag -l" output for
>> tags whose names are 14, 15, 16 and 17 display-columns wide and try
>> to match it.
>
> That should be the fix, since it's a space problem.
> ....
> The problem with doing this is, the lines need to be displayed
> immediately after  the refname,
> followed by a newline, what you're suggesting breaks that flow.

That is only because show_ref_array_item() does too much.  The
function is given a placeholder string and a set of data to fill the
placeholder with for an item, and the reason why the caller
repeatedly calls it, once per item it has, is because it wants to
produce a one-line-per-item output.  An alternative valid way to
structure the API is to have it format into a string and leave the
printing to the caller.  You can give a new format_ref_array_item()
that does not print but fills a strbuf to this caller, make
show_ref_array_item() a thin wrapper that calls it and prints it
with the final LF for other callers.

Another alternate approach, if you want to give "tag -l" annotation
available to for-each-ref, would be to do this:

       if (filter->lines)
               format = xstrfmt("%%(align:15,left)%%(refname:short)%%(end) "
                                "%%(contents:lines=%s)", filter->lines);
       else
               format = "%(refname:short)";

and teach a new %(contents:lines=1) atom.  That way, you can lose
the ugly special case call to show_tag_lines() that can only be at
the end of the output.  I somehow think this is a better approach.

Of course you can (and probably would want to) do both, giving a
bit lower level "emit to a strbuf" function to the callers _and_
losing hardcoded call to show_tag_lines().

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 11:54           ` Matthieu Moy
@ 2015-08-26 15:44             ` Junio C Hamano
  2015-08-26 15:46               ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-26 15:44 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Karthik Nayak, Git, Christian Couder

Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:

>> For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>> would print non-empty, I guess the documentation holds in that case.
>> Not sure if we require it to print non-empty.
>
> You don't want the %(if) condition to depend on whether
> --shell/--python/... is used. Since %(if:empty)%(align)%(end)%(then)
> holds when you don't use --shell, you also want it to hold when you
> quote. IOW, you should check for emptyness before (or actually without)
> doing the quoting. I guess this is what you're doing, and if so, I think
> it's "The Right Thing".

I agree that %(align)%(end) should expand to empty and %(if:empty)...%(then)
should look at that empty string without quoting.  So 

    %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)

should give "Empty"; otherwise the code is buggy, I think.

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 15:44             ` Junio C Hamano
@ 2015-08-26 15:46               ` Junio C Hamano
  2015-08-26 15:48                 ` Matthieu Moy
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-26 15:46 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Karthik Nayak, Git, Christian Couder

Junio C Hamano <gitster@pobox.com> writes:

> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>
>>> For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>> would print non-empty, I guess the documentation holds in that case.
>>> Not sure if we require it to print non-empty.
>>
>> You don't want the %(if) condition to depend on whether
>> --shell/--python/... is used. Since %(if:empty)%(align)%(end)%(then)
>> holds when you don't use --shell, you also want it to hold when you
>> quote. IOW, you should check for emptyness before (or actually without)
>> doing the quoting. I guess this is what you're doing, and if so, I think
>> it's "The Right Thing".
>
> I agree that %(align)%(end) should expand to empty and %(if:empty)...%(then)
> should look at that empty string without quoting.  So 
>
>     %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>
> should give "Empty"; otherwise the code is buggy, I think.

(I shouldn't be typing while eating...)

It should give "Empty", but the --shell/--python/... may make the
whole "Empty", as the result of %(if:...)...%(end), be quoted.  So
you may see "'Empty'" in the output.

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 15:46               ` Junio C Hamano
@ 2015-08-26 15:48                 ` Matthieu Moy
  2015-08-26 19:11                   ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Matthieu Moy @ 2015-08-26 15:48 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Karthik Nayak, Git, Christian Couder

Junio C Hamano <gitster@pobox.com> writes:

> Junio C Hamano <gitster@pobox.com> writes:
>
>> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>>
>>>> For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>>> would print non-empty, I guess the documentation holds in that case.
>>>> Not sure if we require it to print non-empty.
>>>
>>> You don't want the %(if) condition to depend on whether
>>> --shell/--python/... is used. Since %(if:empty)%(align)%(end)%(then)
>>> holds when you don't use --shell, you also want it to hold when you
>>> quote. IOW, you should check for emptyness before (or actually without)
>>> doing the quoting. I guess this is what you're doing, and if so, I think
>>> it's "The Right Thing".
>>
>> I agree that %(align)%(end) should expand to empty and %(if:empty)...%(then)
>> should look at that empty string without quoting.  So 
>>
>>     %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>
>> should give "Empty"; otherwise the code is buggy, I think.
>
> (I shouldn't be typing while eating...)
>
> It should give "Empty", but the --shell/--python/... may make the
> whole "Empty", as the result of %(if:...)...%(end), be quoted.  So
> you may see "'Empty'" in the output.

Agreed (with both points).

-- 
Matthieu Moy
http://www-verimag.imag.fr/~moy/

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-22  3:39 ` [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes Karthik Nayak
  2015-08-24 22:24   ` Junio C Hamano
@ 2015-08-26 16:10   ` Michael Haggerty
  2015-08-27 12:42     ` Karthik Nayak
  1 sibling, 1 reply; 60+ messages in thread
From: Michael Haggerty @ 2015-08-26 16:10 UTC (permalink / raw)
  To: Karthik Nayak, git; +Cc: christian.couder, Matthieu.Moy, gitster

Comments inline.

On 08/22/2015 05:39 AM, Karthik Nayak wrote:
> From: Karthik Nayak <karthik.188@gmail.com>
> 
> Add a function called 'for_each_reftype_fullpath()' to refs.{c,h}
> which iterates through each ref for the given path without trimming
> the path and also accounting for broken refs, if mentioned.
> 
> Add 'filter_ref_kind()' in ref-filter.c to check the kind of ref being
> handled and return the kind to 'ref_filter_handler()', where we
> discard refs which we do not need and assign the kind to needed refs.
> 
> Mentored-by: Christian Couder <christian.couder@gmail.com>
> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
>  ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>  ref-filter.h | 12 ++++++++++--
>  refs.c       |  9 +++++++++
>  refs.h       |  1 +
>  4 files changed, 74 insertions(+), 7 deletions(-)
> 
> diff --git a/ref-filter.c b/ref-filter.c
> index ffec10a..d5fae1a 100644
> --- a/ref-filter.c
> +++ b/ref-filter.c
> @@ -1123,6 +1123,36 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
>  	return ref;
>  }
>  
> +static int filter_ref_kind(struct ref_filter *filter, const char *refname)
> +{
> +	unsigned int i;
> +
> +	static struct {
> +		const char *prefix;
> +		unsigned int kind;
> +	} ref_kind[] = {
> +		{ "refs/heads/" , FILTER_REFS_BRANCHES },
> +		{ "refs/remotes/" , FILTER_REFS_REMOTES },
> +		{ "refs/tags/", FILTER_REFS_TAGS}
> +	};
> +
> +	if (filter->kind == FILTER_REFS_BRANCHES)
> +		return FILTER_REFS_BRANCHES;
> +	else if (filter->kind == FILTER_REFS_REMOTES)
> +		return FILTER_REFS_REMOTES;
> +	else if (filter->kind == FILTER_REFS_TAGS)
> +		return FILTER_REFS_TAGS;
> +	else if (!strcmp(refname, "HEAD"))
> +		return FILTER_REFS_DETACHED_HEAD;

I think this would be clearer written like so:

    if (filter->kind == FILTER_REFS_BRANCHES ||
        filter->kind == FILTER_REFS_REMOTES ||
        filter->kind == FILTER_REFS_TAGS)
            return filter->kind;
    if (!strcmp(refname, "HEAD"))
            return FILTER_REFS_DETACHED_HEAD;

Or, even better, if you can set filter->kind to zero if it is not one of
these constants, then you could simplify this to

    if (filter->kind)
            return filter->kind;
    if (!strcmp(refname, "HEAD"))
            return FILTER_REFS_DETACHED_HEAD;

> +
> +	for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
> +		if (starts_with(refname, ref_kind[i].prefix))
> +			return ref_kind[i].kind;
> +	}
> +
> +	return FILTER_REFS_OTHERS;
> +}
> +
>  /*
>   * A call-back given to for_each_ref().  Filter refs and keep them for
>   * later object processing.
> @@ -1133,6 +1163,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  	struct ref_filter *filter = ref_cbdata->filter;
>  	struct ref_array_item *ref;
>  	struct commit *commit = NULL;
> +	unsigned int kind;
>  
>  	if (flag & REF_BAD_NAME) {
>  		warning("ignoring ref with broken name %s", refname);
> @@ -1144,6 +1175,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  		return 0;
>  	}
>  
> +	kind = filter_ref_kind(filter, refname);
> +	if (!(kind & filter->kind))
> +		return 0;
> +
>  	if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
>  		return 0;
>  
> @@ -1175,6 +1210,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>  
>  	REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
>  	ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
> +	ref->kind = kind;
>  	return 0;
>  }
>  
> @@ -1251,16 +1287,29 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
>  {
>  	struct ref_filter_cbdata ref_cbdata;
>  	int ret = 0;
> +	unsigned int broken = 0;
>  
>  	ref_cbdata.array = array;
>  	ref_cbdata.filter = filter;
>  
>  	/*  Simple per-ref filtering */
> -	if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
> -		ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
> -	else if (type & FILTER_REFS_ALL)
> -		ret = for_each_ref(ref_filter_handler, &ref_cbdata);
> -	else if (type)
> +	if (type & FILTER_REFS_INCLUDE_BROKEN) {
> +		type &= ~FILTER_REFS_INCLUDE_BROKEN;
> +		broken = 1;
> +	}

I wouldn't mask out the FILTER_REFS_INCLUDE_BROKEN bit here. Instead I
would write

> +
> +	filter->kind = type;

as

        filter->kind = type & FILTER_REFS_KIND_MASK;

where FILTER_REFS_KIND_MASK is the OR of all of the kind bits. The
advantage is that this approach would remain correct if more bits are
added to type. Then in the following if statements, test filter->kind
instead of type.

> +	if (type == FILTER_REFS_BRANCHES)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
> +	else if (type == FILTER_REFS_REMOTES)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
> +	else if (type == FILTER_REFS_TAGS)
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
> +	else if (type & FILTER_REFS_ALL) {
> +		ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
> +		if (type & FILTER_REFS_DETACHED_HEAD)
> +			head_ref(ref_filter_handler, &ref_cbdata);

The usual promise of the for_each_ref functions is that they stop
iterating if the function ever returns a nonzero value. So the above
should be

                if (! ret && (type & FILTER_REFS_DETACHED_HEAD))
                        ret = head_ref(ref_filter_handler, &ref_cbdata);

Also, these functions usually iterate in lexicographic order, so I think
you should process HEAD before the others.

But there's another problem here. It seems like
FILTER_REFS_DETACHED_HEAD is only processed if (type & FILTER_REFS_ALL)
is nonzero. But shouldn't it be allowed to process *only* HEAD?

So, finally, I think this code should look like

        else if (!filter->kind)
                die("filter_refs: invalid type");
        else {
                if (filter->kind & FILTER_REFS_DETACHED_HEAD)
                        ret = head_ref(ref_filter_handler, &ref_cbdata);
                if (! ret && (filter->kind & FILTER_REFS_ALL))
                        ret =
for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
        }

> +	} else
>  		die("filter_refs: invalid type");
>  
>  	/*  Filters that need revision walking */
> diff --git a/ref-filter.h b/ref-filter.h
> index 45026d0..99f081b 100644
> --- a/ref-filter.h
> +++ b/ref-filter.h
> @@ -13,8 +13,14 @@
>  #define QUOTE_PYTHON 4
>  #define QUOTE_TCL 8
>  
> -#define FILTER_REFS_INCLUDE_BROKEN 0x1
> -#define FILTER_REFS_ALL 0x2
> +#define FILTER_REFS_INCLUDE_BROKEN 0x0001
> +#define FILTER_REFS_TAGS           0x0002
> +#define FILTER_REFS_BRANCHES       0x0004
> +#define FILTER_REFS_REMOTES        0x0008
> +#define FILTER_REFS_OTHERS         0x0010
> +#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
> +				    FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
> +#define FILTER_REFS_DETACHED_HEAD  0x0020

Do you expect it to be useful to OR these bits together, or will (in
practice) all callers want to iterate over one or the other type of
reference, or all references? I ask because allowing an arbitrary
bitmask complicates the code a bit (otherwise filter_ref_kind() wouldn't
be needed).

If there is code that wants to iterate over, say, branches *and* tags,
then maybe it is an acceptable imposition for it to make two function calls,

    for_each_reftype_fullpath(fn, "refs/heads/", broken, cb_data) ||
    for_each_reftype_fullpath(fn, "refs/tags/", broken, cb_data);

which might even be a bit more efficient because it only has to iterate
over those two namespaces rather than all references.

But the more flexible bitmask code is not a lot of extra overhead, so if
you think it will have a use case then it is fine to keep this interface.

>  
>  struct atom_value;
>  
> @@ -27,6 +33,7 @@ struct ref_sorting {
>  struct ref_array_item {
>  	unsigned char objectname[20];
>  	int flag;
> +	unsigned int kind;
>  	const char *symref;
>  	struct commit *commit;
>  	struct atom_value *value;
> @@ -51,6 +58,7 @@ struct ref_filter {
>  	struct commit *merge_commit;
>  
>  	unsigned int with_commit_tag_algo : 1;
> +	unsigned int kind;
>  };
>  
>  struct ref_filter_cbdata {
> diff --git a/refs.c b/refs.c
> index 4e15f60..3266617 100644
> --- a/refs.c
> +++ b/refs.c
> @@ -2150,6 +2150,15 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
>  			       strlen(git_replace_ref_base), 0, cb_data);
>  }
>  
> +int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data)
> +{
> +	unsigned int flag = 0;
> +
> +	if (broken)
> +		flag = DO_FOR_EACH_INCLUDE_BROKEN;
> +	return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
> +}
> +
>  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>  {
>  	struct strbuf buf = STRBUF_INIT;
> diff --git a/refs.h b/refs.h
> index e9a5f32..6e913ee 100644
> --- a/refs.h
> +++ b/refs.h
> @@ -179,6 +179,7 @@ extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
>  extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
>  extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
>  extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
> +extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data);

This function is most like for_each_ref_in(prefix, fn, cb_data).
Therefore, I suggest that you rename the "type" parameter" to "prefix",
maybe reorder its arguments, and maybe rename it (to
for_each_fullref_in()?) for consistency, and maybe put its declaration
next to that function's. (I see that the argument orders among these
functions are already pretty inconsistent, but it seems best to be
consistent with the function that is most similar to it.)

I think the "type"/"prefix" argument can be "const char *".

>  
>  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
>  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
> 

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 15:48                 ` Matthieu Moy
@ 2015-08-26 19:11                   ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-26 19:11 UTC (permalink / raw)
  To: Matthieu Moy; +Cc: Junio C Hamano, Git, Christian Couder

On Wed, Aug 26, 2015 at 9:18 PM, Matthieu Moy
<Matthieu.Moy@grenoble-inp.fr> wrote:
> Junio C Hamano <gitster@pobox.com> writes:
>
>> Junio C Hamano <gitster@pobox.com> writes:
>>
>>> Matthieu Moy <Matthieu.Moy@grenoble-inp.fr> writes:
>>>
>>>>> For the current code %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>>>> would print non-empty, I guess the documentation holds in that case.
>>>>> Not sure if we require it to print non-empty.
>>>>
>>>> You don't want the %(if) condition to depend on whether
>>>> --shell/--python/... is used. Since %(if:empty)%(align)%(end)%(then)
>>>> holds when you don't use --shell, you also want it to hold when you
>>>> quote. IOW, you should check for emptyness before (or actually without)
>>>> doing the quoting. I guess this is what you're doing, and if so, I think
>>>> it's "The Right Thing".
>>>
>>> I agree that %(align)%(end) should expand to empty and %(if:empty)...%(then)
>>> should look at that empty string without quoting.  So
>>>
>>>     %(if:empty)%(align)%(end)%(then)Empty%(else)Not Empty%(end)
>>>
>>> should give "Empty"; otherwise the code is buggy, I think.
>>
>> (I shouldn't be typing while eating...)
>>
>> It should give "Empty", but the --shell/--python/... may make the
>> whole "Empty", as the result of %(if:...)...%(end), be quoted.  So
>> you may see "'Empty'" in the output.
>
> Agreed (with both points).
>

Thanks, will work on this.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 14:37             ` Junio C Hamano
@ 2015-08-26 19:14               ` Karthik Nayak
  2015-08-26 20:19                 ` Junio C Hamano
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-26 19:14 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Wed, Aug 26, 2015 at 8:07 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Wed, Aug 26, 2015 at 12:46 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>> I didn't check how wide the original is supposed to be, but perhaps
>>> changing builtin/tag.c this way
>>>
>>>                 if (filter->lines)
>>> -                       format = "%(align:16,left)%(refname:short)%(end)";
>>> +                       format = "%(align:15,left)%(refname:short)%(end) ";
>>>                 else
>>>                         format = "%(refname:short)";
>>>         }
>>>
>>> may be one way to fix it.  Check the original "tag -l" output for
>>> tags whose names are 14, 15, 16 and 17 display-columns wide and try
>>> to match it.
>>
>> That should be the fix, since it's a space problem.
>> ....
>> The problem with doing this is, the lines need to be displayed
>> immediately after  the refname,
>> followed by a newline, what you're suggesting breaks that flow.
>
> That is only because show_ref_array_item() does too much.  The
> function is given a placeholder string and a set of data to fill the
> placeholder with for an item, and the reason why the caller
> repeatedly calls it, once per item it has, is because it wants to
> produce a one-line-per-item output.  An alternative valid way to
> structure the API is to have it format into a string and leave the
> printing to the caller.  You can give a new format_ref_array_item()
> that does not print but fills a strbuf to this caller, make
> show_ref_array_item() a thin wrapper that calls it and prints it
> with the final LF for other callers.
>
> Another alternate approach, if you want to give "tag -l" annotation
> available to for-each-ref, would be to do this:
>
>        if (filter->lines)
>                format = xstrfmt("%%(align:15,left)%%(refname:short)%%(end) "
>                                 "%%(contents:lines=%s)", filter->lines);
>        else
>                format = "%(refname:short)";
>
> and teach a new %(contents:lines=1) atom.  That way, you can lose
> the ugly special case call to show_tag_lines() that can only be at
> the end of the output.  I somehow think this is a better approach.
>

This seems like a good approach, since contents is already an atom, this would
fit in easily.

> Of course you can (and probably would want to) do both, giving a
> bit lower level "emit to a strbuf" function to the callers _and_
> losing hardcoded call to show_tag_lines().

You're saying remove show_ref_array_item() (even the wrapper you mentioned
above) and just have something like format_ref_array_item() which
would output to a strbuf. and let the caller worry about the printing?

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 19:14               ` Karthik Nayak
@ 2015-08-26 20:19                 ` Junio C Hamano
  2015-08-27 18:00                   ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Junio C Hamano @ 2015-08-26 20:19 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Matthieu Moy, Git, Christian Couder

Karthik Nayak <karthik.188@gmail.com> writes:

> On Wed, Aug 26, 2015 at 8:07 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>> ...  You can give a new format_ref_array_item()
>> that does not print but fills a strbuf to this caller, make
>> show_ref_array_item() a thin wrapper that calls it and prints it
>> with the final LF for other callers.
>>
> You're saying remove show_ref_array_item() (even the wrapper you mentioned
> above) and just have something like format_ref_array_item() which
> would output to a strbuf. and let the caller worry about the printing?

Among the current callers, the one in builtin/tag.c that wants to
trigger show_tag_lines() hack embedded in show_ref_array_item()
function can stop calling show_ref_array_item() and instead can do

	for (i = 0; i < array.nr; i++) {
		struct strbuf out = STRBUF_INIT;
        	format_ref_array_item(&out, ...);
                if (filter->lines) {
                	... append tag lines to out ...
		}
                printf("%s\n", out.buf);
                strbuf_reset(&out);
	}
                
The current and future callers of show_ref_array_item() that do not
want to trigger the show_tag_liens() hack embedded in there may
still want it to print the formatted string including the trailing
LF, so you can keep show_ref_array_item() as a thin wrapper around
format_ref_array_item() for them to call, e.g.

	show_ref_array_item(...) {
		struct strbuf out = STRBUF_INIT;
        	format_ref_array_item(&out, ...);
                printf("%s\n", out.buf);
                strbuf_release(&out);
	}

But if it has only one caller each, you may not even want to have
show_ref_array_item(), if you are going to do the "output to strbuf"
variant.


		        	

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-26 16:10   ` Michael Haggerty
@ 2015-08-27 12:42     ` Karthik Nayak
  2015-08-27 15:24       ` Michael Haggerty
  0 siblings, 1 reply; 60+ messages in thread
From: Karthik Nayak @ 2015-08-27 12:42 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Git, Christian Couder, Matthieu Moy, Junio C Hamano

On Wed, Aug 26, 2015 at 9:40 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
> Comments inline.
>
> On 08/22/2015 05:39 AM, Karthik Nayak wrote:
>> From: Karthik Nayak <karthik.188@gmail.com>
>>
>> Add a function called 'for_each_reftype_fullpath()' to refs.{c,h}
>> which iterates through each ref for the given path without trimming
>> the path and also accounting for broken refs, if mentioned.
>>
>> Add 'filter_ref_kind()' in ref-filter.c to check the kind of ref being
>> handled and return the kind to 'ref_filter_handler()', where we
>> discard refs which we do not need and assign the kind to needed refs.
>>
>> Mentored-by: Christian Couder <christian.couder@gmail.com>
>> Mentored-by: Matthieu Moy <matthieu.moy@grenoble-inp.fr>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>>  ref-filter.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
>>  ref-filter.h | 12 ++++++++++--
>>  refs.c       |  9 +++++++++
>>  refs.h       |  1 +
>>  4 files changed, 74 insertions(+), 7 deletions(-)
>>
>> diff --git a/ref-filter.c b/ref-filter.c
>> index ffec10a..d5fae1a 100644
>> --- a/ref-filter.c
>> +++ b/ref-filter.c
>> @@ -1123,6 +1123,36 @@ static struct ref_array_item *new_ref_array_item(const char *refname,
>>       return ref;
>>  }
>>
>> +static int filter_ref_kind(struct ref_filter *filter, const char *refname)
>> +{
>> +     unsigned int i;
>> +
>> +     static struct {
>> +             const char *prefix;
>> +             unsigned int kind;
>> +     } ref_kind[] = {
>> +             { "refs/heads/" , FILTER_REFS_BRANCHES },
>> +             { "refs/remotes/" , FILTER_REFS_REMOTES },
>> +             { "refs/tags/", FILTER_REFS_TAGS}
>> +     };
>> +
>> +     if (filter->kind == FILTER_REFS_BRANCHES)
>> +             return FILTER_REFS_BRANCHES;
>> +     else if (filter->kind == FILTER_REFS_REMOTES)
>> +             return FILTER_REFS_REMOTES;
>> +     else if (filter->kind == FILTER_REFS_TAGS)
>> +             return FILTER_REFS_TAGS;
>> +     else if (!strcmp(refname, "HEAD"))
>> +             return FILTER_REFS_DETACHED_HEAD;
>
> I think this would be clearer written like so:
>
>     if (filter->kind == FILTER_REFS_BRANCHES ||
>         filter->kind == FILTER_REFS_REMOTES ||
>         filter->kind == FILTER_REFS_TAGS)
>             return filter->kind;
>     if (!strcmp(refname, "HEAD"))
>             return FILTER_REFS_DETACHED_HEAD;
>

This looks good.

> Or, even better, if you can set filter->kind to zero if it is not one of
> these constants, then you could simplify this to
>
>     if (filter->kind)
>             return filter->kind;
>     if (!strcmp(refname, "HEAD"))
>             return FILTER_REFS_DETACHED_HEAD;
>

But then that would mean we would loose the ability of having each
ref_array_item
know its kind, as we return the kind and set it in ref_filter_handler().

This would work in the current scenario. This is particularly needed
in the future for
branch.c using ref-filter where we might have to append "remotes/" for
printing remotes
via "git branch - a".

>> +
>> +     for (i = 0; i < ARRAY_SIZE(ref_kind); i++) {
>> +             if (starts_with(refname, ref_kind[i].prefix))
>> +                     return ref_kind[i].kind;
>> +     }
>> +
>> +     return FILTER_REFS_OTHERS;
>> +}
>> +
>>  /*
>>   * A call-back given to for_each_ref().  Filter refs and keep them for
>>   * later object processing.
>> @@ -1133,6 +1163,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>       struct ref_filter *filter = ref_cbdata->filter;
>>       struct ref_array_item *ref;
>>       struct commit *commit = NULL;
>> +     unsigned int kind;
>>
>>       if (flag & REF_BAD_NAME) {
>>               warning("ignoring ref with broken name %s", refname);
>> @@ -1144,6 +1175,10 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>               return 0;
>>       }
>>
>> +     kind = filter_ref_kind(filter, refname);
>> +     if (!(kind & filter->kind))
>> +             return 0;
>> +
>>       if (*filter->name_patterns && !match_name_as_path(filter->name_patterns, refname))
>>               return 0;
>>
>> @@ -1175,6 +1210,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
>>
>>       REALLOC_ARRAY(ref_cbdata->array->items, ref_cbdata->array->nr + 1);
>>       ref_cbdata->array->items[ref_cbdata->array->nr++] = ref;
>> +     ref->kind = kind;
>>       return 0;
>>  }
>>
>> @@ -1251,16 +1287,29 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
>>  {
>>       struct ref_filter_cbdata ref_cbdata;
>>       int ret = 0;
>> +     unsigned int broken = 0;
>>
>>       ref_cbdata.array = array;
>>       ref_cbdata.filter = filter;
>>
>>       /*  Simple per-ref filtering */
>> -     if (type & (FILTER_REFS_ALL | FILTER_REFS_INCLUDE_BROKEN))
>> -             ret = for_each_rawref(ref_filter_handler, &ref_cbdata);
>> -     else if (type & FILTER_REFS_ALL)
>> -             ret = for_each_ref(ref_filter_handler, &ref_cbdata);
>> -     else if (type)
>> +     if (type & FILTER_REFS_INCLUDE_BROKEN) {
>> +             type &= ~FILTER_REFS_INCLUDE_BROKEN;
>> +             broken = 1;
>> +     }
>
> I wouldn't mask out the FILTER_REFS_INCLUDE_BROKEN bit here. Instead I
> would write
>
>> +
>> +     filter->kind = type;
>
> as
>
>         filter->kind = type & FILTER_REFS_KIND_MASK;
>
> where FILTER_REFS_KIND_MASK is the OR of all of the kind bits. The
> advantage is that this approach would remain correct if more bits are
> added to type. Then in the following if statements, test filter->kind
> instead of type.
>

Thats brilliant.

>> +     if (type == FILTER_REFS_BRANCHES)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
>> +     else if (type == FILTER_REFS_REMOTES)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
>> +     else if (type == FILTER_REFS_TAGS)
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
>> +     else if (type & FILTER_REFS_ALL) {
>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>> +             if (type & FILTER_REFS_DETACHED_HEAD)
>> +                     head_ref(ref_filter_handler, &ref_cbdata);
>
> The usual promise of the for_each_ref functions is that they stop
> iterating if the function ever returns a nonzero value. So the above
> should be
>
>                 if (! ret && (type & FILTER_REFS_DETACHED_HEAD))
>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>
> Also, these functions usually iterate in lexicographic order, so I think
> you should process HEAD before the others.

This is done on purpose, cause we need to print the HEAD ref separately
so we print the last ref_array_item in the ref_array, free that memory and
sort and print the rest, hence HEAD ref is attached to the last.

>
> But there's another problem here. It seems like
> FILTER_REFS_DETACHED_HEAD is only processed if (type & FILTER_REFS_ALL)
> is nonzero. But shouldn't it be allowed to process *only* HEAD?
>
> So, finally, I think this code should look like
>
>         else if (!filter->kind)
>                 die("filter_refs: invalid type");
>         else {
>                 if (filter->kind & FILTER_REFS_DETACHED_HEAD)
>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>                 if (! ret && (filter->kind & FILTER_REFS_ALL))
>                         ret =
> for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>         }
>

So finally something like this perhaps

    if (!filter->kind)
        die("filter_refs: invalid type");
    else {
        if (filter->kind == FILTER_REFS_BRANCHES)
            ret = for_each_reftype_fullpath(ref_filter_handler,
"refs/heads/", broken, &ref_cbdata);
        else if (filter->kind == FILTER_REFS_REMOTES)
            ret = for_each_reftype_fullpath(ref_filter_handler,
"refs/remotes/", broken, &ref_cbdata);
        else if (filter->kind == FILTER_REFS_TAGS)
            ret = for_each_reftype_fullpath(ref_filter_handler,
"refs/tags/", broken, &ref_cbdata);
        else if (filter->kind & FILTER_REFS_ALL)
            ret = for_each_reftype_fullpath(ref_filter_handler, "",
broken, &ref_cbdata);
        if (filter->kind & FILTER_REFS_DETACHED_HEAD)
            head_ref(ref_filter_handler, &ref_cbdata);
    }

>> +     } else
>>               die("filter_refs: invalid type");
>>
>>       /*  Filters that need revision walking */
>> diff --git a/ref-filter.h b/ref-filter.h
>> index 45026d0..99f081b 100644
>> --- a/ref-filter.h
>> +++ b/ref-filter.h
>> @@ -13,8 +13,14 @@
>>  #define QUOTE_PYTHON 4
>>  #define QUOTE_TCL 8
>>
>> -#define FILTER_REFS_INCLUDE_BROKEN 0x1
>> -#define FILTER_REFS_ALL 0x2
>> +#define FILTER_REFS_INCLUDE_BROKEN 0x0001
>> +#define FILTER_REFS_TAGS           0x0002
>> +#define FILTER_REFS_BRANCHES       0x0004
>> +#define FILTER_REFS_REMOTES        0x0008
>> +#define FILTER_REFS_OTHERS         0x0010
>> +#define FILTER_REFS_ALL            (FILTER_REFS_TAGS | FILTER_REFS_BRANCHES | \
>> +                                 FILTER_REFS_REMOTES | FILTER_REFS_OTHERS)
>> +#define FILTER_REFS_DETACHED_HEAD  0x0020
>
> Do you expect it to be useful to OR these bits together, or will (in
> practice) all callers want to iterate over one or the other type of
> reference, or all references? I ask because allowing an arbitrary
> bitmask complicates the code a bit (otherwise filter_ref_kind() wouldn't
> be needed).
>
> If there is code that wants to iterate over, say, branches *and* tags,
> then maybe it is an acceptable imposition for it to make two function calls,
>
>     for_each_reftype_fullpath(fn, "refs/heads/", broken, cb_data) ||
>     for_each_reftype_fullpath(fn, "refs/tags/", broken, cb_data);
>
> which might even be a bit more efficient because it only has to iterate
> over those two namespaces rather than all references.
>
> But the more flexible bitmask code is not a lot of extra overhead, so if
> you think it will have a use case then it is fine to keep this interface.
>

Yeah, 'git branch -l' has '-a' option which iterates over remotes, branches and
also the head ref. Hence the option. Thats also why if only a single
type is needed
we only iterate through those refs.

>>
>>  struct atom_value;
>>
>> @@ -27,6 +33,7 @@ struct ref_sorting {
>>  struct ref_array_item {
>>       unsigned char objectname[20];
>>       int flag;
>> +     unsigned int kind;
>>       const char *symref;
>>       struct commit *commit;
>>       struct atom_value *value;
>> @@ -51,6 +58,7 @@ struct ref_filter {
>>       struct commit *merge_commit;
>>
>>       unsigned int with_commit_tag_algo : 1;
>> +     unsigned int kind;
>>  };
>>
>>  struct ref_filter_cbdata {
>> diff --git a/refs.c b/refs.c
>> index 4e15f60..3266617 100644
>> --- a/refs.c
>> +++ b/refs.c
>> @@ -2150,6 +2150,15 @@ int for_each_replace_ref(each_ref_fn fn, void *cb_data)
>>                              strlen(git_replace_ref_base), 0, cb_data);
>>  }
>>
>> +int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data)
>> +{
>> +     unsigned int flag = 0;
>> +
>> +     if (broken)
>> +             flag = DO_FOR_EACH_INCLUDE_BROKEN;
>> +     return do_for_each_ref(&ref_cache, type, fn, 0, flag, cb_data);
>> +}
>> +
>>  int head_ref_namespaced(each_ref_fn fn, void *cb_data)
>>  {
>>       struct strbuf buf = STRBUF_INIT;
>> diff --git a/refs.h b/refs.h
>> index e9a5f32..6e913ee 100644
>> --- a/refs.h
>> +++ b/refs.h
>> @@ -179,6 +179,7 @@ extern int for_each_remote_ref(each_ref_fn fn, void *cb_data);
>>  extern int for_each_replace_ref(each_ref_fn fn, void *cb_data);
>>  extern int for_each_glob_ref(each_ref_fn fn, const char *pattern, void *cb_data);
>>  extern int for_each_glob_ref_in(each_ref_fn fn, const char *pattern, const char *prefix, void *cb_data);
>> +extern int for_each_reftype_fullpath(each_ref_fn fn, char *type, unsigned int broken, void *cb_data);
>
> This function is most like for_each_ref_in(prefix, fn, cb_data).
> Therefore, I suggest that you rename the "type" parameter" to "prefix",
> maybe reorder its arguments, and maybe rename it (to
> for_each_fullref_in()?) for consistency, and maybe put its declaration
> next to that function's. (I see that the argument orders among these
> functions are already pretty inconsistent, but it seems best to be
> consistent with the function that is most similar to it.)
>
> I think the "type"/"prefix" argument can be "const char *".
>

Agree with everything said here, will do.

>>
>>  extern int head_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
>>  extern int for_each_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data);
>>
>
> Michael
>
> --
> Michael Haggerty
> mhagger@alum.mit.edu
>

Thanks for the review.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-27 12:42     ` Karthik Nayak
@ 2015-08-27 15:24       ` Michael Haggerty
  2015-08-27 15:35         ` Karthik Nayak
  0 siblings, 1 reply; 60+ messages in thread
From: Michael Haggerty @ 2015-08-27 15:24 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy, Junio C Hamano

On 08/27/2015 02:42 PM, Karthik Nayak wrote:
> On Wed, Aug 26, 2015 at 9:40 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
>> On 08/22/2015 05:39 AM, Karthik Nayak wrote:
>>> [...]
>>> +     if (type == FILTER_REFS_BRANCHES)
>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
>>> +     else if (type == FILTER_REFS_REMOTES)
>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
>>> +     else if (type == FILTER_REFS_TAGS)
>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
>>> +     else if (type & FILTER_REFS_ALL) {
>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>>> +             if (type & FILTER_REFS_DETACHED_HEAD)
>>> +                     head_ref(ref_filter_handler, &ref_cbdata);
>>
>> The usual promise of the for_each_ref functions is that they stop
>> iterating if the function ever returns a nonzero value. So the above
>> should be
>>
>>                 if (! ret && (type & FILTER_REFS_DETACHED_HEAD))
>>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>>
>> Also, these functions usually iterate in lexicographic order, so I think
>> you should process HEAD before the others.
> 
> This is done on purpose, cause we need to print the HEAD ref separately
> so we print the last ref_array_item in the ref_array, free that memory and
> sort and print the rest, hence HEAD ref is attached to the last.

Without having looked at the other patches, this makes me wonder whether
it makes sense to store HEAD in the ref_array at all or whether it
should be handled separately.

>> But there's another problem here. It seems like
>> FILTER_REFS_DETACHED_HEAD is only processed if (type & FILTER_REFS_ALL)
>> is nonzero. But shouldn't it be allowed to process *only* HEAD?
>>
>> So, finally, I think this code should look like
>>
>>         else if (!filter->kind)
>>                 die("filter_refs: invalid type");
>>         else {
>>                 if (filter->kind & FILTER_REFS_DETACHED_HEAD)
>>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>>                 if (! ret && (filter->kind & FILTER_REFS_ALL))
>>                         ret =
>> for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>>         }
>>
> 
> So finally something like this perhaps
> 
>     if (!filter->kind)
>         die("filter_refs: invalid type");
>     else {
>         if (filter->kind == FILTER_REFS_BRANCHES)
>             ret = for_each_reftype_fullpath(ref_filter_handler,
> "refs/heads/", broken, &ref_cbdata);
>         else if (filter->kind == FILTER_REFS_REMOTES)
>             ret = for_each_reftype_fullpath(ref_filter_handler,
> "refs/remotes/", broken, &ref_cbdata);
>         else if (filter->kind == FILTER_REFS_TAGS)
>             ret = for_each_reftype_fullpath(ref_filter_handler,
> "refs/tags/", broken, &ref_cbdata);
>         else if (filter->kind & FILTER_REFS_ALL)
>             ret = for_each_reftype_fullpath(ref_filter_handler, "",
> broken, &ref_cbdata);
>         if (filter->kind & FILTER_REFS_DETACHED_HEAD)
>             head_ref(ref_filter_handler, &ref_cbdata);
>     }

Yes, but the last test should be

        if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))

unless you have a reason not to follow the usual convention that a
nonzero return value from fn means that the iteration should be aborted.

Michael

-- 
Michael Haggerty
mhagger@alum.mit.edu

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

* Re: [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes
  2015-08-27 15:24       ` Michael Haggerty
@ 2015-08-27 15:35         ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-27 15:35 UTC (permalink / raw)
  To: Michael Haggerty; +Cc: Git, Christian Couder, Matthieu Moy, Junio C Hamano

On Thu, Aug 27, 2015 at 8:54 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
> On 08/27/2015 02:42 PM, Karthik Nayak wrote:
>> On Wed, Aug 26, 2015 at 9:40 PM, Michael Haggerty <mhagger@alum.mit.edu> wrote:
>>> On 08/22/2015 05:39 AM, Karthik Nayak wrote:
>>>> [...]
>>>> +     if (type == FILTER_REFS_BRANCHES)
>>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/heads/", broken, &ref_cbdata);
>>>> +     else if (type == FILTER_REFS_REMOTES)
>>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/remotes/", broken, &ref_cbdata);
>>>> +     else if (type == FILTER_REFS_TAGS)
>>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "refs/tags/", broken, &ref_cbdata);
>>>> +     else if (type & FILTER_REFS_ALL) {
>>>> +             ret = for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>>>> +             if (type & FILTER_REFS_DETACHED_HEAD)
>>>> +                     head_ref(ref_filter_handler, &ref_cbdata);
>>>
>>> The usual promise of the for_each_ref functions is that they stop
>>> iterating if the function ever returns a nonzero value. So the above
>>> should be
>>>
>>>                 if (! ret && (type & FILTER_REFS_DETACHED_HEAD))
>>>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>>>
>>> Also, these functions usually iterate in lexicographic order, so I think
>>> you should process HEAD before the others.
>>
>> This is done on purpose, cause we need to print the HEAD ref separately
>> so we print the last ref_array_item in the ref_array, free that memory and
>> sort and print the rest, hence HEAD ref is attached to the last.
>
> Without having looked at the other patches, this makes me wonder whether
> it makes sense to store HEAD in the ref_array at all or whether it
> should be handled separately.
>

Well then we'd need another ref_array just for that, that also could
be an option.
But apart from printing it first, everything else is the same for all the refs.

>>> But there's another problem here. It seems like
>>> FILTER_REFS_DETACHED_HEAD is only processed if (type & FILTER_REFS_ALL)
>>> is nonzero. But shouldn't it be allowed to process *only* HEAD?
>>>
>>> So, finally, I think this code should look like
>>>
>>>         else if (!filter->kind)
>>>                 die("filter_refs: invalid type");
>>>         else {
>>>                 if (filter->kind & FILTER_REFS_DETACHED_HEAD)
>>>                         ret = head_ref(ref_filter_handler, &ref_cbdata);
>>>                 if (! ret && (filter->kind & FILTER_REFS_ALL))
>>>                         ret =
>>> for_each_reftype_fullpath(ref_filter_handler, "", broken, &ref_cbdata);
>>>         }
>>>
>>
>> So finally something like this perhaps
>>
>>     if (!filter->kind)
>>         die("filter_refs: invalid type");
>>     else {
>>         if (filter->kind == FILTER_REFS_BRANCHES)
>>             ret = for_each_reftype_fullpath(ref_filter_handler,
>> "refs/heads/", broken, &ref_cbdata);
>>         else if (filter->kind == FILTER_REFS_REMOTES)
>>             ret = for_each_reftype_fullpath(ref_filter_handler,
>> "refs/remotes/", broken, &ref_cbdata);
>>         else if (filter->kind == FILTER_REFS_TAGS)
>>             ret = for_each_reftype_fullpath(ref_filter_handler,
>> "refs/tags/", broken, &ref_cbdata);
>>         else if (filter->kind & FILTER_REFS_ALL)
>>             ret = for_each_reftype_fullpath(ref_filter_handler, "",
>> broken, &ref_cbdata);
>>         if (filter->kind & FILTER_REFS_DETACHED_HEAD)
>>             head_ref(ref_filter_handler, &ref_cbdata);
>>     }
>
> Yes, but the last test should be
>
>         if (!ret && (filter->kind & FILTER_REFS_DETACHED_HEAD))
>
> unless you have a reason not to follow the usual convention that a
> nonzero return value from fn means that the iteration should be aborted.
>

No, of course, I missed that while typing here.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v13 00/12] port tag.c to use ref-filter APIs
  2015-08-26 20:19                 ` Junio C Hamano
@ 2015-08-27 18:00                   ` Karthik Nayak
  0 siblings, 0 replies; 60+ messages in thread
From: Karthik Nayak @ 2015-08-27 18:00 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Matthieu Moy, Git, Christian Couder

On Thu, Aug 27, 2015 at 1:49 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Wed, Aug 26, 2015 at 8:07 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>>
>>> ...  You can give a new format_ref_array_item()
>>> that does not print but fills a strbuf to this caller, make
>>> show_ref_array_item() a thin wrapper that calls it and prints it
>>> with the final LF for other callers.
>>>
>> You're saying remove show_ref_array_item() (even the wrapper you mentioned
>> above) and just have something like format_ref_array_item() which
>> would output to a strbuf. and let the caller worry about the printing?
>
> Among the current callers, the one in builtin/tag.c that wants to
> trigger show_tag_lines() hack embedded in show_ref_array_item()
> function can stop calling show_ref_array_item() and instead can do
>
>         for (i = 0; i < array.nr; i++) {
>                 struct strbuf out = STRBUF_INIT;
>                 format_ref_array_item(&out, ...);
>                 if (filter->lines) {
>                         ... append tag lines to out ...
>                 }
>                 printf("%s\n", out.buf);
>                 strbuf_reset(&out);
>         }
>
> The current and future callers of show_ref_array_item() that do not
> want to trigger the show_tag_liens() hack embedded in there may
> still want it to print the formatted string including the trailing
> LF, so you can keep show_ref_array_item() as a thin wrapper around
> format_ref_array_item() for them to call, e.g.
>
>         show_ref_array_item(...) {
>                 struct strbuf out = STRBUF_INIT;
>                 format_ref_array_item(&out, ...);
>                 printf("%s\n", out.buf);
>                 strbuf_release(&out);
>         }
>
> But if it has only one caller each, you may not even want to have
> show_ref_array_item(), if you are going to do the "output to strbuf"
> variant.
>
>
>

This is exactly what I did at the moment, I'm also trying to get
%(contents:lines=N)
work. Thanks for explaining though.

-- 
Regards,
Karthik Nayak

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

end of thread, other threads:[~2015-08-27 18:01 UTC | newest]

Thread overview: 60+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-22  3:39 [PATCH v13 00/12] port tag.c to use ref-filter APIs Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 01/12] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 02/12] ref-filter: introduce ref_formatting_state and ref_formatting_stack Karthik Nayak
2015-08-24 21:56   ` Junio C Hamano
2015-08-25 10:26     ` Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 03/12] utf8: add function to align a string into given strbuf Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 04/12] ref-filter: implement an `align` atom Karthik Nayak
2015-08-24 22:13   ` Junio C Hamano
2015-08-24 22:15     ` Junio C Hamano
2015-08-25 13:28       ` Karthik Nayak
2015-08-25  6:47     ` Matthieu Moy
2015-08-25 13:30       ` Karthik Nayak
2015-08-25 17:56         ` Junio C Hamano
2015-08-26  6:41           ` Karthik Nayak
2015-08-25 17:52       ` Junio C Hamano
2015-08-25 13:28     ` Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 05/12] ref-filter: add option to filter out tags, branches and remotes Karthik Nayak
2015-08-24 22:24   ` Junio C Hamano
2015-08-25 13:07     ` Karthik Nayak
2015-08-26 16:10   ` Michael Haggerty
2015-08-27 12:42     ` Karthik Nayak
2015-08-27 15:24       ` Michael Haggerty
2015-08-27 15:35         ` Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 06/12] ref-filter: support printing N lines from tag annotation Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 07/12] ref-filter: add support to sort by version Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 08/12] ref-filter: add option to match literal pattern Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 09/12] tag.c: use 'ref-filter' data structures Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 10/12] tag.c: use 'ref-filter' APIs Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 11/12] tag.c: implement '--format' option Karthik Nayak
2015-08-23 19:56   ` Matthieu Moy
2015-08-24 15:07     ` Karthik Nayak
2015-08-24 15:14       ` Matthieu Moy
2015-08-24 15:21         ` Karthik Nayak
2015-08-22  3:39 ` [PATCH v13 12/12] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak
2015-08-23 20:00 ` [PATCH v13 00/12] port tag.c to use ref-filter APIs Matthieu Moy
2015-08-24 15:09   ` Karthik Nayak
2015-08-24 15:16     ` Matthieu Moy
2015-08-24 15:22       ` Karthik Nayak
2015-08-24 22:34   ` Junio C Hamano
2015-08-24 22:58     ` Junio C Hamano
2015-08-25 13:26       ` Karthik Nayak
2015-08-25 19:16         ` Junio C Hamano
2015-08-25 19:43           ` Junio C Hamano
2015-08-26  5:56           ` Karthik Nayak
2015-08-26 14:37             ` Junio C Hamano
2015-08-26 19:14               ` Karthik Nayak
2015-08-26 20:19                 ` Junio C Hamano
2015-08-27 18:00                   ` Karthik Nayak
2015-08-25 13:23     ` Karthik Nayak
2015-08-24 17:27 ` Junio C Hamano
2015-08-24 18:09   ` Karthik Nayak
2015-08-24 18:53     ` Junio C Hamano
2015-08-24 22:35       ` Junio C Hamano
2015-08-26 10:07         ` Karthik Nayak
2015-08-26 11:54           ` Matthieu Moy
2015-08-26 15:44             ` Junio C Hamano
2015-08-26 15:46               ` Junio C Hamano
2015-08-26 15:48                 ` Matthieu Moy
2015-08-26 19:11                   ` Karthik Nayak
2015-08-25 13:25       ` Karthik Nayak

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.