git@vger.kernel.org mailing list mirror (one of many)
 help / color / mirror / code / Atom feed
* [PATCH v10 00/13] port tag.c to use ref-filter APIs
@ 2015-08-09 14:11 Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 01/13] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
                   ` (13 more replies)
  0 siblings, 14 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

This is part of my GSoC project to unify git tag -l, git branch -l,
git for-each-ref.  This patch series is continued from: Git (next)
https://github.com/git/git/commit/bf5418f49ff0cebc6e5ce04ad1417e1a47c81b61

This series consists of porting tag.c over to using the ref-filter APIs

Version 8 can be found here:
http://thread.gmane.org/gmane.comp.version-control.git/275252

Changes in this version: 
* use fwrite instead of looping over single characters and printing them.
* use strbuf_reset() at places instead of strbuf_release().
* make ref_formatting_state hold a strbuf, rather than a pointer to one.
* change a few function names to denote changes made to their implementations.
* move alignment to utf8, and use an enum for align_type.
* rename some variables to give better meanings.
* combine skip_prefix and starts_with in populate_value().
* fix utf8 checking.
* more small changes in the interdiff.

Thanks to everyone for their suggestions, especially Eric.

Karthik Nayak (13):
  ref-filter: move `struct atom_value` to ref-filter.c
  ref-filter: print output to strbuf for formatting
  ref-filter: introduce ref_formatting_state
  utf8: add function to align a string into given strbuf
  ref-filter: implement an `align` atom
  ref-filter: add option to filter only tags
  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          |  34 +++-
 builtin/for-each-ref.c             |   3 +-
 builtin/tag.c                      | 367 +++++++------------------------------
 ref-filter.c                       | 243 +++++++++++++++++++++---
 ref-filter.h                       |  22 ++-
 refs.c                             |   5 +
 refs.h                             |   1 +
 t/t6302-for-each-ref-filter.sh     |  84 +++++++++
 t/t7004-tag.sh                     |  51 +++++-
 utf8.c                             |  22 +++
 utf8.h                             |  13 ++
 12 files changed, 500 insertions(+), 357 deletions(-)

The interdiff between v9 and v10:

diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 6b6eb93..a795d5f 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -128,12 +128,13 @@ color::
 	are described in `color.branch.*`.
 
 align::
-	Align any string with or without %(atom) before the %(end)
-	atom to the right, left or middle. Followed by
-	`:<type>,<paddinglength>`, where the `<type>` is either left,
-	right or middle and `<paddinglength>` is the total length of
-	the padding to be performed. If the string length is more than
-	the padding length then no padding is performed.
+	Implement an `align` atom which left-, middle-, or
+	right-aligns the content between %(align:..)  and
+	%(end). Followed by `:<position>,<width>`, 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. Currently nested alignment is not supported.
 
 In addition to the above, for commit and tag objects, the header
 field names (`tree`, `parent`, `object`, `type`, and `tag`) can
diff --git a/builtin/tag.c b/builtin/tag.c
index 529b29f..22efbba 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -41,7 +41,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting, con
 		filter->lines = 0;
 
 	if (filter->lines)
-		format = "%(align:left,16)%(refname:short)";
+		format = "%(align:left,16)%(refname:short)%(end)";
 	else if (!format)
 		format = "%(refname:short)";
 
diff --git a/ref-filter.c b/ref-filter.c
index de84dd4..d22bf66 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -60,6 +60,19 @@ static struct {
 	{ "end" },
 };
 
+struct align {
+	align_type position;
+	unsigned int width;
+};
+
+struct atom_value{
+	const char *s;
+	struct align *align;
+	unsigned long ul; /* used for sorting when not FIELD_STR */
+	unsigned int modifier_atom : 1,
+		end : 1;
+};
+
 /*
  * An atom is a valid field atom listed above, possibly prefixed with
  * a "*" to denote deref_tag().
@@ -625,8 +638,9 @@ static void populate_value(struct ref_array_item *ref)
 		const char *name = used_atom[i];
 		struct atom_value *v = &ref->value[i];
 		int deref = 0;
-		const char *refname = NULL;
+		const char *refname;
 		const char *formatp;
+		const char *valp;
 		struct branch *branch = NULL;
 
 		if (*name == '*') {
@@ -692,26 +706,23 @@ static void populate_value(struct ref_array_item *ref)
 			else
 				v->s = " ";
 			continue;
-		} else if (starts_with(name, "align:")) {
-			const char *valp = NULL;
+		} else if (skip_prefix(name, "align:", &valp)) {
 			struct align *align = xmalloc(sizeof(struct align));
 
-			skip_prefix(name, "align:", &valp);
-
 			if (skip_prefix(valp, "left,", &valp))
-				align->align_type = ALIGN_LEFT;
+				align->position = ALIGN_LEFT;
 			else if (skip_prefix(valp, "right,", &valp))
-				align->align_type = ALIGN_RIGHT;
+				align->position = ALIGN_RIGHT;
 			else if (skip_prefix(valp, "middle,", &valp))
-				align->align_type = ALIGN_MIDDLE;
+				align->position = ALIGN_MIDDLE;
 			else
-				die(_("align: improper format"));
-			if (strtoul_ui(valp, 10, &align->align_value))
-				die(_("align: positive value expected"));
+				die(_("improper format entered align:%s"), valp);
+			if (strtoul_ui(valp, 10, &align->width))
+				die(_("positive width expected align:%s"), valp);
 			v->align = align;
 			v->modifier_atom = 1;
 			continue;
-		} else if (starts_with(name, "end")) {
+		} else if (!strcmp(name, "end")) {
 			v->end = 1;
 			v->modifier_atom = 1;
 			continue;
@@ -1253,23 +1264,31 @@ 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, struct ref_formatting_state *state)
+
+struct ref_formatting_state {
+	struct strbuf output;
+	struct align *align;
+	int quote_style;
+	unsigned int end : 1;
+};
+
+static void format_quote_value(struct atom_value *v, struct ref_formatting_state *state)
 {
 	switch (state->quote_style) {
 	case QUOTE_NONE:
-		strbuf_addstr(state->output, v->s);
+		strbuf_addstr(&state->output, v->s);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(state->output, v->s);
+		sq_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(state->output, v->s);
+		perl_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(state->output, v->s);
+		python_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(state->output, v->s);
+		tcl_quote_buf(&state->output, v->s);
 		break;
 	}
 }
@@ -1292,7 +1311,7 @@ static int hex2(const char *cp)
 		return -1;
 }
 
-static void emit(const char *cp, const char *ep, struct ref_formatting_state *state)
+static void append_non_atom(const char *cp, const char *ep, struct ref_formatting_state *state)
 {
 	while (*cp && (!ep || cp < ep)) {
 		if (*cp == '%') {
@@ -1301,18 +1320,18 @@ static void emit(const char *cp, const char *ep, struct ref_formatting_state *st
 			else {
 				int ch = hex2(cp + 1);
 				if (0 <= ch) {
-					strbuf_addch(state->output, ch);
+					strbuf_addch(&state->output, ch);
 					cp += 3;
 					continue;
 				}
 			}
 		}
-		strbuf_addch(state->output, *cp);
+		strbuf_addch(&state->output, *cp);
 		cp++;
 	}
 }
 
-static void process_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
+static void set_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
 {
 	if (atomv->align) {
 		state->align = atomv->align;
@@ -1322,42 +1341,25 @@ static void process_formatting_state(struct atom_value *atomv, struct ref_format
 		state->end = 1;
 }
 
-static void apply_formatting_state(struct ref_formatting_state *state, struct strbuf *final)
+static int align_ref_strbuf(struct ref_formatting_state *state, struct strbuf *final)
 {
 	if (state->align && state->end) {
-		struct strbuf *value = state->output;
-		int len = 0, buf_len = value->len;
 		struct align *align = state->align;
-
-		if (!value->buf)
-			return;
-		if (!is_utf8(value->buf)) {
-			len = value->len - utf8_strwidth(value->buf);
-			buf_len -= len;
-		}
-
-		if (align->align_value < buf_len) {
-			state->align = NULL;
-			strbuf_addbuf(final, value);
-			strbuf_release(value);
-			return;
-		}
-
-		if (align->align_type == ALIGN_LEFT)
-			strbuf_addf(final, "%-*s", len + align->align_value, value->buf);
-		else if (align->align_type == ALIGN_MIDDLE) {
-			int right = (align->align_value - buf_len)/2;
-			strbuf_addf(final, "%*s%-*s", align->align_value - right + len,
-				    value->buf, right, "");
-		} else if (align->align_type == ALIGN_RIGHT)
-			strbuf_addf(final, "%*s", align->align_value, value->buf);
-		strbuf_release(value);
+		strbuf_utf8_align(final, align->position, align->width, state->output.buf);
+		strbuf_reset(&state->output);
 		state->align = NULL;
-		return;
+		return 1;
 	} else if (state->align)
+		return 1;
+	return 0;
+}
+
+static void perform_state_formatting(struct ref_formatting_state *state, struct strbuf *final)
+{
+	if (align_ref_strbuf(state, final))
 		return;
-	strbuf_addbuf(final, state->output);
-	strbuf_release(state->output);
+	strbuf_addbuf(final, &state->output);
+	strbuf_reset(&state->output);
 }
 
 /*
@@ -1407,41 +1409,38 @@ 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 value = STRBUF_INIT;
 	struct strbuf final_buf = STRBUF_INIT;
 	struct ref_formatting_state state;
-	int i;
 
-	memset(&state, 0, sizeof(state));
 	state.quote_style = quote_style;
-	state.output = &value;
+	strbuf_init(&state.output, 0);
+	state.align = NULL;
+	state.end = 0;
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
-		struct atom_value *atomv = NULL;
+		struct atom_value *atomv;
 
 		ep = strchr(sp, ')');
 		if (cp < sp) {
-			emit(cp, sp, &state);
-			apply_formatting_state(&state, &final_buf);
+			append_non_atom(cp, sp, &state);
+			perform_state_formatting(&state, &final_buf);
 		}
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
 		if (atomv->modifier_atom)
-			process_formatting_state(atomv, &state);
+			set_formatting_state(atomv, &state);
 		else {
-			print_value(atomv, &state);
-			apply_formatting_state(&state, &final_buf);
-		}
-		if (atomv->end) {
-			print_value(atomv, &state);
-			apply_formatting_state(&state, &final_buf);
+			format_quote_value(atomv, &state);
+			perform_state_formatting(&state, &final_buf);
 		}
+		if (atomv->end)
+			perform_state_formatting(&state, &final_buf);
 	}
 	if (state.align)
 		die(_("format: align used without an `end` atom"));
 	if (*cp) {
 		sp = cp + strlen(cp);
-		emit(cp, sp, &state);
-		apply_formatting_state(&state, &final_buf);
+		append_non_atom(cp, sp, &state);
+		perform_state_formatting(&state, &final_buf);
 	}
 	if (need_color_reset_at_eol) {
 		struct atom_value resetv;
@@ -1450,13 +1449,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;
-		print_value(&resetv, &state);
-		apply_formatting_state(&state, &final_buf);
+		format_quote_value(&resetv, &state);
+		perform_state_formatting(&state, &final_buf);
 	}
 
-	for (i = 0; i < final_buf.len; i++)
-		printf("%c", final_buf.buf[i]);
+	fwrite(final_buf.buf, 1, final_buf.len, stdout);
 	strbuf_release(&final_buf);
+	strbuf_release(&state.output);
 
 	if (lines > 0) {
 		struct object_id oid;
diff --git a/ref-filter.h b/ref-filter.h
index 5be3e35..75bf989 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "commit.h"
 #include "parse-options.h"
+#include "utf8.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -17,29 +18,7 @@
 #define FILTER_REFS_ALL 0x2
 #define FILTER_REFS_TAGS 0x4
 
-#define ALIGN_LEFT 0x01
-#define ALIGN_RIGHT 0x02
-#define ALIGN_MIDDLE 0x04
-
-struct ref_formatting_state {
-	int quote_style;
-	struct strbuf *output;
-	struct align *align;
-	unsigned int end : 1;
-};
-
-struct align {
-	unsigned int align_type,
-		align_value;
-};
-
-struct atom_value {
-	const char *s;
-	struct align *align;
-	unsigned long ul; /* used for sorting when not FIELD_STR */
-	unsigned int modifier_atom : 1,
-		end : 1;
-};
+struct atom_value;
 
 struct ref_sorting {
 	struct ref_sorting *next;
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 272a7e4..ce455d9 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -103,7 +103,7 @@ test_expect_success 'middle alignment' '
 	|  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/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
diff --git a/utf8.c b/utf8.c
index 28e6d76..bcbc621 100644
--- a/utf8.c
+++ b/utf8.c
@@ -644,3 +644,25 @@ 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 display_len = utf8_strnwidth(s, strlen(s), 0);
+	int utf8_compenstation = strlen(s) - display_len;
+
+	if (!strlen(s))
+		return;
+	if (display_len >= width) {
+		strbuf_addstr(buf, s);
+		return;
+	}
+
+	if (position == ALIGN_LEFT)
+		strbuf_addf(buf, "%-*s", width + utf8_compenstation, s);
+	else if (position == ALIGN_MIDDLE) {
+		int left = (width - display_len)/2;
+		strbuf_addf(buf, "%*s%-*s", left, "", width - left + ut8_compenstation, s);
+	} else if (position == ALIGN_RIGHT)
+		strbuf_addf(buf, "%*s", width + utf8_compenstation, s);
+}
diff --git a/utf8.h b/utf8.h
index 5a9e94b..db8ca63 100644
--- a/utf8.h
+++ b/utf8.h
@@ -55,4 +55,17 @@ 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 type
+ * and width.
+ */
+void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
+		       const char *s);
+
 #endif

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

* [PATCH v10 01/13] ref-filter: move `struct atom_value` to ref-filter.c
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 02/13] ref-filter: print output to strbuf for formatting Karthik Nayak
                   ` (12 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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..07949f4 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] 40+ messages in thread

* [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 01/13] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-11 17:56   ` Junio C Hamano
  2015-08-11 18:00   ` Junio C Hamano
  2015-08-09 14:11 ` [PATCH v10 03/13] ref-filter: introduce ref_formatting_state Karthik Nayak
                   ` (11 subsequent siblings)
  13 siblings, 2 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak,
	Karthik Nayak

Introduce a strbuf `output` which will act as a substitute rather than
printing directly to stdout. This will be used for formatting
eventually.

Rename some functions to reflect the changes made:
print_value() -> format_quote_value()
emit()        -> append_non_atom()

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 | 34 ++++++++++++++++------------------
 1 file changed, 16 insertions(+), 18 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 07949f4..95f027b 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1195,30 +1195,25 @@ 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 format_quote_value(struct atom_value *v, int quote_style, struct strbuf *output)
 {
-	struct strbuf sb = STRBUF_INIT;
 	switch (quote_style) {
 	case QUOTE_NONE:
-		fputs(v->s, stdout);
+		strbuf_addstr(output, v->s);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(&sb, v->s);
+		sq_quote_buf(output, v->s);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(&sb, v->s);
+		perl_quote_buf(output, v->s);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(&sb, v->s);
+		python_quote_buf(output, v->s);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(&sb, v->s);
+		tcl_quote_buf(output, v->s);
 		break;
 	}
-	if (quote_style != QUOTE_NONE) {
-		fputs(sb.buf, stdout);
-		strbuf_release(&sb);
-	}
 }
 
 static int hex1(char ch)
@@ -1239,7 +1234,7 @@ static int hex2(const char *cp)
 		return -1;
 }
 
-static void emit(const char *cp, const char *ep)
+static void append_non_atom(const char *cp, const char *ep, struct strbuf *output)
 {
 	while (*cp && (!ep || cp < ep)) {
 		if (*cp == '%') {
@@ -1248,13 +1243,13 @@ static void emit(const char *cp, const char *ep)
 			else {
 				int ch = hex2(cp + 1);
 				if (0 <= ch) {
-					putchar(ch);
+					strbuf_addch(output, ch);
 					cp += 3;
 					continue;
 				}
 			}
 		}
-		putchar(*cp);
+		strbuf_addch(output, *cp);
 		cp++;
 	}
 }
@@ -1262,19 +1257,20 @@ 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 output = STRBUF_INIT;
 
 	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_non_atom(cp, sp, &output);
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
-		print_value(atomv, quote_style);
+		format_quote_value(atomv, quote_style, &output);
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
-		emit(cp, sp);
+		append_non_atom(cp, sp, &output);
 	}
 	if (need_color_reset_at_eol) {
 		struct atom_value resetv;
@@ -1283,9 +1279,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);
+		format_quote_value(&resetv, quote_style, &output);
 	}
+	fwrite(output.buf, 1, output.len, stdout);
 	putchar('\n');
+	strbuf_release(&output);
 }
 
 /*  If no sorting option is given, use refname to sort as default */
-- 
2.5.0

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

* [PATCH v10 03/13] ref-filter: introduce ref_formatting_state
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 01/13] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 02/13] ref-filter: print output to strbuf for formatting Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-11 18:13   ` Junio C Hamano
  2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
                   ` (10 subsequent siblings)
  13 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak,
	Karthik Nayak

Introduce a ref_formatting_state which will eventually hold the values
of modifier atoms. Implement this within ref-filter.

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 | 65 +++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 18 deletions(-)

diff --git a/ref-filter.c b/ref-filter.c
index 95f027b..e2df209 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1195,23 +1195,29 @@ 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 format_quote_value(struct atom_value *v, int quote_style, struct strbuf *output)
+
+struct ref_formatting_state {
+	struct strbuf output;
+	int quote_style;
+};
+
+static void format_quote_value(struct atom_value *v, struct ref_formatting_state *state)
 {
-	switch (quote_style) {
+	switch (state->quote_style) {
 	case QUOTE_NONE:
-		strbuf_addstr(output, v->s);
+		strbuf_addstr(&state->output, v->s);
 		break;
 	case QUOTE_SHELL:
-		sq_quote_buf(output, v->s);
+		sq_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_PERL:
-		perl_quote_buf(output, v->s);
+		perl_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_PYTHON:
-		python_quote_buf(output, v->s);
+		python_quote_buf(&state->output, v->s);
 		break;
 	case QUOTE_TCL:
-		tcl_quote_buf(output, v->s);
+		tcl_quote_buf(&state->output, v->s);
 		break;
 	}
 }
@@ -1234,7 +1240,7 @@ static int hex2(const char *cp)
 		return -1;
 }
 
-static void append_non_atom(const char *cp, const char *ep, struct strbuf *output)
+static void append_non_atom(const char *cp, const char *ep, struct ref_formatting_state *state)
 {
 	while (*cp && (!ep || cp < ep)) {
 		if (*cp == '%') {
@@ -1243,34 +1249,55 @@ static void append_non_atom(const char *cp, const char *ep, struct strbuf *outpu
 			else {
 				int ch = hex2(cp + 1);
 				if (0 <= ch) {
-					strbuf_addch(output, ch);
+					strbuf_addch(&state->output, ch);
 					cp += 3;
 					continue;
 				}
 			}
 		}
-		strbuf_addch(output, *cp);
+		strbuf_addch(&state->output, *cp);
 		cp++;
 	}
 }
 
+static void set_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
+{
+	/* Based on the atomv values, the formatting state is set */
+}
+
+static void perform_state_formatting(struct ref_formatting_state *state, struct strbuf *final)
+{
+	/* More formatting options to be eventually added */
+	strbuf_addbuf(final, &state->output);
+	strbuf_reset(&state->output);
+}
+
 void show_ref_array_item(struct ref_array_item *info, const char *format, int quote_style)
 {
 	const char *cp, *sp, *ep;
-	struct strbuf output = STRBUF_INIT;
+	struct strbuf final_buf = STRBUF_INIT;
+	struct ref_formatting_state state;
+
+	state.quote_style = quote_style;
+	strbuf_init(&state.output, 0);
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 		struct atom_value *atomv;
 
 		ep = strchr(sp, ')');
-		if (cp < sp)
-			append_non_atom(cp, sp, &output);
+		if (cp < sp) {
+			append_non_atom(cp, sp, &state);
+			perform_state_formatting(&state, &final_buf);
+		}
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
-		format_quote_value(atomv, quote_style, &output);
+		set_formatting_state(atomv, &state);
+		format_quote_value(atomv, &state);
+		perform_state_formatting(&state, &final_buf);
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
-		append_non_atom(cp, sp, &output);
+		append_non_atom(cp, sp, &state);
+		perform_state_formatting(&state, &final_buf);
 	}
 	if (need_color_reset_at_eol) {
 		struct atom_value resetv;
@@ -1279,11 +1306,13 @@ 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;
-		format_quote_value(&resetv, quote_style, &output);
+		format_quote_value(&resetv, &state);
+		perform_state_formatting(&state, &final_buf);
 	}
-	fwrite(output.buf, 1, output.len, stdout);
+	fwrite(final_buf.buf, 1, final_buf.len, stdout);
 	putchar('\n');
-	strbuf_release(&output);
+	strbuf_release(&final_buf);
+	strbuf_release(&state.output);
 }
 
 /*  If no sorting option is given, use refname to sort as default */
-- 
2.5.0

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

* [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (2 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 03/13] ref-filter: introduce ref_formatting_state Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-11 18:22   ` Junio C Hamano
                     ` (2 more replies)
  2015-08-09 14:11 ` [PATCH v10 05/13] ref-filter: implement an `align` atom Karthik Nayak
                   ` (9 subsequent siblings)
  13 siblings, 3 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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 | 22 ++++++++++++++++++++++
 utf8.h | 13 +++++++++++++
 2 files changed, 35 insertions(+)

diff --git a/utf8.c b/utf8.c
index 28e6d76..65e55cc 100644
--- a/utf8.c
+++ b/utf8.c
@@ -644,3 +644,25 @@ 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 display_len = utf8_strnwidth(s, strlen(s), 0);
+	int utf8_compenstation = strlen(s) - display_len;
+
+	if (!strlen(s))
+		return;
+	if (display_len >= width) {
+		strbuf_addstr(buf, s);
+		return;
+	}
+
+	if (position == ALIGN_LEFT)
+		strbuf_addf(buf, "%-*s", width + utf8_compenstation, s);
+	else if (position == ALIGN_MIDDLE) {
+		int left = (width - display_len)/2;
+		strbuf_addf(buf, "%*s%-*s", left, "", width - left + utf8_compenstation, s);
+	} else if (position == ALIGN_RIGHT)
+		strbuf_addf(buf, "%*s", width + utf8_compenstation, s);
+}
diff --git a/utf8.h b/utf8.h
index 5a9e94b..db8ca63 100644
--- a/utf8.h
+++ b/utf8.h
@@ -55,4 +55,17 @@ 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 type
+ * and width.
+ */
+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] 40+ messages in thread

* [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (3 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-11 18:52   ` Junio C Hamano
  2015-08-13 18:26   ` Eric Sunshine
  2015-08-09 14:11 ` [PATCH v10 06/13] ref-filter: add option to filter only tags Karthik Nayak
                   ` (8 subsequent siblings)
  13 siblings, 2 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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 `:<position>,<width>`, 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)".

This is done by calling the strbuf_utf8_align() function in utf8.c and
currently does not support nested alignment.

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                       | 72 +++++++++++++++++++++++++++++++++++---
 ref-filter.h                       |  1 +
 t/t6302-for-each-ref-filter.sh     | 48 +++++++++++++++++++++++++
 4 files changed, 125 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index e49d578..d949812 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::
+	Implement an `align` atom which left-, middle-, or
+	right-aligns the content between %(align:..)  and
+	%(end). Followed by `:<position>,<width>`, 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. Currently nested alignment is not supported.
+
 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 e2df209..57e988c 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,11 +54,21 @@ static struct {
 	{ "flag" },
 	{ "HEAD" },
 	{ "color" },
+	{ "align" },
+	{ "end" },
+};
+
+struct align {
+	align_type position;
+	unsigned int width;
 };
 
 struct atom_value{
 	const char *s;
+	struct align *align;
 	unsigned long ul; /* used for sorting when not FIELD_STR */
+	unsigned int modifier_atom : 1,
+		end : 1;
 };
 
 /*
@@ -627,6 +638,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 == '*') {
@@ -692,6 +704,26 @@ 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));
+
+			if (skip_prefix(valp, "left,", &valp))
+				align->position = ALIGN_LEFT;
+			else if (skip_prefix(valp, "right,", &valp))
+				align->position = ALIGN_RIGHT;
+			else if (skip_prefix(valp, "middle,", &valp))
+				align->position = ALIGN_MIDDLE;
+			else
+				die(_("improper format entered align:%s"), valp);
+			if (strtoul_ui(valp, 10, &align->width))
+				die(_("positive width expected align:%s"), valp);
+			v->align = align;
+			v->modifier_atom = 1;
+			continue;
+		} else if (!strcmp(name, "end")) {
+			v->end = 1;
+			v->modifier_atom = 1;
+			continue;
 		} else
 			continue;
 
@@ -1198,7 +1230,9 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
 
 struct ref_formatting_state {
 	struct strbuf output;
+	struct align *align;
 	int quote_style;
+	unsigned int end : 1;
 };
 
 static void format_quote_value(struct atom_value *v, struct ref_formatting_state *state)
@@ -1262,12 +1296,31 @@ static void append_non_atom(const char *cp, const char *ep, struct ref_formattin
 
 static void set_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
 {
-	/* Based on the atomv values, the formatting state is set */
+	if (atomv->align) {
+		state->align = atomv->align;
+		atomv->align = NULL;
+	}
+	if (atomv->end)
+		state->end = 1;
+}
+
+static int align_ref_strbuf(struct ref_formatting_state *state, struct strbuf *final)
+{
+	if (state->align && state->end) {
+		struct align *align = state->align;
+		strbuf_utf8_align(final, align->position, align->width, state->output.buf);
+		strbuf_reset(&state->output);
+		state->align = NULL;
+		return 1;
+	} else if (state->align)
+		return 1;
+	return 0;
 }
 
 static void perform_state_formatting(struct ref_formatting_state *state, struct strbuf *final)
 {
-	/* More formatting options to be eventually added */
+	if (align_ref_strbuf(state, final))
+		return;
 	strbuf_addbuf(final, &state->output);
 	strbuf_reset(&state->output);
 }
@@ -1280,6 +1333,8 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 
 	state.quote_style = quote_style;
 	strbuf_init(&state.output, 0);
+	state.align = NULL;
+	state.end = 0;
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 		struct atom_value *atomv;
@@ -1290,10 +1345,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 			perform_state_formatting(&state, &final_buf);
 		}
 		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
-		set_formatting_state(atomv, &state);
-		format_quote_value(atomv, &state);
-		perform_state_formatting(&state, &final_buf);
+		if (atomv->modifier_atom)
+			set_formatting_state(atomv, &state);
+		else {
+			format_quote_value(atomv, &state);
+			perform_state_formatting(&state, &final_buf);
+		}
+		if (atomv->end)
+			perform_state_formatting(&state, &final_buf);
 	}
+	if (state.align)
+		die(_("format: align used without an `end` atom"));
 	if (*cp) {
 		sp = cp + strlen(cp);
 		append_non_atom(cp, sp, &state);
diff --git a/ref-filter.h b/ref-filter.h
index 45026d0..144a633 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -5,6 +5,7 @@
 #include "refs.h"
 #include "commit.h"
 #include "parse-options.h"
+#include "utf8.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
diff --git a/t/t6302-for-each-ref-filter.sh b/t/t6302-for-each-ref-filter.sh
index 505a360..f6b921b 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -81,4 +81,52 @@ 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:left,30)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:middle,30)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:right,30)refname is %(refname)%(end)|%(refname)" >actual &&
+	test_cmp expect actual
+'
+
 test_done
-- 
2.5.0

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

* [PATCH v10 06/13] ref-filter: add option to filter only tags
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (4 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 05/13] ref-filter: implement an `align` atom Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 07/13] ref-filter: support printing N lines from tag annotation Karthik Nayak
                   ` (7 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

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

Add a functions called 'for_each_tag_ref_fullpath()' to refs.{c,h}
which iterates through each tag ref without trimming the path.

Add an option in 'filter_refs()' to use 'for_each_tag_ref_fullpath()'
and filter refs. This type checking is done by adding a
'FILTER_REFS_TAGS' in 'ref-filter.h'

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 | 2 ++
 ref-filter.h | 1 +
 refs.c       | 5 +++++
 refs.h       | 1 +
 4 files changed, 9 insertions(+)

diff --git a/ref-filter.c b/ref-filter.c
index 57e988c..fb5d74e 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1172,6 +1172,8 @@ int filter_refs(struct ref_array *array, struct ref_filter *filter, unsigned int
 		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 & FILTER_REFS_TAGS)
+		ret = for_each_tag_ref_fullpath(ref_filter_handler, &ref_cbdata);
 	else if (type)
 		die("filter_refs: invalid type");
 
diff --git a/ref-filter.h b/ref-filter.h
index 144a633..1886baa 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -16,6 +16,7 @@
 
 #define FILTER_REFS_INCLUDE_BROKEN 0x1
 #define FILTER_REFS_ALL 0x2
+#define FILTER_REFS_TAGS 0x4
 
 struct atom_value;
 
diff --git a/refs.c b/refs.c
index 2db2975..0103a88 100644
--- a/refs.c
+++ b/refs.c
@@ -2114,6 +2114,11 @@ int for_each_tag_ref(each_ref_fn fn, void *cb_data)
 	return for_each_ref_in("refs/tags/", fn, cb_data);
 }
 
+int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data)
+{
+	return do_for_each_ref(&ref_cache, "refs/tags/", fn, 0, 0, cb_data);
+}
+
 int for_each_tag_ref_submodule(const char *submodule, each_ref_fn fn, void *cb_data)
 {
 	return for_each_ref_in_submodule(submodule, "refs/tags/", fn, cb_data);
diff --git a/refs.h b/refs.h
index 6a3fa6d..0956255 100644
--- a/refs.h
+++ b/refs.h
@@ -174,6 +174,7 @@ extern int head_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_ref(each_ref_fn fn, void *cb_data);
 extern int for_each_ref_in(const char *prefix, each_ref_fn fn, void *cb_data);
 extern int for_each_tag_ref(each_ref_fn fn, void *cb_data);
+extern int for_each_tag_ref_fullpath(each_ref_fn fn, void *cb_data);
 extern int for_each_branch_ref(each_ref_fn fn, void *cb_data);
 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);
-- 
2.5.0

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

* [PATCH v10 07/13] ref-filter: support printing N lines from tag annotation
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (5 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 06/13] ref-filter: add option to filter only tags Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 08/13] ref-filter: add support to sort by version Karthik Nayak
                   ` (6 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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           | 55 ++++++++++++++++++++++++++++++++++++++++++++++++--
 ref-filter.h           |  9 +++++++--
 4 files changed, 65 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 fb5d74e..2042962 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -1327,7 +1327,51 @@ static void perform_state_formatting(struct ref_formatting_state *state, struct
 	strbuf_reset(&state->output);
 }
 
-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 = STRBUF_INIT;
@@ -1373,10 +1417,17 @@ void show_ref_array_item(struct ref_array_item *info, const char *format, int qu
 		format_quote_value(&resetv, &state);
 		perform_state_formatting(&state, &final_buf);
 	}
+
 	fwrite(final_buf.buf, 1, final_buf.len, stdout);
-	putchar('\n');
 	strbuf_release(&final_buf);
 	strbuf_release(&state.output);
+
+	if (lines > 0) {
+		struct object_id oid;
+		hashcpy(oid.hash, info->objectname);
+		show_tag_lines(&oid, lines);
+	}
+	putchar('\n');
 }
 
 /*  If no sorting option is given, use refname to sort as default */
diff --git a/ref-filter.h b/ref-filter.h
index 1886baa..72e21bb 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -53,6 +53,7 @@ struct ref_filter {
 	struct commit *merge_commit;
 
 	unsigned int with_commit_tag_algo : 1;
+	unsigned int lines;
 };
 
 struct ref_filter_cbdata {
@@ -84,8 +85,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] 40+ messages in thread

* [PATCH v10 08/13] ref-filter: add support to sort by version
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (6 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 07/13] ref-filter: support printing N lines from tag annotation Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 09/13] ref-filter: add option to match literal pattern Karthik Nayak
                   ` (5 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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 eventaully 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 d949812..a795d5f 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 2042962..abb17d7 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;
 
@@ -1192,19 +1194,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;
 }
 
@@ -1459,6 +1461,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 72e21bb..e824754 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -23,7 +23,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 f6b921b..ce455d9 100755
--- a/t/t6302-for-each-ref-filter.sh
+++ b/t/t6302-for-each-ref-filter.sh
@@ -129,4 +129,40 @@ test_expect_success 'right alignment' '
 	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] 40+ messages in thread

* [PATCH v10 09/13] ref-filter: add option to match literal pattern
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (7 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 08/13] ref-filter: add support to sort by version Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:11 ` [PATCH v10 10/13] tag.c: use 'ref-filter' data structures Karthik Nayak
                   ` (4 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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           | 39 ++++++++++++++++++++++++++++++++++++---
 ref-filter.h           |  3 ++-
 3 files changed, 39 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 abb17d7..d22bf66 100644
--- a/ref-filter.c
+++ b/ref-filter.c
@@ -967,9 +967,32 @@ 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));
+
+	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)
 {
@@ -990,6 +1013,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
@@ -1058,7 +1091,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid,
 		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 e824754..75bf989 100644
--- a/ref-filter.h
+++ b/ref-filter.h
@@ -53,7 +53,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 lines;
 };
 
-- 
2.5.0

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

* [PATCH v10 10/13] tag.c: use 'ref-filter' data structures
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (8 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 09/13] ref-filter: add option to match literal pattern Karthik Nayak
@ 2015-08-09 14:11 ` Karthik Nayak
  2015-08-09 14:17 ` Karthik Nayak
                   ` (3 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:11 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] 40+ messages in thread

* [PATCH v10 10/13] tag.c: use 'ref-filter' data structures
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (9 preceding siblings ...)
  2015-08-09 14:11 ` [PATCH v10 10/13] tag.c: use 'ref-filter' data structures Karthik Nayak
@ 2015-08-09 14:17 ` Karthik Nayak
  2015-08-09 14:32 ` [PATCH v10 11/13] tag.c: use 'ref-filter' APIs Karthik Nayak
                   ` (2 subsequent siblings)
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:17 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] 40+ messages in thread

* [PATCH v10 11/13] tag.c: use 'ref-filter' APIs
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (10 preceding siblings ...)
  2015-08-09 14:17 ` Karthik Nayak
@ 2015-08-09 14:32 ` Karthik Nayak
  2015-08-09 14:32 ` [PATCH v10 12/13] tag.c: implement '--format' option Karthik Nayak
  2015-08-09 14:32 ` [PATCH v10 13/13] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:32 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..e6a9a56 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:left,16)%(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..1f066aa 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*" >actual
 '
 
 test_expect_success 'version sort with prerelease reordering' '
-- 
2.5.0

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

* [PATCH v10 12/13] tag.c: implement '--format' option
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (11 preceding siblings ...)
  2015-08-09 14:32 ` [PATCH v10 11/13] tag.c: use 'ref-filter' APIs Karthik Nayak
@ 2015-08-09 14:32 ` Karthik Nayak
  2015-08-09 14:32 ` [PATCH v10 13/13] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:32 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

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

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 | 15 ++++++++++++++-
 builtin/tag.c             | 11 +++++++----
 t/t7004-tag.sh            | 16 ++++++++++++++++
 3 files changed, 37 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 3ac4a96..75703c5 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,18 @@ 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.  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`.
+
 
 CONFIGURATION
 -------------
diff --git a/builtin/tag.c b/builtin/tag.c
index e6a9a56..affb460 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -30,10 +30,9 @@ static const char * const git_tag_usage[] = {
 
 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));
@@ -43,7 +42,7 @@ static int list_tags(struct ref_filter *filter, struct ref_sorting *sorting)
 
 	if (filter->lines)
 		format = "%(align:left,16)%(refname:short)%(end)";
-	else
+	else if (!format)
 		format = "%(refname:short)";
 
 	verify_ref_format(format);
@@ -327,6 +326,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 +359,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()
 	};
 
@@ -398,8 +399,10 @@ 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);
+		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 1f066aa..1809011 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1519,4 +1519,20 @@ 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
+	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] 40+ messages in thread

* [PATCH v10 13/13] tag.c: implement '--merged' and '--no-merged' options
  2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
                   ` (12 preceding siblings ...)
  2015-08-09 14:32 ` [PATCH v10 12/13] tag.c: implement '--format' option Karthik Nayak
@ 2015-08-09 14:32 ` Karthik Nayak
  13 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-09 14:32 UTC (permalink / raw)
  To: git; +Cc: christian.couder, Matthieu.Moy, gitster, Karthik Nayak

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

Using 'ref-filter' APIs 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 75703c5..c2785d9 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
@@ -171,6 +171,11 @@ This option is only applicable when listing tags without annotation lines.
 	`%0a` to `\n` (LF).  The fields are same as those in `git
 	for-each-ref`.
 
+--[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
 -------------
diff --git a/builtin/tag.c b/builtin/tag.c
index affb460..22efbba 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[<pattern>...]"),
+		"\n\t\t[--[no-]merged [<commit>]] [<pattern>...]"),
 	N_("git tag -v <tagname>..."),
 	NULL
 };
@@ -353,6 +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")),
+		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 1809011..5b73539 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1535,4 +1535,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] 40+ messages in thread

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-09 14:11 ` [PATCH v10 02/13] ref-filter: print output to strbuf for formatting Karthik Nayak
@ 2015-08-11 17:56   ` Junio C Hamano
  2015-08-12 13:22     ` Karthik Nayak
  2015-08-11 18:00   ` Junio C Hamano
  1 sibling, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-11 17:56 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

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

> -static void print_value(struct atom_value *v, int quote_style)
> +static void format_quote_value(struct atom_value *v, int quote_style, struct strbuf *output)
>  {

Hmph...

> -static void emit(const char *cp, const char *ep)
> +static void append_non_atom(const char *cp, const char *ep, struct strbuf *output)

I was tempted to suggest s/non_atom/literal/, but this would do for now...

> @@ -1262,19 +1257,20 @@ 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 output = STRBUF_INIT;
>  
>  	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_non_atom(cp, sp, &output);
>  		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
> -		print_value(atomv, quote_style);
> +		format_quote_value(atomv, quote_style, &output);

If the one to add a literal string (with %hex escaping) is called "append_",
then this should be called append_quoted_atom() or something, no?

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

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-09 14:11 ` [PATCH v10 02/13] ref-filter: print output to strbuf for formatting Karthik Nayak
  2015-08-11 17:56   ` Junio C Hamano
@ 2015-08-11 18:00   ` Junio C Hamano
  2015-08-12 13:22     ` Karthik Nayak
  1 sibling, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-11 18:00 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

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

> @@ -1283,9 +1279,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);
> +		format_quote_value(&resetv, quote_style, &output);

Mental note: I _think_ the logic to scan the string and set
need_color_reset_at_eol that happens at the beginning can be removed
once the code fully utilizes formatting-state information.  A
coloring atom would leave a bit in the formatting state to say that
the line color has been changed to something other than reset, and
then this "at the end of line" code can decide if that is the case
and add a "reset" thing here (i.e. the code inside the "if
(need_color_reset_at_eol)" block shown here does not need to change,
but the "if" condition would).

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

* Re: [PATCH v10 03/13] ref-filter: introduce ref_formatting_state
  2015-08-09 14:11 ` [PATCH v10 03/13] ref-filter: introduce ref_formatting_state Karthik Nayak
@ 2015-08-11 18:13   ` Junio C Hamano
  2015-08-12 13:26     ` Karthik Nayak
  0 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-11 18:13 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

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

>  		get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
> -		format_quote_value(atomv, quote_style, &output);
> +		set_formatting_state(atomv, &state);
> +		format_quote_value(atomv, &state);
> +		perform_state_formatting(&state, &final_buf);
>  	}
>  	if (*cp) {
>  		sp = cp + strlen(cp);
> -		append_non_atom(cp, sp, &output);
> +		append_non_atom(cp, sp, &state);
> +		perform_state_formatting(&state, &final_buf);
>  	}

With the two helpers being very sketchy at this stage, it is very
hard to judge if they make sense.  At the conceptual level, I can
see that set-formatting-state is to allow an atom to affect the
state before the value of the atom is emitted into the buffer.
I cannot tell what perform-state-formatting is meant to do from
these call sites.

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
@ 2015-08-11 18:22   ` Junio C Hamano
  2015-08-12 13:41     ` Karthik Nayak
  2015-08-13 19:08   ` Eric Sunshine
  2015-08-15  8:27   ` Duy Nguyen
  2 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-11 18:22 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

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

> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
> +		       const char *s)
> +{
> +	int display_len = utf8_strnwidth(s, strlen(s), 0);
> +	int utf8_compenstation = strlen(s) - display_len;

compensation, perhaps?  But notice you are running two strlen and
then also a call to utf8-strnwidth here already, and then

> +	if (!strlen(s))
> +		return;

you return here without doing anything.

Worse yet, this logic looks very strange.  If you give it a width of
8 because you want to align like-item on each record at that column,
a record with 1-display-column item will be shown in 8-display-column
with 7 SP padding (either at the beginning, or at the end, or on
both ends to center).  If it happens to be an empty string, the entire
8-display-column disappears.

Is that really what you meant to do?  The logic does not make much
sense to me, even though the code may implement that logic correctly
and clearly.

> +	if (display_len >= width) {
> +		strbuf_addstr(buf, s);
> +		return;
> +	}

Mental note: this function values the information more than
presentation; it refuses to truncate (to lose information) and
instead accepts jaggy output.  OK.

> +	if (position == ALIGN_LEFT)
> +		strbuf_addf(buf, "%-*s", width + utf8_compenstation, s);
> +	else if (position == ALIGN_MIDDLE) {
> +		int left = (width - display_len)/2;
> +		strbuf_addf(buf, "%*s%-*s", left, "", width - left + utf8_compenstation, s);
> +	} else if (position == ALIGN_RIGHT)
> +		strbuf_addf(buf, "%*s", width + utf8_compenstation, s);
> +}

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-09 14:11 ` [PATCH v10 05/13] ref-filter: implement an `align` atom Karthik Nayak
@ 2015-08-11 18:52   ` Junio C Hamano
  2015-08-12 16:24     ` Karthik Nayak
  2015-08-13 18:26   ` Eric Sunshine
  1 sibling, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-11 18:52 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: git, christian.couder, Matthieu.Moy

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

>  struct atom_value{

Obviously not a problem with this step, but you need a SP before the
open brace.

> @@ -692,6 +704,26 @@ 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));
> +
> +			if (skip_prefix(valp, "left,", &valp))
> +				align->position = ALIGN_LEFT;
> +			else if (skip_prefix(valp, "right,", &valp))
> +				align->position = ALIGN_RIGHT;
> +			else if (skip_prefix(valp, "middle,", &valp))
> +				align->position = ALIGN_MIDDLE;
> +			else
> +				die(_("improper format entered align:%s"), valp);
> +			if (strtoul_ui(valp, 10, &align->width))
> +				die(_("positive width expected align:%s"), valp);

Minor nits on the design.  %(align:<width>[,<position>]) would let
us write %(align:16)...%(end) and use the "default position", which
may be beneficial if one kind of alignment is prevalent (I guess all
the internal users left-align?)  %(align:<position>,<width>) forces
users to spell both out all the time.

> @@ -1198,7 +1230,9 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
>  
>  struct ref_formatting_state {
>  	struct strbuf output;
> +	struct align *align;
>  	int quote_style;
> +	unsigned int end : 1;
>  };

Mental note: it is not clear why you need 'end' field in the state.
Perhaps it is an indication that the division of labor is poorly
designed between the helper that updates the formatting state and
the other helper that reflects the formatting state to the final
string.

> @@ -1262,12 +1296,31 @@ static void append_non_atom(const char *cp, const char *ep, struct ref_formattin
>  
>  static void set_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
>  {
> -	/* Based on the atomv values, the formatting state is set */
> +	if (atomv->align) {
> +		state->align = atomv->align;
> +		atomv->align = NULL;
> +	}
> +	if (atomv->end)
> +		state->end = 1;
> +}
> +
> +static int align_ref_strbuf(struct ref_formatting_state *state, struct strbuf *final)
> +{
> +	if (state->align && state->end) {

... and I think that is what I see.  If this function knows that we
are processing %(end), i.e. perform-state-formatting is called for
each atom and receives atomv, there wouldn't have to be a code like
this.

> +		struct align *align = state->align;
> +		strbuf_utf8_align(final, align->position, align->width, state->output.buf);
> +		strbuf_reset(&state->output);
> +		state->align = NULL;
> +		return 1;
> +	} else if (state->align)
> +		return 1;
> +	return 0;
>  }
>  
>  static void perform_state_formatting(struct ref_formatting_state *state, struct strbuf *final)
>  {
> -	/* More formatting options to be eventually added */
> +	if (align_ref_strbuf(state, final))
> +		return;

At the design level, I have a strong suspicion that it is a wrong
way to go.  It piles more "if (this state bit was left by the
previous atom) then do this" on this function and will make an
unmanageable mess.

You have a dictionary of all possible atoms somewhere.  Why not hook
a pointer to the "handler" function (or two) to each element in it,
instead of duplicating "this one is special" information down to
individual atom instantiations (i.e. atomv) as atomv.modifier_atom
bit, an dstructure the caller more like this?

	get_ref_atom_value(info, parse_ref_filter_atom, &atomv);
        if (atomv->pre_handler)
        	atomv->pre_handler(atomv, &state);
	format_quote_value(atomv, &state);
        if (atomv->post_handler)
        	atomv->post_handler(atomv, &state);

Actually, each atom could just have a single handler; an atom like
%(refname:short) whose sole effect is to append atomv->s to the
state buffer can point a function to do so in its handler.

On the other hand, align atom's handler would push a new state on
the stack, marking that it is the one to handle diverted output.

	align_atom_handler(atomv, state)
        {
        	struct format_state *new = push_new_state(state);
		strbuf_init(&new->output);
                new->atend = align_handler;
                new->return_to = atomv; /* or whatever that holds width,pos */
	}

Then end atom's handler would pop the state from the stack, and the
processing to be done

	end_atom_handler(atomv, state)
	{
                state->atend(state);
                pop_state(state);
	}                

and the called align_handler would be something like:

	align_handler(state)
	{
                struct strbuf aligned = STRBUF_INIT;
		struct format_state *return_to = state->prev;
                struct atom_value *atomv = state->return_to;

                strbuf_utf8_align(&aligned,
                        atomv->align.pos, atomv->align.width,
                        state->output.buf);
		strbuf_addbuf(&return_to->output, &aligned);
                strbuf_release(&aligned);
	}

With an arrangement like that, the body of the loop in
show_ref_array_item() could be as simple and regular as:

	get_ref_atom_value(info, parse_ref_filter_atom, &atomv);
       	atomv->handler(atomv, &state);

without any new "ah, this %(end) is special so we need a new
mechanism to pass information between set_formatting_state and
perform_formatting" logic introduced every time you add new things.

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

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-11 17:56   ` Junio C Hamano
@ 2015-08-12 13:22     ` Karthik Nayak
  2015-08-12 16:29       ` Junio C Hamano
  0 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 13:22 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 11, 2015 at 11:26 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> -static void print_value(struct atom_value *v, int quote_style)
>> +static void format_quote_value(struct atom_value *v, int quote_style, struct strbuf *output)
>>  {
>
> Hmph...
>
>> -static void emit(const char *cp, const char *ep)
>> +static void append_non_atom(const char *cp, const char *ep, struct strbuf *output)
>
> I was tempted to suggest s/non_atom/literal/, but this would do for now...
>

Since I'll be doing a re-roll I could do that.

>> @@ -1262,19 +1257,20 @@ 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 output = STRBUF_INIT;
>>
>>       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_non_atom(cp, sp, &output);
>>               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
>> -             print_value(atomv, quote_style);
>> +             format_quote_value(atomv, quote_style, &output);
>
> If the one to add a literal string (with %hex escaping) is called "append_",
> then this should be called append_quoted_atom() or something, no?

Although it does append like "append_non_atom" this is more of formatting based
on the quote given hence the name.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-11 18:00   ` Junio C Hamano
@ 2015-08-12 13:22     ` Karthik Nayak
  0 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 13:22 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 11, 2015 at 11:30 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> @@ -1283,9 +1279,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);
>> +             format_quote_value(&resetv, quote_style, &output);
>
> Mental note: I _think_ the logic to scan the string and set
> need_color_reset_at_eol that happens at the beginning can be removed
> once the code fully utilizes formatting-state information.  A
> coloring atom would leave a bit in the formatting state to say that
> the line color has been changed to something other than reset, and
> then this "at the end of line" code can decide if that is the case
> and add a "reset" thing here (i.e. the code inside the "if
> (need_color_reset_at_eol)" block shown here does not need to change,
> but the "if" condition would).
>
>

This could be done, when we implement color as a state :) Thanks for the note


-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 03/13] ref-filter: introduce ref_formatting_state
  2015-08-11 18:13   ` Junio C Hamano
@ 2015-08-12 13:26     ` Karthik Nayak
  0 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 13:26 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 11, 2015 at 11:43 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>>               get_ref_atom_value(info, parse_ref_filter_atom(sp + 2, ep), &atomv);
>> -             format_quote_value(atomv, quote_style, &output);
>> +             set_formatting_state(atomv, &state);
>> +             format_quote_value(atomv, &state);
>> +             perform_state_formatting(&state, &final_buf);
>>       }
>>       if (*cp) {
>>               sp = cp + strlen(cp);
>> -             append_non_atom(cp, sp, &output);
>> +             append_non_atom(cp, sp, &state);
>> +             perform_state_formatting(&state, &final_buf);
>>       }
>
> With the two helpers being very sketchy at this stage, it is very
> hard to judge if they make sense.  At the conceptual level, I can
> see that set-formatting-state is to allow an atom to affect the
> state before the value of the atom is emitted into the buffer.
> I cannot tell what perform-state-formatting is meant to do from
> these call sites.


True, set formatting state is to ensure that the state is manipulated for
a given atom.

perform_state_formatting() is meant to act on the state set by the atom,
It performs formatting based on the state values, here is just copies the
strbuf set within the state to the final_buf.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-11 18:22   ` Junio C Hamano
@ 2015-08-12 13:41     ` Karthik Nayak
  2015-08-12 16:40       ` Junio C Hamano
  0 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 13:41 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Tue, Aug 11, 2015 at 11:52 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
>> +                    const char *s)
>> +{
>> +     int display_len = utf8_strnwidth(s, strlen(s), 0);
>> +     int utf8_compenstation = strlen(s) - display_len;
>
> compensation, perhaps?  But notice you are running two strlen and
> then also a call to utf8-strnwidth here already, and then
>

compensation it is.
probably have a single strlen() call and set the value to another variable.

>> +     if (!strlen(s))
>> +             return;
>
> you return here without doing anything.
>
> Worse yet, this logic looks very strange.  If you give it a width of
> 8 because you want to align like-item on each record at that column,
> a record with 1-display-column item will be shown in 8-display-column
> with 7 SP padding (either at the beginning, or at the end, or on
> both ends to center).  If it happens to be an empty string, the entire
> 8-display-column disappears.
>
> Is that really what you meant to do?  The logic does not make much
> sense to me, even though the code may implement that logic correctly
> and clearly.
>

Not sure what you meant.
But this does not act on empty strings and that was intentional. It
does not make
sense to have an alignment on an empty string, where the caller could themselves
just do a `strbuf_addchars(&buf, " ", width)`.

>> +     if (display_len >= width) {
>> +             strbuf_addstr(buf, s);
>> +             return;
>> +     }
>
> Mental note: this function values the information more than
> presentation; it refuses to truncate (to lose information) and
> instead accepts jaggy output.  OK.
>

With regards to its usage in ref-filter, this is needed, don't you think?

>> +     if (position == ALIGN_LEFT)
>> +             strbuf_addf(buf, "%-*s", width + utf8_compenstation, s);
>> +     else if (position == ALIGN_MIDDLE) {
>> +             int left = (width - display_len)/2;
>> +             strbuf_addf(buf, "%*s%-*s", left, "", width - left + utf8_compenstation, s);
>> +     } else if (position == ALIGN_RIGHT)
>> +             strbuf_addf(buf, "%*s", width + utf8_compenstation, s);
>> +}

Thanks!

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-11 18:52   ` Junio C Hamano
@ 2015-08-12 16:24     ` Karthik Nayak
  2015-08-12 17:13       ` Junio C Hamano
  0 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 16:24 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Wed, Aug 12, 2015 at 12:22 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>>  struct atom_value{
>
> Obviously not a problem with this step, but you need a SP before the
> open brace.
>

Will add.

>> @@ -692,6 +704,26 @@ 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));
>> +
>> +                     if (skip_prefix(valp, "left,", &valp))
>> +                             align->position = ALIGN_LEFT;
>> +                     else if (skip_prefix(valp, "right,", &valp))
>> +                             align->position = ALIGN_RIGHT;
>> +                     else if (skip_prefix(valp, "middle,", &valp))
>> +                             align->position = ALIGN_MIDDLE;
>> +                     else
>> +                             die(_("improper format entered align:%s"), valp);
>> +                     if (strtoul_ui(valp, 10, &align->width))
>> +                             die(_("positive width expected align:%s"), valp);
>
> Minor nits on the design.  %(align:<width>[,<position>]) would let
> us write %(align:16)...%(end) and use the "default position", which
> may be beneficial if one kind of alignment is prevalent (I guess all
> the internal users left-align?)  %(align:<position>,<width>) forces
> users to spell both out all the time.
>

Isn't that better? I mean It sets a format which the others eventually
can follow
%(atom:suboption,value).
For example: %(objectname:abbrev,size)
Changing this would cause inconsistency according to me.

>> @@ -1198,7 +1230,9 @@ void ref_array_sort(struct ref_sorting *sorting, struct ref_array *array)
>>
>>  struct ref_formatting_state {
>>       struct strbuf output;
>> +     struct align *align;
>>       int quote_style;
>> +     unsigned int end : 1;
>>  };
>
> Mental note: it is not clear why you need 'end' field in the state.
> Perhaps it is an indication that the division of labor is poorly
> designed between the helper that updates the formatting state and
> the other helper that reflects the formatting state to the final
> string.
>

This goes with what you've said below!

>> @@ -1262,12 +1296,31 @@ static void append_non_atom(const char *cp, const char *ep, struct ref_formattin
>>
>>  static void set_formatting_state(struct atom_value *atomv, struct ref_formatting_state *state)
>>  {
>> -     /* Based on the atomv values, the formatting state is set */
>> +     if (atomv->align) {
>> +             state->align = atomv->align;
>> +             atomv->align = NULL;
>> +     }
>> +     if (atomv->end)
>> +             state->end = 1;
>> +}
>> +
>> +static int align_ref_strbuf(struct ref_formatting_state *state, struct strbuf *final)
>> +{
>> +     if (state->align && state->end) {
>
> ... and I think that is what I see.  If this function knows that we
> are processing %(end), i.e. perform-state-formatting is called for
> each atom and receives atomv, there wouldn't have to be a code like
> this.
>

Agreed, your suggestion below eradicates this

>> +             struct align *align = state->align;
>> +             strbuf_utf8_align(final, align->position, align->width, state->output.buf);
>> +             strbuf_reset(&state->output);
>> +             state->align = NULL;
>> +             return 1;
>> +     } else if (state->align)
>> +             return 1;
>> +     return 0;
>>  }
>>
>>  static void perform_state_formatting(struct ref_formatting_state *state, struct strbuf *final)
>>  {
>> -     /* More formatting options to be eventually added */
>> +     if (align_ref_strbuf(state, final))
>> +             return;
>
> At the design level, I have a strong suspicion that it is a wrong
> way to go.  It piles more "if (this state bit was left by the
> previous atom) then do this" on this function and will make an
> unmanageable mess.

Hmm, yeah makes sense.

>
> You have a dictionary of all possible atoms somewhere.  Why not hook
> a pointer to the "handler" function (or two) to each element in it,
> instead of duplicating "this one is special" information down to
> individual atom instantiations (i.e. atomv) as atomv.modifier_atom
> bit, an dstructure the caller more like this?
>
>         get_ref_atom_value(info, parse_ref_filter_atom, &atomv);
>         if (atomv->pre_handler)
>                 atomv->pre_handler(atomv, &state);
>         format_quote_value(atomv, &state);
>         if (atomv->post_handler)
>                 atomv->post_handler(atomv, &state);
>
> Actually, each atom could just have a single handler; an atom like
> %(refname:short) whose sole effect is to append atomv->s to the
> state buffer can point a function to do so in its handler.
>
> On the other hand, align atom's handler would push a new state on
> the stack, marking that it is the one to handle diverted output.
>
>         align_atom_handler(atomv, state)
>         {
>                 struct format_state *new = push_new_state(state);
>                 strbuf_init(&new->output);
>                 new->atend = align_handler;
>                 new->return_to = atomv; /* or whatever that holds width,pos */
>         }
>
> Then end atom's handler would pop the state from the stack, and the
> processing to be done
>
>         end_atom_handler(atomv, state)
>         {
>                 state->atend(state);
>                 pop_state(state);
>         }
>
> and the called align_handler would be something like:
>
>         align_handler(state)
>         {
>                 struct strbuf aligned = STRBUF_INIT;
>                 struct format_state *return_to = state->prev;
>                 struct atom_value *atomv = state->return_to;
>
>                 strbuf_utf8_align(&aligned,
>                         atomv->align.pos, atomv->align.width,
>                         state->output.buf);
>                 strbuf_addbuf(&return_to->output, &aligned);
>                 strbuf_release(&aligned);
>         }
>
> With an arrangement like that, the body of the loop in
> show_ref_array_item() could be as simple and regular as:
>
>         get_ref_atom_value(info, parse_ref_filter_atom, &atomv);
>         atomv->handler(atomv, &state);
>
> without any new "ah, this %(end) is special so we need a new
> mechanism to pass information between set_formatting_state and
> perform_formatting" logic introduced every time you add new things.
>

This I agree is a way better scheme, This could also eventually help cleanup
populate_value() which is a huge function at the moment.

I like your suggestion, I'll work on this, Thanks a lot :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-12 13:22     ` Karthik Nayak
@ 2015-08-12 16:29       ` Junio C Hamano
  2015-08-12 16:56         ` Karthik Nayak
  0 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-12 16:29 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

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

>>> +             format_quote_value(atomv, quote_style, &output);
>>
>> If the one to add a literal string (with %hex escaping) is called "append_",
>> then this should be called append_quoted_atom() or something, no?
>
> Although it does append like "append_non_atom" this is more of formatting based
> on the quote given hence the name.

Appending formatted values is still appending, no?

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-12 13:41     ` Karthik Nayak
@ 2015-08-12 16:40       ` Junio C Hamano
  2015-08-12 17:10         ` Karthik Nayak
  0 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-12 16:40 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

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

> On Tue, Aug 11, 2015 at 11:52 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> Karthik Nayak <karthik.188@gmail.com> writes:
>>
>>> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
>>> +                    const char *s)
>>> +{
>>> +     int display_len = utf8_strnwidth(s, strlen(s), 0);
>>> +     int utf8_compenstation = strlen(s) - display_len;
>>
>> compensation, perhaps?  But notice you are running two strlen and
>> then also a call to utf8-strnwidth here already, and then
>>
>
> compensation it is.
> probably have a single strlen() call and set the value to another variable.

That is not what I meant.  If you want to keep the early return of
"doing nothing for an empty string" (which you should *NOT*, by the
way), declare these variables upfront, do the early return and then
call utf8_strnwidth() to compute the value you are going to use,
only when you know you are going to use them.  That is what I meant.

>>> +     if (!strlen(s))
>>> +             return;
>>
>> you return here without doing anything.
>>
>> Worse yet, this logic looks very strange.  If you give it a width of
>> 8 because you want to align like-item on each record at that column,
>> a record with 1-display-column item will be shown in 8-display-column
>> with 7 SP padding (either at the beginning, or at the end, or on
>> both ends to center).  If it happens to be an empty string, the entire
>> 8-display-column disappears.
>>
>> Is that really what you meant to do?  The logic does not make much
>> sense to me, even though the code may implement that logic correctly
>> and clearly.
>
> Not sure what you meant.
> But this does not act on empty strings and that was intentional.

If it is truly intentional, then the design is wrong.  You are
writing a public function that can be called by other people.

Which one makes more sense?  Remember that you are going to have
many callers over time in the course of project in the future.

 (A) The caller is forced to always check the string that he is
     merely passing on, i.e.

	struct strbuf buf = STRBUF_INIT;
        struct strbuf out = STRBUF_INIT;

	fill_some_placeholder(&buf, fmt, args);
        if (buf.len)
        	strbuf_utf8_align(&out, ALIGN_LEFT, 8, buf.buf);
        else
        	strbuf_addchars(&out, ' ', 8);

     only because the callee strbuf_utf8_align() refuses to do the
     trivial work.

 (B) The caller does not have to care, i.e.

	struct strbuf buf = STRBUF_INIT;
        struct strbuf out = STRBUF_INIT;

	fill_some_placeholder(&buf, fmt, args);
       	strbuf_utf8_align(&out, ALIGN_LEFT, 8, buf.buf);

> It
> does not make
> sense to have an alignment on an empty string, where the caller could themselves
> just do a `strbuf_addchars(&buf, " ", width)`.

It simply does not make sense to force the caller to even care.
What they want is a series of lines, whose columns are aligned.

>>> +     if (display_len >= width) {
>>> +             strbuf_addstr(buf, s);
>>> +             return;
>>> +     }
>>
>> Mental note: this function values the information more than
>> presentation; it refuses to truncate (to lose information) and
>> instead accepts jaggy output.  OK.
>
> With regards to its usage in ref-filter, this is needed, don't you think?

That is exactly why I said "OK".

I was primarily trying to lead other reviewers by example,
demonstrating that a review will become more credible if it shows
that the reviewer actually read the patch by explaining the thinking
behind what the code does in his own words.  I see too many "FWIW,
reviewed by me" without indicating if it is "from just a cursory
read it looks nice" or "I fully read and understood it and I agree
with the design choices it makes", which does not help me very much
when queuing a patch.

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

* Re: [PATCH v10 02/13] ref-filter: print output to strbuf for formatting
  2015-08-12 16:29       ` Junio C Hamano
@ 2015-08-12 16:56         ` Karthik Nayak
  0 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 16:56 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Wed, Aug 12, 2015 at 9:59 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>>>> +             format_quote_value(atomv, quote_style, &output);
>>>
>>> If the one to add a literal string (with %hex escaping) is called "append_",
>>> then this should be called append_quoted_atom() or something, no?
>>
>> Although it does append like "append_non_atom" this is more of formatting based
>> on the quote given hence the name.
>
> Appending formatted values is still appending, no?

Of course :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-12 16:40       ` Junio C Hamano
@ 2015-08-12 17:10         ` Karthik Nayak
  0 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 17:10 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Wed, Aug 12, 2015 at 10:10 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Tue, Aug 11, 2015 at 11:52 PM, Junio C Hamano <gitster@pobox.com> wrote:
>>> Karthik Nayak <karthik.188@gmail.com> writes:
>>>
>>>> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
>>>> +                    const char *s)
>>>> +{
>>>> +     int display_len = utf8_strnwidth(s, strlen(s), 0);
>>>> +     int utf8_compenstation = strlen(s) - display_len;
>>>
>>> compensation, perhaps?  But notice you are running two strlen and
>>> then also a call to utf8-strnwidth here already, and then
>>>
>>
>> compensation it is.
>> probably have a single strlen() call and set the value to another variable.
>
> That is not what I meant.  If you want to keep the early return of
> "doing nothing for an empty string" (which you should *NOT*, by the
> way), declare these variables upfront, do the early return and then
> call utf8_strnwidth() to compute the value you are going to use,
> only when you know you are going to use them.  That is what I meant.
>

Oh okay, after reading your mail now that makes sense.

>>>> +     if (!strlen(s))
>>>> +             return;
>>>
>>> you return here without doing anything.
>>>
>>> Worse yet, this logic looks very strange.  If you give it a width of
>>> 8 because you want to align like-item on each record at that column,
>>> a record with 1-display-column item will be shown in 8-display-column
>>> with 7 SP padding (either at the beginning, or at the end, or on
>>> both ends to center).  If it happens to be an empty string, the entire
>>> 8-display-column disappears.
>>>
>>> Is that really what you meant to do?  The logic does not make much
>>> sense to me, even though the code may implement that logic correctly
>>> and clearly.
>>
>> Not sure what you meant.
>> But this does not act on empty strings and that was intentional.
>
> If it is truly intentional, then the design is wrong.  You are
> writing a public function that can be called by other people.
>
> Which one makes more sense?  Remember that you are going to have
> many callers over time in the course of project in the future.
>
>  (A) The caller is forced to always check the string that he is
>      merely passing on, i.e.
>
>         struct strbuf buf = STRBUF_INIT;
>         struct strbuf out = STRBUF_INIT;
>
>         fill_some_placeholder(&buf, fmt, args);
>         if (buf.len)
>                 strbuf_utf8_align(&out, ALIGN_LEFT, 8, buf.buf);
>         else
>                 strbuf_addchars(&out, ' ', 8);
>
>      only because the callee strbuf_utf8_align() refuses to do the
>      trivial work.
>
>  (B) The caller does not have to care, i.e.
>
>         struct strbuf buf = STRBUF_INIT;
>         struct strbuf out = STRBUF_INIT;
>
>         fill_some_placeholder(&buf, fmt, args);
>         strbuf_utf8_align(&out, ALIGN_LEFT, 8, buf.buf);
>

Yea, good point here, thanks for putting it out :)

>> It
>> does not make
>> sense to have an alignment on an empty string, where the caller could themselves
>> just do a `strbuf_addchars(&buf, " ", width)`.
>
> It simply does not make sense to force the caller to even care.
> What they want is a series of lines, whose columns are aligned.
>

My ignorance, sorry!

>>>> +     if (display_len >= width) {
>>>> +             strbuf_addstr(buf, s);
>>>> +             return;
>>>> +     }
>>>
>>> Mental note: this function values the information more than
>>> presentation; it refuses to truncate (to lose information) and
>>> instead accepts jaggy output.  OK.
>>
>> With regards to its usage in ref-filter, this is needed, don't you think?
>
> That is exactly why I said "OK".
>
> I was primarily trying to lead other reviewers by example,
> demonstrating that a review will become more credible if it shows
> that the reviewer actually read the patch by explaining the thinking
> behind what the code does in his own words.  I see too many "FWIW,
> reviewed by me" without indicating if it is "from just a cursory
> read it looks nice" or "I fully read and understood it and I agree
> with the design choices it makes", which does not help me very much
> when queuing a patch.
>
>

Ah! okay! was just wondering if you were looking for an explanation :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-12 16:24     ` Karthik Nayak
@ 2015-08-12 17:13       ` Junio C Hamano
  2015-08-12 20:07         ` Karthik Nayak
  0 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-12 17:13 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

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

> On Wed, Aug 12, 2015 at 12:22 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
>> Minor nits on the design.  %(align:<width>[,<position>]) would let
>> us write %(align:16)...%(end) and use the "default position", which
>> may be beneficial if one kind of alignment is prevalent (I guess all
>> the internal users left-align?)  %(align:<position>,<width>) forces
>> users to spell both out all the time.
>>
>
> Isn't that better? I mean It sets a format which the others eventually
> can follow
> %(atom:suboption,value).
> For example: %(objectname:abbrev,size)

No, I do not think it is better.  First of all, the similarity you
are perceiving does not exist.  For 'objectname:abbrev', the 'size'
is a property of the 'abbrev'.  For 'align:left', the 'width' is not
a property of the position.  It is a property given to 'align'
(i.e. you have this many display columns to work with).

Also, if there are ways other than 'abbrev' that '8' could affect
the way how %(objectname) is modified, then it should be spelled as
%(objectname:abbrev=8).  To specify two modification magics, each of
which takes a number, the user would say e.g.

    %(objectname:abbrev=8,magic=4)

The syntax %(objectname:abbrev,size) would not allow you to extend
it nicely---you would end up with %(objectname:abbrev,8,magic,4) or
something silly like that.

Seeing your %(objectname:abbrev,size), I'd imagine that you are
assuming that you will never allow any magic other than 'abbrev'
that takes a number to %(objectname).  And under that assumption
%(objectname:8) would be a short-hand for %(objectname:8,abbrev).

And that would be following %(align:8).  Both 'left' (implied
default) and '8' are instructing 'align' what to do.

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-12 17:13       ` Junio C Hamano
@ 2015-08-12 20:07         ` Karthik Nayak
  2015-08-12 20:24           ` Junio C Hamano
  0 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-12 20:07 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Wed, Aug 12, 2015 at 10:43 PM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Wed, Aug 12, 2015 at 12:22 AM, Junio C Hamano <gitster@pobox.com> wrote:
>>
>>> Minor nits on the design.  %(align:<width>[,<position>]) would let
>>> us write %(align:16)...%(end) and use the "default position", which
>>> may be beneficial if one kind of alignment is prevalent (I guess all
>>> the internal users left-align?)  %(align:<position>,<width>) forces
>>> users to spell both out all the time.
>>>
>>
>> Isn't that better? I mean It sets a format which the others eventually
>> can follow
>> %(atom:suboption,value).
>> For example: %(objectname:abbrev,size)
>
> No, I do not think it is better.  First of all, the similarity you
> are perceiving does not exist.  For 'objectname:abbrev', the 'size'
> is a property of the 'abbrev'.  For 'align:left', the 'width' is not
> a property of the position.  It is a property given to 'align'
> (i.e. you have this many display columns to work with).
>
> Also, if there are ways other than 'abbrev' that '8' could affect
> the way how %(objectname) is modified, then it should be spelled as
> %(objectname:abbrev=8).  To specify two modification magics, each of
> which takes a number, the user would say e.g.
>
>     %(objectname:abbrev=8,magic=4)
>
> The syntax %(objectname:abbrev,size) would not allow you to extend
> it nicely---you would end up with %(objectname:abbrev,8,magic,4) or
> something silly like that.
>
> Seeing your %(objectname:abbrev,size), I'd imagine that you are
> assuming that you will never allow any magic other than 'abbrev'
> that takes a number to %(objectname).  And under that assumption
> %(objectname:8) would be a short-hand for %(objectname:8,abbrev).
>

I never thought about this, thanks for explaining.

> And that would be following %(align:8).  Both 'left' (implied
> default) and '8' are instructing 'align' what to do.
>

Will follow this. :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-12 20:07         ` Karthik Nayak
@ 2015-08-12 20:24           ` Junio C Hamano
  2015-08-14 15:46             ` Karthik Nayak
  0 siblings, 1 reply; 40+ messages in thread
From: Junio C Hamano @ 2015-08-12 20:24 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

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

> On Wed, Aug 12, 2015 at 10:43 PM, Junio C Hamano <gitster@pobox.com> wrote:
> ...
>> %(objectname:abbrev=8).  To specify two modification magics, each of
>> which takes a number, the user would say e.g.
>>
>>     %(objectname:abbrev=8,magic=4)
>> ...
>> And that would be following %(align:8).  Both 'left' (implied
>> default) and '8' are instructing 'align' what to do.
>
> Will follow this. :)

I think the most generic way to think about this is to consider that
the most fully spelled form of align would be this:

	%(align:width=12,position=left)

And another rule you would have is that the user is allowed to omit
"<attr>=" when it is obvious from its value.  For "align", 'left'
can only possibly be the value for 'position' and similarly '12' for
'width'.  That is why the "objectname" example says "abbrev=8", and
not "abbrev,8", because from the value of "8" without the attribute
name, you cannot tell if the user meant abbrev=8 or magic=8.

That '"<attr>=" can be omitted' rule makes both of these valid
constructs:

	%(align:12,left) %(align:left,12)

Moreover, if you make "left aligned" the default behaviour when
position is not specified, you can make %(align:12) the shortest way
to spell the same thing.  Note that I said "if you make"; I do not
offhand know if all the internal callers of this mechanism your
updates to "branch -l" and "tag -l" would want left aligned output
(if so, that is one argument to favor making left-aligned the
implicit default; if not, it may be better to require the position
always specified).

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-09 14:11 ` [PATCH v10 05/13] ref-filter: implement an `align` atom Karthik Nayak
  2015-08-11 18:52   ` Junio C Hamano
@ 2015-08-13 18:26   ` Eric Sunshine
  2015-08-13 20:29     ` Karthik Nayak
  1 sibling, 1 reply; 40+ messages in thread
From: Eric Sunshine @ 2015-08-13 18:26 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git List, Christian Couder, Matthieu Moy, Junio C Hamano

On Sun, Aug 9, 2015 at 10:11 AM, Karthik Nayak <karthik.188@gmail.com> wrote:
> Implement an `align` atom which left-, middle-, or right-aligns the
> content between %(align:..) and %(end).
>
> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
> index e49d578..d949812 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::
> +       Implement an `align` atom which left-, middle-, or
> +       right-aligns the content between %(align:..)  and

This documentation seems to be copied a bit too literally from the
commit message. Saying "Implement an `align` atom" makes sense for the
commit message, but not for the documentation of the 'align' atom.
Instead,

    Left-, middle-, or right-align the content between
    %(align:...) and %(end).

would sound better.

> +       %(end). Followed by `:<position>,<width>`, 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. Currently nested alignment is not supported.

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
  2015-08-11 18:22   ` Junio C Hamano
@ 2015-08-13 19:08   ` Eric Sunshine
  2015-08-13 20:55     ` Karthik Nayak
  2015-08-14 16:06     ` Junio C Hamano
  2015-08-15  8:27   ` Duy Nguyen
  2 siblings, 2 replies; 40+ messages in thread
From: Eric Sunshine @ 2015-08-13 19:08 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git List, Christian Couder, Matthieu Moy, Junio C Hamano

On Sun, Aug 9, 2015 at 10:11 AM, Karthik Nayak <karthik.188@gmail.com> wrote:
> 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.

In addition to Junio's valuable comments...

> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
> ---
> diff --git a/utf8.h b/utf8.h
> index 5a9e94b..db8ca63 100644
> --- a/utf8.h
> +++ b/utf8.h
> @@ -55,4 +55,17 @@ 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 type
> + * and width.
> + */

Please extend this documentation to state explicitly that this
function preserves (does not truncate) the input string if it is wider
than 'width'. That's quite important information for the caller to
know.

(Aside: I could easily see this function being extended to support
optional truncation, but that's a separate topic, and something that
can be done by someone else when the feature is actually needed; it's
not your responsibility.)

> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
> +                      const char *s);
> +
>  #endif

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-13 18:26   ` Eric Sunshine
@ 2015-08-13 20:29     ` Karthik Nayak
  0 siblings, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-13 20:29 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List, Christian Couder, Matthieu Moy, Junio C Hamano

On Thu, Aug 13, 2015 at 11:56 PM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, Aug 9, 2015 at 10:11 AM, Karthik Nayak <karthik.188@gmail.com> wrote:
>> Implement an `align` atom which left-, middle-, or right-aligns the
>> content between %(align:..) and %(end).
>>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
>> index e49d578..d949812 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::
>> +       Implement an `align` atom which left-, middle-, or
>> +       right-aligns the content between %(align:..)  and
>
> This documentation seems to be copied a bit too literally from the
> commit message. Saying "Implement an `align` atom" makes sense for the
> commit message, but not for the documentation of the 'align' atom.
> Instead,
>
>     Left-, middle-, or right-align the content between
>     %(align:...) and %(end).
>
> would sound better.
>

Okay! Will do thanks!

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-13 19:08   ` Eric Sunshine
@ 2015-08-13 20:55     ` Karthik Nayak
  2015-08-14 16:06     ` Junio C Hamano
  1 sibling, 0 replies; 40+ messages in thread
From: Karthik Nayak @ 2015-08-13 20:55 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Git List, Christian Couder, Matthieu Moy, Junio C Hamano

On Fri, Aug 14, 2015 at 12:38 AM, Eric Sunshine <sunshine@sunshineco.com> wrote:
> On Sun, Aug 9, 2015 at 10:11 AM, Karthik Nayak <karthik.188@gmail.com> wrote:
>> 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.
>
> In addition to Junio's valuable comments...
>
>> Signed-off-by: Karthik Nayak <karthik.188@gmail.com>
>> ---
>> diff --git a/utf8.h b/utf8.h
>> index 5a9e94b..db8ca63 100644
>> --- a/utf8.h
>> +++ b/utf8.h
>> @@ -55,4 +55,17 @@ 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 type
>> + * and width.
>> + */
>
> Please extend this documentation to state explicitly that this
> function preserves (does not truncate) the input string if it is wider
> than 'width'. That's quite important information for the caller to
> know.
>
> (Aside: I could easily see this function being extended to support
> optional truncation, but that's a separate topic, and something that
> can be done by someone else when the feature is actually needed; it's
> not your responsibility.)
>
>> +void strbuf_utf8_align(struct strbuf *buf, align_type position, unsigned int width,
>> +                      const char *s);
>> +
>>  #endif

Yeah will do :)

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-12 20:24           ` Junio C Hamano
@ 2015-08-14 15:46             ` Karthik Nayak
  2015-08-14 17:42               ` Junio C Hamano
  0 siblings, 1 reply; 40+ messages in thread
From: Karthik Nayak @ 2015-08-14 15:46 UTC (permalink / raw)
  To: Junio C Hamano; +Cc: Git, Christian Couder, Matthieu Moy

On Thu, Aug 13, 2015 at 1:54 AM, Junio C Hamano <gitster@pobox.com> wrote:
> Karthik Nayak <karthik.188@gmail.com> writes:
>
>> On Wed, Aug 12, 2015 at 10:43 PM, Junio C Hamano <gitster@pobox.com> wrote:
>> ...
>>> %(objectname:abbrev=8).  To specify two modification magics, each of
>>> which takes a number, the user would say e.g.
>>>
>>>     %(objectname:abbrev=8,magic=4)
>>> ...
>>> And that would be following %(align:8).  Both 'left' (implied
>>> default) and '8' are instructing 'align' what to do.
>>
>> Will follow this. :)
>
> I think the most generic way to think about this is to consider that
> the most fully spelled form of align would be this:
>
>         %(align:width=12,position=left)
>
> And another rule you would have is that the user is allowed to omit
> "<attr>=" when it is obvious from its value.  For "align", 'left'
> can only possibly be the value for 'position' and similarly '12' for
> 'width'.  That is why the "objectname" example says "abbrev=8", and
> not "abbrev,8", because from the value of "8" without the attribute
> name, you cannot tell if the user meant abbrev=8 or magic=8.
>
> That '"<attr>=" can be omitted' rule makes both of these valid
> constructs:
>
>         %(align:12,left) %(align:left,12)
>

Are you sure you want it to be so flexible? I mean It's nice to have it,
but it would include a lot more processing, I'd prefer having either the
generic type you mentioned about so users know what they're typing
or else a predefined way like

           %(align:<width>,<position>)

> Moreover, if you make "left aligned" the default behaviour when
> position is not specified, you can make %(align:12) the shortest way
> to spell the same thing.  Note that I said "if you make"; I do not
> offhand know if all the internal callers of this mechanism your
> updates to "branch -l" and "tag -l" would want left aligned output
> (if so, that is one argument to favor making left-aligned the
> implicit default; if not, it may be better to require the position
> always specified).

Left align is what I'd prefer as default for branch -l and tag -l so
that will be
done.

-- 
Regards,
Karthik Nayak

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-13 19:08   ` Eric Sunshine
  2015-08-13 20:55     ` Karthik Nayak
@ 2015-08-14 16:06     ` Junio C Hamano
  1 sibling, 0 replies; 40+ messages in thread
From: Junio C Hamano @ 2015-08-14 16:06 UTC (permalink / raw)
  To: Eric Sunshine; +Cc: Karthik Nayak, Git List, Christian Couder, Matthieu Moy

Eric Sunshine <sunshine@sunshineco.com> writes:

> Please extend this documentation to state explicitly that this
> function preserves (does not truncate) the input string if it is wider
> than 'width'. That's quite important information for the caller to
> know.
>
> (Aside: I could easily see this function being extended to support
> optional truncation, but that's a separate topic, and something that
> can be done by someone else when the feature is actually needed; it's
> not your responsibility.)

Thanks for being explicit.  That is exactly what I had in mind when
I commented on this part with "mental note".

Extending what the function does is outside the scope of this topic,
but documenting the current design decision is, in order to help
those who might want to do so.

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

* Re: [PATCH v10 05/13] ref-filter: implement an `align` atom
  2015-08-14 15:46             ` Karthik Nayak
@ 2015-08-14 17:42               ` Junio C Hamano
  0 siblings, 0 replies; 40+ messages in thread
From: Junio C Hamano @ 2015-08-14 17:42 UTC (permalink / raw)
  To: Karthik Nayak; +Cc: Git, Christian Couder, Matthieu Moy

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

> On Thu, Aug 13, 2015 at 1:54 AM, Junio C Hamano <gitster@pobox.com> wrote:
>
>> I think the most generic way to think about this is to consider that
>> the most fully spelled form of align would be this:
>>
>>         %(align:width=12,position=left)
>>
>> And another rule you would have is that the user is allowed to omit
>> "<attr>=" when it is obvious from its value.  For "align", 'left'
>> can only possibly be the value for 'position' and similarly '12' for
>> 'width'.  That is why the "objectname" example says "abbrev=8", and
>> not "abbrev,8", because from the value of "8" without the attribute
>> name, you cannot tell if the user meant abbrev=8 or magic=8.
>>
>> That '"<attr>=" can be omitted' rule makes both of these valid
>> constructs:
>>
>>         %(align:12,left) %(align:left,12)
>>
>
> Are you sure you want it to be so flexible?

Eventually, yes.

But that does not mean you must do it by next week, and that is why
I said "way to think about is to consider", not "way to design this
is to implement".

In the worst case, if the only-allowed form the end-users can give
were limited to "align:12,left" and "align:12" and "align:left,12"
failed in the version you complete by the end of GSoC, it is still
OK to ship it in 2.7, with a "known bugs" section for somebody
(maybe you) to further polish in the future.  As long as we leave
the door open for such an enhancement that would not make what used
to be valid invalid and only would allow what used to be invalid
valid, we would be fine---we can make progress without harming
existing users.

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

* Re: [PATCH v10 04/13] utf8: add function to align a string into given strbuf
  2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
  2015-08-11 18:22   ` Junio C Hamano
  2015-08-13 19:08   ` Eric Sunshine
@ 2015-08-15  8:27   ` Duy Nguyen
  2 siblings, 0 replies; 40+ messages in thread
From: Duy Nguyen @ 2015-08-15  8:27 UTC (permalink / raw)
  To: Karthik Nayak
  Cc: Git Mailing List, Christian Couder, Matthieu Moy, Junio C Hamano

On Sun, Aug 9, 2015 at 9:11 PM, Karthik Nayak <karthik.188@gmail.com> wrote:
> 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.

I smell an opportunity to reuse this code and kill some in pretty.c's
format_and_pad_commit(). If you want, you can do it. Else this is my
personal note so I don't forget to do it later :)
-- 
Duy

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

end of thread, other threads:[~2015-08-15  8:28 UTC | newest]

Thread overview: 40+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2015-08-09 14:11 [PATCH v10 00/13] port tag.c to use ref-filter APIs Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 01/13] ref-filter: move `struct atom_value` to ref-filter.c Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 02/13] ref-filter: print output to strbuf for formatting Karthik Nayak
2015-08-11 17:56   ` Junio C Hamano
2015-08-12 13:22     ` Karthik Nayak
2015-08-12 16:29       ` Junio C Hamano
2015-08-12 16:56         ` Karthik Nayak
2015-08-11 18:00   ` Junio C Hamano
2015-08-12 13:22     ` Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 03/13] ref-filter: introduce ref_formatting_state Karthik Nayak
2015-08-11 18:13   ` Junio C Hamano
2015-08-12 13:26     ` Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 04/13] utf8: add function to align a string into given strbuf Karthik Nayak
2015-08-11 18:22   ` Junio C Hamano
2015-08-12 13:41     ` Karthik Nayak
2015-08-12 16:40       ` Junio C Hamano
2015-08-12 17:10         ` Karthik Nayak
2015-08-13 19:08   ` Eric Sunshine
2015-08-13 20:55     ` Karthik Nayak
2015-08-14 16:06     ` Junio C Hamano
2015-08-15  8:27   ` Duy Nguyen
2015-08-09 14:11 ` [PATCH v10 05/13] ref-filter: implement an `align` atom Karthik Nayak
2015-08-11 18:52   ` Junio C Hamano
2015-08-12 16:24     ` Karthik Nayak
2015-08-12 17:13       ` Junio C Hamano
2015-08-12 20:07         ` Karthik Nayak
2015-08-12 20:24           ` Junio C Hamano
2015-08-14 15:46             ` Karthik Nayak
2015-08-14 17:42               ` Junio C Hamano
2015-08-13 18:26   ` Eric Sunshine
2015-08-13 20:29     ` Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 06/13] ref-filter: add option to filter only tags Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 07/13] ref-filter: support printing N lines from tag annotation Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 08/13] ref-filter: add support to sort by version Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 09/13] ref-filter: add option to match literal pattern Karthik Nayak
2015-08-09 14:11 ` [PATCH v10 10/13] tag.c: use 'ref-filter' data structures Karthik Nayak
2015-08-09 14:17 ` Karthik Nayak
2015-08-09 14:32 ` [PATCH v10 11/13] tag.c: use 'ref-filter' APIs Karthik Nayak
2015-08-09 14:32 ` [PATCH v10 12/13] tag.c: implement '--format' option Karthik Nayak
2015-08-09 14:32 ` [PATCH v10 13/13] tag.c: implement '--merged' and '--no-merged' options Karthik Nayak

Code repositories for project(s) associated with this public inbox

	https://80x24.org/mirrors/git.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).