for-each-ref: introduce %(upstream:track[short])
authorRamkumar Ramachandra <artagnon@gmail.com>
Mon, 18 Nov 2013 17:39:11 +0000 (23:09 +0530)
committerJunio C Hamano <gitster@pobox.com>
Tue, 19 Nov 2013 18:14:15 +0000 (10:14 -0800)
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>
Documentation/git-for-each-ref.txt
builtin/for-each-ref.c
t/t6300-for-each-ref.sh
index 8f87c9a1ef733b98987fad377850fbcf2272ae95..92e82fdb02b81e19bf4f4c764c161f7a556f4d48 100644 (file)
@@ -91,7 +91,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: ">" (ahead), "<" (behind), "<>" (ahead and behind),
+       or "=" (in sync).  Has no effect if the ref does not have
+       tracking information associated with it.
 
 HEAD::
        '*' if HEAD matches current ref (the checked out branch), ' '
index 5f1842f48f89007eae821c422caeadcd4ef8a20c..9e45abd40a72049881b585d9cee127f75958ab09 100644 (file)
@@ -641,6 +641,7 @@ static void populate_value(struct refinfo *ref)
                int deref = 0;
                const char *refname;
                const char *formatp;
+               struct branch *branch = NULL;
 
                if (*name == '*') {
                        deref = 1;
@@ -652,7 +653,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;
@@ -679,6 +679,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 = "*";
@@ -689,13 +690,46 @@ static void populate_value(struct refinfo *ref)
                        continue;
 
                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];
+
+                               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")) {
+                               assert(branch);
+                               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);
                }
index 1d998f8550719afaff0c817ff61eff1a588ec222..d88d7acbddc31ef7f7eea7ce63c015e8591e6c4a 100755 (executable)
@@ -303,6 +303,33 @@ test_expect_success 'Check short upstream format' '
        test_cmp expected actual
 '
 
+test_expect_success 'setup for upstream:track[short]' '
+       test_commit two
+'
+
+cat >expected <<EOF
+[ahead 1]
+EOF
+
+test_expect_success 'Check upstream:track format' '
+       git for-each-ref --format="%(upstream:track)" refs/heads >actual &&
+       test_cmp expected actual
+'
+
+cat >expected <<EOF
+>
+EOF
+
+test_expect_success 'Check upstream:trackshort format' '
+       git for-each-ref --format="%(upstream:trackshort)" refs/heads >actual &&
+       test_cmp expected actual
+'
+
+test_expect_success 'Check that :track[short] cannot be used with other atoms' '
+       test_must_fail git for-each-ref --format="%(refname:track)" 2>/dev/null &&
+       test_must_fail git for-each-ref --format="%(refname:trackshort)" 2>/dev/null
+'
+
 cat >expected <<EOF
 $(git rev-parse --short HEAD)
 EOF