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

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: %(refname:short)%(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> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Ramkumar Ramachandra committed Nov 18, 2013 at 23:09 UTC b28061ce0d2e31d390fee5a5724b8a061d0492a7
3 files changed +69 -4
Documentation/git-for-each-ref.txt
+5 -1
@@ -91,7 +91,11 @@ objectname::
91 upstream::
92 The name of a local ref which can be considered ``upstream''
93 from the displayed ref. Respects `:short` in the same way as
94 - `refname` above.
94 + `refname` above. Additionally respects `:track` to show
95 + "[ahead N, behind M]" and `:trackshort` to show the terse
96 + version: ">" (ahead), "<" (behind), "<>" (ahead and behind),
97 + or "=" (in sync). Has no effect if the ref does not have
98 + tracking information associated with it.
99
100 HEAD::
101 '*' if HEAD matches current ref (the checked out branch), ' '
builtin/for-each-ref.c
+37 -3
@@ -641,6 +641,7 @@ static void populate_value(struct refinfo *ref)
641 int deref = 0;
642 const char *refname;
643 const char *formatp;
644 + struct branch *branch = NULL;
645
646 if (*name == '*') {
647 deref = 1;
@@ -652,7 +653,6 @@ static void populate_value(struct refinfo *ref)
653 else if (!prefixcmp(name, "symref"))
654 refname = ref->symref ? ref->symref : "";
655 else if (!prefixcmp(name, "upstream")) {
655 - struct branch *branch;
656 /* only local branches may have an upstream */
657 if (prefixcmp(ref->refname, "refs/heads/"))
658 continue;
@@ -679,6 +679,7 @@ static void populate_value(struct refinfo *ref)
679 } else if (!strcmp(name, "HEAD")) {
680 const char *head;
681 unsigned char sha1[20];
682 +
683 head = resolve_ref_unsafe("HEAD", sha1, 1, NULL);
684 if (!strcmp(ref->refname, head))
685 v->s = "*";
@@ -689,13 +690,46 @@ static void populate_value(struct refinfo *ref)
690 continue;
691
692 formatp = strchr(name, ':');
692 - /* look for "short" refname format */
693 if (formatp) {
694 + int num_ours, num_theirs;
695 +
696 formatp++;
697 if (!strcmp(formatp, "short"))
698 refname = shorten_unambiguous_ref(refname,
699 warn_ambiguous_refs);
698 - else
700 + else if (!strcmp(formatp, "track") &&
701 + !prefixcmp(name, "upstream")) {
702 + char buf[40];
703 +
704 + stat_tracking_info(branch, &num_ours, &num_theirs);
705 + if (!num_ours && !num_theirs)
706 + v->s = "";
707 + else if (!num_ours) {
708 + sprintf(buf, "[behind %d]", num_theirs);
709 + v->s = xstrdup(buf);
710 + } else if (!num_theirs) {
711 + sprintf(buf, "[ahead %d]", num_ours);
712 + v->s = xstrdup(buf);
713 + } else {
714 + sprintf(buf, "[ahead %d, behind %d]",
715 + num_ours, num_theirs);
716 + v->s = xstrdup(buf);
717 + }
718 + continue;
719 + } else if (!strcmp(formatp, "trackshort") &&
720 + !prefixcmp(name, "upstream")) {
721 + assert(branch);
722 + stat_tracking_info(branch, &num_ours, &num_theirs);
723 + if (!num_ours && !num_theirs)
724 + v->s = "=";
725 + else if (!num_ours)
726 + v->s = "<";
727 + else if (!num_theirs)
728 + v->s = ">";
729 + else
730 + v->s = "<>";
731 + continue;
732 + } else
733 die("unknown %.*s format %s",
734 (int)(formatp - name), name, formatp);
735 }
t/t6300-for-each-ref.sh
+27
@@ -303,6 +303,33 @@ test_expect_success 'Check short upstream format' '
303 test_cmp expected actual
304 '
305
306 +test_expect_success 'setup for upstream:track[short]' '
307 + test_commit two
308 +'
309 +
310 +cat >expected <<EOF
311 +[ahead 1]
312 +EOF
313 +
314 +test_expect_success 'Check upstream:track format' '
315 + git for-each-ref --format="%(upstream:track)" refs/heads >actual &&
316 + test_cmp expected actual
317 +'
318 +
319 +cat >expected <<EOF
320 +>
321 +EOF
322 +
323 +test_expect_success 'Check upstream:trackshort format' '
324 + git for-each-ref --format="%(upstream:trackshort)" refs/heads >actual &&
325 + test_cmp expected actual
326 +'
327 +
328 +test_expect_success 'Check that :track[short] cannot be used with other atoms' '
329 + test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
330 + test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null
331 +'
332 +
333 cat >expected <<EOF
334 $(git rev-parse --short HEAD)
335 EOF