* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 12:10 ` [PATCH 3/3] for-each-ref: introduce %(upstream:track[short]) Ramkumar Ramachandra
@ 2013-09-27 14:03 ` Phil Hord
2013-09-27 14:27 ` Ramkumar Ramachandra
2013-09-27 14:25 ` Johannes Sixt
2013-09-27 16:06 ` Philip Oakley
2 siblings, 1 reply; 13+ messages in thread
From: Phil Hord @ 2013-09-27 14:03 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Git List, Jonathan Nieder
On Fri, Sep 27, 2013 at 8:10 AM, Ramkumar Ramachandra
<artagnon@gmail.com> 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.
>
> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>
> ---
> 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.
>
> 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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 14:03 ` Phil Hord
@ 2013-09-27 14:27 ` Ramkumar Ramachandra
0 siblings, 0 replies; 13+ messages in thread
From: Ramkumar Ramachandra @ 2013-09-27 14:27 UTC (permalink / raw)
To: Phil Hord; +Cc: Git List, Jonathan Nieder
Phil Hord wrote:
>> --- 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.
>> @@ -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.
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 12:10 ` [PATCH 3/3] for-each-ref: introduce %(upstream:track[short]) Ramkumar Ramachandra
2013-09-27 14:03 ` Phil Hord
@ 2013-09-27 14:25 ` Johannes Sixt
2013-09-27 14:33 ` Ramkumar Ramachandra
2013-09-27 22:18 ` Jonathan Nieder
2013-09-27 16:06 ` Philip Oakley
2 siblings, 2 replies; 13+ messages in thread
From: Johannes Sixt @ 2013-09-27 14:25 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Git List, Jonathan Nieder
Am 9/27/2013 14:10, schrieb Ramkumar Ramachandra:
> + 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?
> + 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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 14:25 ` Johannes Sixt
@ 2013-09-27 14:33 ` Ramkumar Ramachandra
2013-09-27 22:18 ` Jonathan Nieder
1 sibling, 0 replies; 13+ messages in thread
From: Ramkumar Ramachandra @ 2013-09-27 14:33 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Git List, Jonathan Nieder
Johannes Sixt wrote:
>> + 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?
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 14:25 ` Johannes Sixt
2013-09-27 14:33 ` Ramkumar Ramachandra
@ 2013-09-27 22:18 ` Jonathan Nieder
1 sibling, 0 replies; 13+ messages in thread
From: Jonathan Nieder @ 2013-09-27 22:18 UTC (permalink / raw)
To: Johannes Sixt; +Cc: Ramkumar Ramachandra, Git List
Johannes Sixt wrote:
> Am 9/27/2013 14:10, schrieb Ramkumar Ramachandra:
>> + 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
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 12:10 ` [PATCH 3/3] for-each-ref: introduce %(upstream:track[short]) Ramkumar Ramachandra
2013-09-27 14:03 ` Phil Hord
2013-09-27 14:25 ` Johannes Sixt
@ 2013-09-27 16:06 ` Philip Oakley
2013-09-27 16:10 ` Ramkumar Ramachandra
2 siblings, 1 reply; 13+ messages in thread
From: Philip Oakley @ 2013-09-27 16:06 UTC (permalink / raw)
To: Ramkumar Ramachandra, Git List; +Cc: Jonathan Nieder
----- Original Message -----
From: "Ramkumar Ramachandra" <artagnon@gmail.com>
Sent: Friday, September 27, 2013 1:10 PM
> 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 <artagnon@gmail.com>
> ---
> 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
> 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
>
> --
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 16:06 ` Philip Oakley
@ 2013-09-27 16:10 ` Ramkumar Ramachandra
2013-09-27 17:07 ` Philip Oakley
0 siblings, 1 reply; 13+ messages in thread
From: Ramkumar Ramachandra @ 2013-09-27 16:10 UTC (permalink / raw)
To: Philip Oakley; +Cc: Git List, Jonathan Nieder
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 :)
^ permalink raw reply [flat|nested] 13+ messages in thread
* Re: [PATCH 3/3] for-each-ref: introduce %(upstream:track[short])
2013-09-27 16:10 ` Ramkumar Ramachandra
@ 2013-09-27 17:07 ` Philip Oakley
0 siblings, 0 replies; 13+ messages in thread
From: Philip Oakley @ 2013-09-27 17:07 UTC (permalink / raw)
To: Ramkumar Ramachandra; +Cc: Git List, Jonathan Nieder
From: "Ramkumar Ramachandra" <artagnon@gmail.com>
> 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 :)
>
But this documentation section could say ;-)
>>> diff --git a/Documentation/git-for-each-ref.txt
>>> b/Documentation/git-for-each-ref.txt
regards
Philip
^ permalink raw reply [flat|nested] 13+ messages in thread