[PATCH 0/3] Juggling between hot branches

STALE3764d

Revision v1 of 2 in this series.

13 messages, 5 authors, 2016-06-15 · open the first message on its own page

[PATCH 0/3] Juggling between hot branches

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Hi,

I juggle between several hot branches, and an alphabetical listing
from 'git branch' doesn't cut it for me. I've chosen to enhance
for-each-ref so that I get output like (with color):

  $ git hot
    um-build>
    perf-manifest=
  * master=
    sparse=
    ia32-asm-cleanup>
    menuconfig-jk<>
    perf-build=
    perf-completion<

where hot is the following alias:

  for-each-ref --format='%C(red)%(HEAD)%C(reset)
  %C(green)%(refname:short)%C(reset)%(upstream:trackshort)' --count 10
  --sort='-committerdate' refs/heads

While the alias might look a bit horrendous, I get the desired output.

The last time I tried to get this feature merged, there was some
confusion about unifying the format of for-each-ref with
pretty-formats, and enhacing git-branch while at it. I tried going
down that road, but got no reviews; everyone was generally more
unhappy due to the added complexity. Months have passed since, and we
still don't have this feature.

Let's keep it simple and stupid. A terse +84,-10 (with documentation)
for this wonderful feature now. Let's get it merged, and defer the
kitchen-sink-unification efforts.

Thanks.

Ramkumar Ramachandra (3):
  for-each-ref: introduce %C(...) for color
  for-each-ref: introduce %(HEAD) asterisk marker
  for-each-ref: introduce %(upstream:track[short])

 Documentation/git-for-each-ref.txt | 14 ++++++-
 builtin/for-each-ref.c             | 80 ++++++++++++++++++++++++++++++++++----
 2 files changed, 84 insertions(+), 10 deletions(-)

-- 
1.8.4.478.g55109e3

[PATCH 2/3] for-each-ref: introduce %(HEAD) asterisk marker

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

'git branch' shows which branch you are currently on with an '*', but
'git for-each-ref' misses this feature.  So, extend its format with
%(HEAD) for the same effect.

Now you can use the following format in for-each-ref:

  %C(red)%(HEAD)%C(reset) %(refname:short)

to display a red asterisk next to the current ref.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/git-for-each-ref.txt |  4 ++++
 builtin/for-each-ref.c             | 13 +++++++++++--
 2 files changed, 15 insertions(+), 2 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index 078a116..f1d4e9e 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -95,6 +95,10 @@ upstream::
 	from the displayed ref. Respects `:short` in the same way as
 	`refname` above.
 
+HEAD::
+	Used to indicate the currently checked out branch.  Is '*' if
+	HEAD points to the current ref, and ' ' otherwise.
+
 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/builtin/for-each-ref.c b/builtin/for-each-ref.c
index a1ca186..e54b5d8 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -76,6 +76,7 @@ static struct {
 	{ "upstream" },
 	{ "symref" },
 	{ "flag" },
+	{ "HEAD" },
 };
 
 /*
@@ -682,8 +683,16 @@ static void populate_value(struct refinfo *ref)
 				v->s = xstrdup(buf + 1);
 			}
 			continue;
-		}
-		else
+		} else if (!strcmp(name, "HEAD")) {
+			const char *head;
+			unsigned char sha1[20];
+			head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
+			if (!strcmp(ref->refname, head))
+				v->s = "*";
+			else
+				v->s = " ";
+			continue;
+		} else
 			continue;
 
 		formatp = strchr(name, ':');
-- 
1.8.4.478.g55109e3

[PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Introduce %(upstream:track) to display "[ahead M, behind N]" and
%(upstream:trackshort) to display "=", ">", "<", or "<>"
appropriately (inspired by contrib/completion/git-prompt.sh).

Now you can use the following format in for-each-ref:

  %C(green)%(refname:short)%C(reset)%(upstream:trackshort)

to display refs with terse tracking information.

Note that :track and :trackshort only work with "upstream", and error
out when used with anything else.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/git-for-each-ref.txt |  6 +++++-
 builtin/for-each-ref.c             | 44 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f1d4e9e..682eaa8 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -93,7 +93,11 @@ objectname::
 upstream::
 	The name of a local ref which can be considered ``upstream''
 	from the displayed ref. Respects `:short` in the same way as
-	`refname` above.
+	`refname` above.  Additionally respects `:track` to show
+	"[ahead N, behind M]" and `:trackshort` to show the terse
+	version (like the prompt) ">", "<", "<>", or "=".  Has no
+	effect if the ref does not have tracking information
+	associated with it.
 
 HEAD::
 	Used to indicate the currently checked out branch.  Is '*' if
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e54b5d8..10843bb 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -631,6 +631,7 @@ static void populate_value(struct refinfo *ref)
 	int eaten, i;
 	unsigned long size;
 	const unsigned char *tagged;
+	int upstream_present = 0;
 
 	ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
 
@@ -648,6 +649,7 @@ static void populate_value(struct refinfo *ref)
 		int deref = 0;
 		const char *refname;
 		const char *formatp;
+		struct branch *branch;
 
 		if (*name == '*') {
 			deref = 1;
@@ -659,7 +661,6 @@ static void populate_value(struct refinfo *ref)
 		else if (!prefixcmp(name, "symref"))
 			refname = ref->symref ? ref->symref : "";
 		else if (!prefixcmp(name, "upstream")) {
-			struct branch *branch;
 			/* only local branches may have an upstream */
 			if (prefixcmp(ref->refname, "refs/heads/"))
 				continue;
@@ -669,6 +670,7 @@ static void populate_value(struct refinfo *ref)
 			    !branch->merge[0]->dst)
 				continue;
 			refname = branch->merge[0]->dst;
+			upstream_present = 1;
 		}
 		else if (!strcmp(name, "flag")) {
 			char buf[256], *cp = buf;
@@ -686,6 +688,7 @@ static void populate_value(struct refinfo *ref)
 		} else if (!strcmp(name, "HEAD")) {
 			const char *head;
 			unsigned char sha1[20];
+
 			head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
 			if (!strcmp(ref->refname, head))
 				v->s = "*";
@@ -698,11 +701,48 @@ static void populate_value(struct refinfo *ref)
 		formatp = strchr(name, ':');
 		/* look for "short" refname format */
 		if (formatp) {
+			int num_ours, num_theirs;
+
 			formatp++;
 			if (!strcmp(formatp, "short"))
 				refname = shorten_unambiguous_ref(refname,
 						      warn_ambiguous_refs);
-			else
+			else if (!strcmp(formatp, "track") &&
+				!prefixcmp(name, "upstream")) {
+				char buf[40];
+
+				if (!upstream_present)
+					continue;
+				stat_tracking_info(branch, &num_ours, &num_theirs);
+				if (!num_ours && !num_theirs)
+					v->s = "";
+				else if (!num_ours) {
+					sprintf(buf, "[behind %d]", num_theirs);
+					v->s = xstrdup(buf);
+				} else if (!num_theirs) {
+					sprintf(buf, "[ahead %d]", num_ours);
+					v->s = xstrdup(buf);
+				} else {
+					sprintf(buf, "[ahead %d, behind %d]",
+						num_ours, num_theirs);
+					v->s = xstrdup(buf);
+				}
+				continue;
+			} else if (!strcmp(formatp, "trackshort") &&
+				!prefixcmp(name, "upstream")) {
+				if (!upstream_present)
+					continue;
+				stat_tracking_info(branch, &num_ours, &num_theirs);
+				if (!num_ours && !num_theirs)
+					v->s = "=";
+				else if (!num_ours)
+					v->s = "<";
+				else if (!num_theirs)
+					v->s = ">";
+				else
+					v->s = "<>";
+				continue;
+			} else
 				die("unknown %.*s format %s",
 				    (int)(formatp - name), name, formatp);
 		}
-- 
1.8.4.478.g55109e3

[PATCH 1/3] for-each-ref: introduce %C(...) for color

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Enhance 'git for-each-ref' with color formatting options.  You can now
use the following format in for-each-ref:

  %C(green)%(refname:short)%C(reset)

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/git-for-each-ref.txt |  4 +++-
 builtin/for-each-ref.c             | 23 +++++++++++++++++++----
 2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f2e08d1..078a116 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -45,7 +45,9 @@ OPTIONS
 	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).
+	`%09` to `\t` (TAB) and `%0a` to `\n` (LF). Additionally,
+	colors can be specified using `%C(colorname)`. Use
+	`%C(reset)` to reset the color.
 
 <pattern>...::
 	If one or more patterns are given, only refs are shown that
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 1d4083c..a1ca186 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "parse-options.h"
 #include "remote.h"
+#include "color.h"
 
 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -155,10 +156,13 @@ static const char *find_next(const char *cp)
 	while (*cp) {
 		if (*cp == '%') {
 			/*
+			 * %C( is the start of a color;
 			 * %( is the start of an atom;
 			 * %% is a quoted per-cent.
 			 */
-			if (cp[1] == '(')
+			if (cp[1] == 'C' && cp[2] == '(')
+				return cp;
+			else if (cp[1] == '(')
 				return cp;
 			else if (cp[1] == '%')
 				cp++; /* skip over two % */
@@ -180,8 +184,11 @@ static int verify_format(const char *format)
 		const char *ep = strchr(sp, ')');
 		if (!ep)
 			return error("malformed format string %s", sp);
-		/* sp points at "%(" and ep points at the closing ")" */
-		parse_atom(sp + 2, ep);
+		/* Ignore color specifications: %C(
+		 * sp points at "%(" and ep points at the closing ")"
+		 */
+		if (prefixcmp(sp, "%C("))
+			parse_atom(sp + 2, ep);
 		cp = ep + 1;
 	}
 	return 0;
@@ -933,12 +940,20 @@ static void emit(const char *cp, const char *ep)
 static void show_ref(struct refinfo *info, const char *format, int quote_style)
 {
 	const char *cp, *sp, *ep;
+	char color[COLOR_MAXLEN];
 
 	for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
 		ep = strchr(sp, ')');
 		if (cp < sp)
 			emit(cp, sp);
-		print_value(info, parse_atom(sp + 2, ep), quote_style);
+
+		/* Do we have a color specification? */
+		if (!prefixcmp(sp, "%C("))
+			color_parse_mem(sp + 3, ep - sp - 3, "--format", color);
+		else {
+			printf("%s", color);
+			print_value(info, parse_atom(sp + 2, ep), quote_style);
+		}
 	}
 	if (*cp) {
 		sp = cp + strlen(cp);
-- 
1.8.4.478.g55109e3

Re: [PATCH 1/3] for-each-ref: introduce %C(...) for color

From: Phil Hord <hidden>
Date: 2016-06-15 22:58:54

On Fri, Sep 27, 2013 at 8:10 AM, Ramkumar Ramachandra
[off-list ref] wrote:
quoted hunk
Enhance 'git for-each-ref' with color formatting options.  You can now
use the following format in for-each-ref:

  %C(green)%(refname:short)%C(reset)

Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/git-for-each-ref.txt |  4 +++-
 builtin/for-each-ref.c             | 23 +++++++++++++++++++----
 2 files changed, 22 insertions(+), 5 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f2e08d1..078a116 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -45,7 +45,9 @@ OPTIONS
        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).
+       `%09` to `\t` (TAB) and `%0a` to `\n` (LF). Additionally,
+       colors can be specified using `%C(colorname)`. Use
+       `%C(reset)` to reset the color.
Reduce the color explanation here and refer to the config page.
Something like pretty-formats does:

    '%C(...)': color specification, as described in color.branch.*
config option;
quoted hunk
 <pattern>...::
        If one or more patterns are given, only refs are shown that
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index 1d4083c..a1ca186 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -9,6 +9,7 @@
 #include "quote.h"
 #include "parse-options.h"
 #include "remote.h"
+#include "color.h"

 /* Quoting styles */
 #define QUOTE_NONE 0
@@ -155,10 +156,13 @@ static const char *find_next(const char *cp)
        while (*cp) {
                if (*cp == '%') {
                        /*
+                        * %C( is the start of a color;
                         * %( is the start of an atom;
                         * %% is a quoted per-cent.
                         */
-                       if (cp[1] == '(')
+                       if (cp[1] == 'C' && cp[2] == '(')
+                               return cp;
+                       else if (cp[1] == '(')
                                return cp;
                        else if (cp[1] == '%')
                                cp++; /* skip over two % */
@@ -180,8 +184,11 @@ static int verify_format(const char *format)
                const char *ep = strchr(sp, ')');
                if (!ep)
                        return error("malformed format string %s", sp);
-               /* sp points at "%(" and ep points at the closing ")" */
-               parse_atom(sp + 2, ep);
+               /* Ignore color specifications: %C(
+                * sp points at "%(" and ep points at the closing ")"
+                */
+               if (prefixcmp(sp, "%C("))
+                       parse_atom(sp + 2, ep);
                cp = ep + 1;
        }
        return 0;
@@ -933,12 +940,20 @@ static void emit(const char *cp, const char *ep)
 static void show_ref(struct refinfo *info, const char *format, int quote_style)
 {
        const char *cp, *sp, *ep;
+       char color[COLOR_MAXLEN];

        for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {
                ep = strchr(sp, ')');
                if (cp < sp)
                        emit(cp, sp);
-               print_value(info, parse_atom(sp + 2, ep), quote_style);
+
+               /* Do we have a color specification? */
+               if (!prefixcmp(sp, "%C("))
+                       color_parse_mem(sp + 3, ep - sp - 3, "--format", color);
+               else {
+                       printf("%s", color);
'color' used uninitialized here?
+                       print_value(info, parse_atom(sp + 2, ep), quote_style);
+               }
        }
        if (*cp) {
                sp = cp + strlen(cp);
--
1.8.4.478.g55109e3

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Phil Hord <hidden>
Date: 2016-06-15 22:58:54

On Fri, Sep 27, 2013 at 8:10 AM, Ramkumar Ramachandra
[off-list ref] wrote:
Introduce %(upstream:track) to display "[ahead M, behind N]" and
%(upstream:trackshort) to display "=", ">", "<", or "<>"
appropriately (inspired by contrib/completion/git-prompt.sh).

Now you can use the following format in for-each-ref:

  %C(green)%(refname:short)%C(reset)%(upstream:trackshort)

to display refs with terse tracking information.
Thanks.  I like this.
Note that :track and :trackshort only work with "upstream", and error
out when used with anything else.
I think I would like to use %(refname:track) myself, but this does not
detract from this change.
quoted hunk
Signed-off-by: Ramkumar Ramachandra <redacted>
---
 Documentation/git-for-each-ref.txt |  6 +++++-
 builtin/for-each-ref.c             | 44 ++++++++++++++++++++++++++++++++++++--
 2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f1d4e9e..682eaa8 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -93,7 +93,11 @@ objectname::
 upstream::
        The name of a local ref which can be considered ``upstream''
        from the displayed ref. Respects `:short` in the same way as
-       `refname` above.
+       `refname` above.  Additionally respects `:track` to show
+       "[ahead N, behind M]" and `:trackshort` to show the terse
+       version (like the prompt) ">", "<", "<>", or "=".  Has no
+       effect if the ref does not have tracking information
+       associated with it.

 HEAD::
        Used to indicate the currently checked out branch.  Is '*' if
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e54b5d8..10843bb 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -631,6 +631,7 @@ static void populate_value(struct refinfo *ref)
        int eaten, i;
        unsigned long size;
        const unsigned char *tagged;
+       int upstream_present = 0;
This flag is out of place.  It should be in the same scope as 'branch'
since the code which depends on this flag also depends on '!!branch'.

However, I don't think it is even necessary.  The only way to reach
the places where this flag is tested is when (name="upstream") and
(upstream exists).  In all other cases, the parser loops before
reaching the track/trackshort code or else it doesn't enter it.
quoted hunk
        ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
@@ -648,6 +649,7 @@ static void populate_value(struct refinfo *ref)
                int deref = 0;
                const char *refname;
                const char *formatp;
+               struct branch *branch;

                if (*name == '*') {
                        deref = 1;
@@ -659,7 +661,6 @@ static void populate_value(struct refinfo *ref)
                else if (!prefixcmp(name, "symref"))
                        refname = ref->symref ? ref->symref : "";
                else if (!prefixcmp(name, "upstream")) {
-                       struct branch *branch;
                        /* only local branches may have an upstream */
                        if (prefixcmp(ref->refname, "refs/heads/"))
                                continue;
@@ -669,6 +670,7 @@ static void populate_value(struct refinfo *ref)
                            !branch->merge[0]->dst)
                                continue;
                        refname = branch->merge[0]->dst;
+                       upstream_present = 1;
                }
                else if (!strcmp(name, "flag")) {
                        char buf[256], *cp = buf;
@@ -686,6 +688,7 @@ static void populate_value(struct refinfo *ref)
                } else if (!strcmp(name, "HEAD")) {
                        const char *head;
                        unsigned char sha1[20];
+
                        head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
                        if (!strcmp(ref->refname, head))
                                v->s = "*";
@@ -698,11 +701,48 @@ static void populate_value(struct refinfo *ref)
                formatp = strchr(name, ':');
                /* look for "short" refname format */
                if (formatp) {
+                       int num_ours, num_theirs;
+
                        formatp++;
                        if (!strcmp(formatp, "short"))
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
-                       else
+                       else if (!strcmp(formatp, "track") &&
+                               !prefixcmp(name, "upstream")) {
+                               char buf[40];
+
+                               if (!upstream_present)
+                                       continue;
+                               stat_tracking_info(branch, &num_ours, &num_theirs);
+                               if (!num_ours && !num_theirs)
+                                       v->s = "";
Is this the same as 'continue'?
+                               else if (!num_ours) {
+                                       sprintf(buf, "[behind %d]", num_theirs);
+                                       v->s = xstrdup(buf);
+                               } else if (!num_theirs) {
+                                       sprintf(buf, "[ahead %d]", num_ours);
+                                       v->s = xstrdup(buf);
+                               } else {
+                                       sprintf(buf, "[ahead %d, behind %d]",
+                                               num_ours, num_theirs);
+                                       v->s = xstrdup(buf);
+                               }
+                               continue;
+                       } else if (!strcmp(formatp, "trackshort") &&
+                               !prefixcmp(name, "upstream")) {
+                               if (!upstream_present)
+                                       continue;
+                               stat_tracking_info(branch, &num_ours, &num_theirs);
+                               if (!num_ours && !num_theirs)
+                                       v->s = "=";
+                               else if (!num_ours)
+                                       v->s = "<";
+                               else if (!num_theirs)
+                                       v->s = ">";
+                               else
+                                       v->s = "<>";
+                               continue;
+                       } else
                                die("unknown %.*s format %s",
                                    (int)(formatp - name), name, formatp);
                }
--
1.8.4.478.g55109e3

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majordomo@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Johannes Sixt <hidden>
Date: 2016-06-15 22:58:54

Am 9/27/2013 14:10, schrieb Ramkumar Ramachandra:
quoted hunk
+			else if (!strcmp(formatp, "track") &&
+				!prefixcmp(name, "upstream")) {
+				char buf[40];
+
+				if (!upstream_present)
+					continue;
+				stat_tracking_info(branch, &num_ours, &num_theirs);
+				if (!num_ours && !num_theirs)
+					v->s = "";
+				else if (!num_ours) {
+					sprintf(buf, "[behind %d]", num_theirs);
+					v->s = xstrdup(buf);
+				} else if (!num_theirs) {
+					sprintf(buf, "[ahead %d]", num_ours);
+					v->s = xstrdup(buf);
+				} else {
+					sprintf(buf, "[ahead %d, behind %d]",
+						num_ours, num_theirs);
+					v->s = xstrdup(buf);
+				}
These strdupped strings are leaked, right?
quoted hunk
+				continue;
+			} else if (!strcmp(formatp, "trackshort") &&
+				!prefixcmp(name, "upstream")) {
+				if (!upstream_present)
+					continue;
+				stat_tracking_info(branch, &num_ours, &num_theirs);
+				if (!num_ours && !num_theirs)
+					v->s = "=";
+				else if (!num_ours)
+					v->s = "<";
+				else if (!num_theirs)
+					v->s = ">";
+				else
+					v->s = "<>";
+				continue;
+			} else
 				die("unknown %.*s format %s",
 				    (int)(formatp - name), name, formatp);
 		}
-- Hannes

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Phil Hord wrote:
quoted
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -631,6 +631,7 @@ static void populate_value(struct refinfo *ref)
        int eaten, i;
        unsigned long size;
        const unsigned char *tagged;
+       int upstream_present = 0;
This flag is out of place.  It should be in the same scope as 'branch'
since the code which depends on this flag also depends on '!!branch'.
Agreed. Fixed.
However, I don't think it is even necessary.  The only way to reach
the places where this flag is tested is when (name="upstream") and
(upstream exists).  In all other cases, the parser loops before
reaching the track/trackshort code or else it doesn't enter it.
Yeah, you're right. I was setting upstream_present in this snippet:

  else if (!prefixcmp(name, "upstream")) {
  /* only local branches may have an upstream */
    if (prefixcmp(ref->refname, "refs/heads/"))
      continue;

If the refname doesn't begin with "refs/heads" in the first place
(which is what I was guarding against), the code will loop and never
reach the track[short] code anyway.

upstream_present factored out now.
quoted
@@ -698,11 +701,48 @@ static void populate_value(struct refinfo *ref)
                formatp = strchr(name, ':');
                /* look for "short" refname format */
                if (formatp) {
+                       int num_ours, num_theirs;
+
                        formatp++;
                        if (!strcmp(formatp, "short"))
                                refname = shorten_unambiguous_ref(refname,
                                                      warn_ambiguous_refs);
-                       else
+                       else if (!strcmp(formatp, "track") &&
+                               !prefixcmp(name, "upstream")) {
+                               char buf[40];
+
+                               if (!upstream_present)
+                                       continue;
+                               stat_tracking_info(branch, &num_ours, &num_theirs);
+                               if (!num_ours && !num_theirs)
+                                       v->s = "";
Is this the same as 'continue'?
I'll leave this as it is for readability reasons.

Thanks for the review.

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Johannes Sixt wrote:
quoted
+                             else if (!num_ours) {
+                                     sprintf(buf, "[behind %d]", num_theirs);
+                                     v->s = xstrdup(buf);
+                             } else if (!num_theirs) {
+                                     sprintf(buf, "[ahead %d]", num_ours);
+                                     v->s = xstrdup(buf);
+                             } else {
+                                     sprintf(buf, "[ahead %d, behind %d]",
+                                             num_ours, num_theirs);
+                                     v->s = xstrdup(buf);
+                             }
These strdupped strings are leaked, right?
Yes, there's a minor leakage; there are quite a few instances of this
in the rest of the file. Do you see an easy fix?

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Philip Oakley <hidden>
Date: 2016-06-15 22:58:54

----- Original Message ----- From: "Ramkumar Ramachandra" [off-list ref]
Sent: Friday, September 27, 2013 1:10 PM
quoted hunk
Introduce %(upstream:track) to display "[ahead M, behind N]" and
%(upstream:trackshort) to display "=", ">", "<", or "<>"
appropriately (inspired by contrib/completion/git-prompt.sh).

Now you can use the following format in for-each-ref:

 %C(green)%(refname:short)%C(reset)%(upstream:trackshort)

to display refs with terse tracking information.

Note that :track and :trackshort only work with "upstream", and error
out when used with anything else.

Signed-off-by: Ramkumar Ramachandra <redacted>
---
Documentation/git-for-each-ref.txt |  6 +++++-
builtin/for-each-ref.c             | 44 ++++++++++++++++++++++++++++++++++++--
2 files changed, 47 insertions(+), 3 deletions(-)
diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt
index f1d4e9e..682eaa8 100644
--- a/Documentation/git-for-each-ref.txt
+++ b/Documentation/git-for-each-ref.txt
@@ -93,7 +93,11 @@ objectname::
upstream::
 The name of a local ref which can be considered ``upstream''
 from the displayed ref. Respects `:short` in the same way as
- `refname` above.
+ `refname` above.  Additionally respects `:track` to show
+ "[ahead N, behind M]" and `:trackshort` to show the terse
+ version (like the prompt) ">", "<", "<>", or "=".  Has no
+ effect if the ref does not have tracking information
+ associated with it.
"=" and "<>" I can easily understand (binary choice), but ">" and "<" will
need to be clear which way they indicate in terms of matching
the "[ahead N]" and  "[behind M]" options.

Otherwise a good idea.

Philip
quoted hunk
HEAD::
 Used to indicate the currently checked out branch.  Is '*' if
diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c
index e54b5d8..10843bb 100644
--- a/builtin/for-each-ref.c
+++ b/builtin/for-each-ref.c
@@ -631,6 +631,7 @@ static void populate_value(struct refinfo *ref)
 int eaten, i;
 unsigned long size;
 const unsigned char *tagged;
+ int upstream_present = 0;

 ref->value = xcalloc(sizeof(struct atom_value), used_atom_cnt);
@@ -648,6 +649,7 @@ static void populate_value(struct refinfo *ref)
 int deref = 0;
 const char *refname;
 const char *formatp;
+ struct branch *branch;

 if (*name == '*') {
 deref = 1;
@@ -659,7 +661,6 @@ static void populate_value(struct refinfo *ref)
 else if (!prefixcmp(name, "symref"))
 refname = ref->symref ? ref->symref : "";
 else if (!prefixcmp(name, "upstream")) {
- struct branch *branch;
 /* only local branches may have an upstream */
 if (prefixcmp(ref->refname, "refs/heads/"))
 continue;
@@ -669,6 +670,7 @@ static void populate_value(struct refinfo *ref)
     !branch->merge[0]->dst)
 continue;
 refname = branch->merge[0]->dst;
+ upstream_present = 1;
 }
 else if (!strcmp(name, "flag")) {
 char buf[256], *cp = buf;
@@ -686,6 +688,7 @@ static void populate_value(struct refinfo *ref)
 } else if (!strcmp(name, "HEAD")) {
 const char *head;
 unsigned char sha1[20];
+
 head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
 if (!strcmp(ref->refname, head))
 v->s = "*";
@@ -698,11 +701,48 @@ static void populate_value(struct refinfo *ref)
 formatp = strchr(name, ':');
 /* look for "short" refname format */
 if (formatp) {
+ int num_ours, num_theirs;
+
 formatp++;
 if (!strcmp(formatp, "short"))
 refname = shorten_unambiguous_ref(refname,
       warn_ambiguous_refs);
- else
+ else if (!strcmp(formatp, "track") &&
+ !prefixcmp(name, "upstream")) {
+ char buf[40];
+
+ if (!upstream_present)
+ continue;
+ stat_tracking_info(branch, &num_ours, &num_theirs);
+ if (!num_ours && !num_theirs)
+ v->s = "";
+ else if (!num_ours) {
+ sprintf(buf, "[behind %d]", num_theirs);
+ v->s = xstrdup(buf);
+ } else if (!num_theirs) {
+ sprintf(buf, "[ahead %d]", num_ours);
+ v->s = xstrdup(buf);
+ } else {
+ sprintf(buf, "[ahead %d, behind %d]",
+ num_ours, num_theirs);
+ v->s = xstrdup(buf);
+ }
+ continue;
+ } else if (!strcmp(formatp, "trackshort") &&
+ !prefixcmp(name, "upstream")) {
+ if (!upstream_present)
+ continue;
+ stat_tracking_info(branch, &num_ours, &num_theirs);
+ if (!num_ours && !num_theirs)
+ v->s = "=";
+ else if (!num_ours)
+ v->s = "<";
+ else if (!num_theirs)
+ v->s = ">";
+ else
+ v->s = "<>";
+ continue;
+ } else
 die("unknown %.*s format %s",
     (int)(formatp - name), name, formatp);
 }
-- 
1.8.4.478.g55109e3

--

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Ramkumar Ramachandra <hidden>
Date: 2016-06-15 22:58:54

Philip Oakley wrote:
"=" and "<>" I can easily understand (binary choice), but ">" and "<" will
need to be clear which way they indicate in terms of matching
the "[ahead N]" and  "[behind M]" options.
The ">" corresponds to ahead, while "<" is behind. You'll get used to
it pretty quickly :)

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Philip Oakley <hidden>
Date: 2016-06-15 22:58:55

From: "Ramkumar Ramachandra" <redacted>
Philip Oakley wrote:
quoted
"=" and "<>" I can easily understand (binary choice), but ">" and "<" will
need to be clear which way they indicate in terms of matching
the "[ahead N]" and  "[behind M]" options.
The ">" corresponds to ahead, while "<" is behind. You'll get used to
it pretty quickly :)
But this documentation section could say ;-)
quoted
quoted
diff --git a/Documentation/git-for-each-ref.txt
b/Documentation/git-for-each-ref.txt

regards

Philip

Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])

From: Jonathan Nieder <hidden>
Date: 2016-06-15 22:58:55

Johannes Sixt wrote:
Am 9/27/2013 14:10, schrieb Ramkumar Ramachandra:
quoted
+					v->s = xstrdup(buf);
+				}
These strdupped strings are leaked, right?
The convention seems to be that each refinfo owns its atom_value,
which owns its string that is kept on the heap.  Except when it isn't
(e.g., "v->s = typename(obj->type);").  So at least this patch doesn't
make the muddle any worse. ;-)

A nice followup would be to consistently allocate atom_value.s on the
heap, check for a GIT_FREE_AT_EXIT envvar, and free the refinfos
if that envvar is set at exit.  That would make sure that the code is
careful enough with memory to some day free some refinfo earlier when
there are many refs.  Until that's ready, I think continuing to mix
and match like this (constant strings left as is, dynamically
generated strings on the heap) is the best we can do.

Thanks,
Jonathan
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help