{"thread":{"id":"35344","subject":"[PATCH v3 0/6] Replacement for rr/for-each-ref-decoration","startedAt":"2013-11-15T10:59:02Z","lastAt":"2013-11-17T05:58:28Z","messageCount":12,"participants":["Ramkumar Ramachandra","Eric Sunshine"],"isPatch":true,"patchVersion":3,"patchTotal":6},"messages":[{"id":"230672","messageId":"1384513148-22633-1-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":null,"subject":"[PATCH v3 0/6] Replacement for rr/for-each-ref-decoration","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:02Z","receivedAt":"2013-11-15T10:59:02Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Hi Junio et al,\n\nv3 contains the following changes:\n\n- [1/6] is totally unrelated; it's a \"while we're there\" patch.\n- [2/6] is required for the test modification in [4/6].\n- [3/6], [4/6], [5/6] now come with tests.\n- [6/6] is new: implements Junio's suggestion to auto-reset color.\n\nI haven't included Junio's 78465bb (fixup! for-each-ref: introduce\n%(upstream:track[short]), 2013-10-31), because my compiler doesn't\ncomplain, and because it's ugly.\n\nThanks.\n\nRamkumar Ramachandra (6):\n  t6300 (for-each-ref): clearly demarcate setup\n  t6300 (for-each-ref): don't hardcode SHA-1 hexes\n  for-each-ref: introduce %(HEAD) asterisk marker\n  for-each-ref: introduce %(upstream:track[short])\n  for-each-ref: introduce %(color:...) for color\n  for-each-ref: auto reset color after each atom\n\n Documentation/git-for-each-ref.txt | 14 ++++++-\n builtin/for-each-ref.c             | 86 +++++++++++++++++++++++++++++++++-----\n t/t6300-for-each-ref.sh            | 59 ++++++++++++++++++++------\n 3 files changed, 135 insertions(+), 24 deletions(-)\n\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230673","messageId":"1384513148-22633-2-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 1/6] t6300 (for-each-ref): clearly demarcate setup","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:03Z","receivedAt":"2013-11-15T10:59:03Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Condense the two-step setup into one step, and give it an appropriate\nname.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n t/t6300-for-each-ref.sh | 5 +----\n 1 file changed, 1 insertion(+), 4 deletions(-)\n\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 752f5cb..72d282f 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -18,16 +18,13 @@ setdate_and_increment () {\n     export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n }\n \n-test_expect_success 'Create sample commit with known timestamp' '\n+test_expect_success setup '\n \tsetdate_and_increment &&\n \techo \"Using $datestamp\" > one &&\n \tgit add one &&\n \tgit commit -m \"Initial\" &&\n \tsetdate_and_increment &&\n \tgit tag -a -m \"Tagging at $datestamp\" testtag\n-'\n-\n-test_expect_success 'Create upstream config' '\n \tgit update-ref refs/remotes/origin/master master &&\n \tgit remote add origin nowhere &&\n \tgit config branch.master.remote origin &&\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230674","messageId":"1384513148-22633-3-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 2/6] t6300 (for-each-ref): don't hardcode SHA-1 hexes","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:04Z","receivedAt":"2013-11-15T10:59:04Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Use rev-parse in its place, making it easier for future patches to\nmodify the test script.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n t/t6300-for-each-ref.sh | 16 ++++++++--------\n 1 file changed, 8 insertions(+), 8 deletions(-)\n\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 72d282f..e1f71ff 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -49,8 +49,8 @@ test_atom head refname refs/heads/master\n test_atom head upstream refs/remotes/origin/master\n test_atom head objecttype commit\n test_atom head objectsize 171\n-test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581\n-test_atom head tree 0e51c00fcb93dffc755546f27593d511e1bdb46f\n+test_atom head objectname $(git rev-parse refs/heads/master)\n+test_atom head tree $(git rev-parse refs/heads/master^{tree})\n test_atom head parent ''\n test_atom head numparent 0\n test_atom head object ''\n@@ -82,11 +82,11 @@ test_atom tag refname refs/tags/testtag\n test_atom tag upstream ''\n test_atom tag objecttype tag\n test_atom tag objectsize 154\n-test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322\n+test_atom tag objectname $(git rev-parse refs/tags/testtag)\n test_atom tag tree ''\n test_atom tag parent ''\n test_atom tag numparent ''\n-test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581'\n+test_atom tag object $(git rev-parse refs/tags/testtag^0)\n test_atom tag type 'commit'\n test_atom tag author ''\n test_atom tag authorname ''\n@@ -302,7 +302,7 @@ test_expect_success 'Check short upstream format' '\n '\n \n cat >expected <<EOF\n-67a36f1\n+$(git rev-parse --short HEAD)\n EOF\n \n test_expect_success 'Check short objectname format' '\n@@ -453,9 +453,9 @@ test_atom refs/tags/signed-long contents \"subject line\n body contents\n $sig\"\n \n-cat >expected <<\\EOF\n-408fe76d02a785a006c2e9c669b7be5589ede96d <committer@example.com> refs/tags/master\n-90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 <committer@example.com> refs/tags/bogo\n+cat >expected <<EOF\n+$(git rev-parse refs/tags/master) <committer@example.com> refs/tags/master\n+$(git rev-parse refs/tags/bogo) <committer@example.com> refs/tags/bogo\n EOF\n \n test_expect_success 'Verify sort with multiple keys' '\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230676","messageId":"1384513148-22633-4-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 3/6] for-each-ref: introduce %(HEAD) asterisk marker","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:05Z","receivedAt":"2013-11-15T10:59:05Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"'git branch' shows which branch you are currently on with an '*', but\n'git for-each-ref' misses this feature.  So, extend its format with\n%(HEAD) for the same effect.\n\nNow you can use the following format in for-each-ref:\n\n  %(HEAD) %(refname:short)\n\nto display an asterisk next to the current ref.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  4 ++++\n builtin/for-each-ref.c             | 13 +++++++++++--\n t/t6300-for-each-ref.sh            |  2 ++\n 3 files changed, 17 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex f2e08d1..ab3da0e 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -93,6 +93,10 @@ upstream::\n \tfrom the displayed ref. Respects `:short` in the same way as\n \t`refname` above.\n \n+HEAD::\n+\tUsed to indicate the currently checked out branch.  Is '*' if\n+\tHEAD points to the current ref, and ' ' otherwise.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 1d4083c..5f1842f 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -75,6 +75,7 @@ static struct {\n \t{ \"upstream\" },\n \t{ \"symref\" },\n \t{ \"flag\" },\n+\t{ \"HEAD\" },\n };\n \n /*\n@@ -675,8 +676,16 @@ static void populate_value(struct refinfo *ref)\n \t\t\t\tv->s = xstrdup(buf + 1);\n \t\t\t}\n \t\t\tcontinue;\n-\t\t}\n-\t\telse\n+\t\t} else if (!strcmp(name, \"HEAD\")) {\n+\t\t\tconst char *head;\n+\t\t\tunsigned char sha1[20];\n+\t\t\thead = resolve_ref_unsafe(\"HEAD\", sha1, 1, NULL);\n+\t\t\tif (!strcmp(ref->refname, head))\n+\t\t\t\tv->s = \"*\";\n+\t\t\telse\n+\t\t\t\tv->s = \" \";\n+\t\t\tcontinue;\n+\t\t} else\n \t\t\tcontinue;\n \n \t\tformatp = strchr(name, ':');\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex e1f71ff..5e29ffc 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -77,6 +77,7 @@ test_atom head contents:body ''\n test_atom head contents:signature ''\n test_atom head contents 'Initial\n '\n+test_atom head HEAD '*'\n \n test_atom tag refname refs/tags/testtag\n test_atom tag upstream ''\n@@ -110,6 +111,7 @@ test_atom tag contents:body ''\n test_atom tag contents:signature ''\n test_atom tag contents 'Tagging at 1151939927\n '\n+test_atom tag HEAD ' '\n \n test_expect_success 'Check invalid atoms names are errors' '\n \ttest_must_fail git for-each-ref --format=\"%(INVALID)\" refs/heads\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230675","messageId":"1384513148-22633-5-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 4/6] for-each-ref: introduce %(upstream:track[short])","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:06Z","receivedAt":"2013-11-15T10:59:06Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Introduce %(upstream:track) to display \"[ahead M, behind N]\" and\n%(upstream:trackshort) to display \"=\", \">\", \"<\", or \"<>\"\nappropriately (inspired by contrib/completion/git-prompt.sh).\n\nNow you can use the following format in for-each-ref:\n\n  %(refname:short)%(upstream:trackshort)\n\nto display refs with terse tracking information.\n\nNote that :track and :trackshort only work with \"upstream\", and error\nout when used with anything else.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  6 +++++-\n builtin/for-each-ref.c             | 40 +++++++++++++++++++++++++++++++++++---\n t/t6300-for-each-ref.sh            | 22 +++++++++++++++++++++\n 3 files changed, 64 insertions(+), 4 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex ab3da0e..c9b192e 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -91,7 +91,11 @@ objectname::\n upstream::\n \tThe name of a local ref which can be considered ``upstream''\n \tfrom the displayed ref. Respects `:short` in the same way as\n-\t`refname` above.\n+\t`refname` above.  Additionally respects `:track` to show\n+\t\"[ahead N, behind M]\" and `:trackshort` to show the terse\n+\tversion (like the prompt) \">\", \"<\", \"<>\", or \"=\".  Has no\n+\teffect if the ref does not have tracking information\n+\tassociated with it.\n \n HEAD::\n \tUsed to indicate the currently checked out branch.  Is '*' if\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 5f1842f..ed81407 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -641,6 +641,7 @@ static void populate_value(struct refinfo *ref)\n \t\tint deref = 0;\n \t\tconst char *refname;\n \t\tconst char *formatp;\n+\t\tstruct branch *branch;\n \n \t\tif (*name == '*') {\n \t\t\tderef = 1;\n@@ -652,7 +653,6 @@ static void populate_value(struct refinfo *ref)\n \t\telse if (!prefixcmp(name, \"symref\"))\n \t\t\trefname = ref->symref ? ref->symref : \"\";\n \t\telse if (!prefixcmp(name, \"upstream\")) {\n-\t\t\tstruct branch *branch;\n \t\t\t/* only local branches may have an upstream */\n \t\t\tif (prefixcmp(ref->refname, \"refs/heads/\"))\n \t\t\t\tcontinue;\n@@ -679,6 +679,7 @@ static void populate_value(struct refinfo *ref)\n \t\t} else if (!strcmp(name, \"HEAD\")) {\n \t\t\tconst char *head;\n \t\t\tunsigned char sha1[20];\n+\n \t\t\thead = resolve_ref_unsafe(\"HEAD\", sha1, 1, NULL);\n \t\t\tif (!strcmp(ref->refname, head))\n \t\t\t\tv->s = \"*\";\n@@ -689,13 +690,46 @@ static void populate_value(struct refinfo *ref)\n \t\t\tcontinue;\n \n \t\tformatp = strchr(name, ':');\n-\t\t/* look for \"short\" refname format */\n \t\tif (formatp) {\n+\t\t\tint num_ours, num_theirs;\n+\n \t\t\tformatp++;\n \t\t\tif (!strcmp(formatp, \"short\"))\n \t\t\t\trefname = shorten_unambiguous_ref(refname,\n \t\t\t\t\t\t      warn_ambiguous_refs);\n-\t\t\telse\n+\t\t\telse if (!strcmp(formatp, \"track\") &&\n+\t\t\t\t!prefixcmp(name, \"upstream\")) {\n+\t\t\t\tchar buf[40];\n+\n+\t\t\t\tstat_tracking_info(branch, &num_ours, &num_theirs);\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"\";\n+\t\t\t\telse if (!num_ours) {\n+\t\t\t\t\tsprintf(buf, \"[behind %d]\", num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else if (!num_theirs) {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d]\", num_ours);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t} else {\n+\t\t\t\t\tsprintf(buf, \"[ahead %d, behind %d]\",\n+\t\t\t\t\t\tnum_ours, num_theirs);\n+\t\t\t\t\tv->s = xstrdup(buf);\n+\t\t\t\t}\n+\t\t\t\tcontinue;\n+\t\t\t} else if (!strcmp(formatp, \"trackshort\") &&\n+\t\t\t\t!prefixcmp(name, \"upstream\")) {\n+\n+\t\t\t\tstat_tracking_info(branch, &num_ours, &num_theirs);\n+\t\t\t\tif (!num_ours && !num_theirs)\n+\t\t\t\t\tv->s = \"=\";\n+\t\t\t\telse if (!num_ours)\n+\t\t\t\t\tv->s = \"<\";\n+\t\t\t\telse if (!num_theirs)\n+\t\t\t\t\tv->s = \">\";\n+\t\t\t\telse\n+\t\t\t\t\tv->s = \"<>\";\n+\t\t\t\tcontinue;\n+\t\t\t} else\n \t\t\t\tdie(\"unknown %.*s format %s\",\n \t\t\t\t    (int)(formatp - name), name, formatp);\n \t\t}\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 5e29ffc..9d874fd 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -303,6 +303,28 @@ test_expect_success 'Check short upstream format' '\n \ttest_cmp expected actual\n '\n \n+test_expect_success 'setup for upstream:track[short]' '\n+\ttest_commit two\n+'\n+\n+cat >expected <<EOF\n+[ahead 1]\n+EOF\n+\n+test_expect_success 'Check upstream:track format' '\n+\tgit for-each-ref --format=\"%(upstream:track)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n+cat >expected <<EOF\n+>\n+EOF\n+\n+test_expect_success 'Check upstream:trackshort format' '\n+\tgit for-each-ref --format=\"%(upstream:trackshort)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n cat >expected <<EOF\n $(git rev-parse --short HEAD)\n EOF\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230677","messageId":"1384513148-22633-6-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 5/6] for-each-ref: introduce %(color:...) for color","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:07Z","receivedAt":"2013-11-15T10:59:07Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Enhance 'git for-each-ref' with color formatting options.  You can now\nuse the following format in for-each-ref:\n\n  %(color:green)%(refname:short)%(color:reset)\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n Documentation/git-for-each-ref.txt |  4 ++++\n builtin/for-each-ref.c             | 11 +++++++++--\n t/t6300-for-each-ref.sh            | 14 ++++++++++++++\n 3 files changed, 27 insertions(+), 2 deletions(-)\n\ndiff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\nindex c9b192e..2f3ac22 100644\n--- a/Documentation/git-for-each-ref.txt\n+++ b/Documentation/git-for-each-ref.txt\n@@ -101,6 +101,10 @@ HEAD::\n \tUsed to indicate the currently checked out branch.  Is '*' if\n \tHEAD points to the current ref, and ' ' otherwise.\n \n+color::\n+\tUsed to change color of output.  Followed by `:<colorname>`,\n+\twhere names are described in `color.branch.*`.\n+\n In addition to the above, for commit and tag objects, the header\n field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n be used to specify the value in the header field.\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex ed81407..2ff4e54 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -9,6 +9,7 @@\n #include \"quote.h\"\n #include \"parse-options.h\"\n #include \"remote.h\"\n+#include \"color.h\"\n \n /* Quoting styles */\n #define QUOTE_NONE 0\n@@ -76,6 +77,7 @@ static struct {\n \t{ \"symref\" },\n \t{ \"flag\" },\n \t{ \"HEAD\" },\n+\t{ \"color\" },\n };\n \n /*\n@@ -662,8 +664,13 @@ static void populate_value(struct refinfo *ref)\n \t\t\t    !branch->merge[0]->dst)\n \t\t\t\tcontinue;\n \t\t\trefname = branch->merge[0]->dst;\n-\t\t}\n-\t\telse if (!strcmp(name, \"flag\")) {\n+\t\t} else if (!prefixcmp(name, \"color:\")) {\n+\t\t\tchar color[COLOR_MAXLEN] = \"\";\n+\n+\t\t\tcolor_parse(name + 6, \"--format\", color);\n+\t\t\tv->s = xstrdup(color);\n+\t\t\tcontinue;\n+\t\t} else if (!strcmp(name, \"flag\")) {\n \t\t\tchar buf[256], *cp = buf;\n \t\t\tif (ref->flag & REF_ISSYMREF)\n \t\t\t\tcp = copy_advance(cp, \",symref\");\ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 9d874fd..35ca991 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -338,6 +338,20 @@ test_expect_success 'Check for invalid refname format' '\n \ttest_must_fail git for-each-ref --format=\"%(refname:INVALID)\"\n '\n \n+get_color ()\n+{\n+\tgit config --get-color no.such.slot \"$1\"\n+}\n+\n+cat >expected <<EOF\n+$(get_color red)$(git rev-parse --short HEAD)$(get_color reset) origin/master\n+EOF\n+\n+test_expect_success 'Check %(color:...) ' '\n+\tgit for-each-ref --format=\"%(color:red)%(objectname:short)%(color:reset) %(upstream:short)\" refs/heads >actual &&\n+\ttest_cmp expected actual\n+'\n+\n cat >expected <<\\EOF\n heads/master\n tags/master\n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230678","messageId":"1384513148-22633-7-git-send-email-artagnon@gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-1-git-send-email-artagnon@gmail.com","subject":"[PATCH v3 6/6] for-each-ref: auto reset color after each atom","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-15T10:59:08Z","receivedAt":"2013-11-15T10:59:08Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"It makes it easier to write a sensible format string, since you don't\nhave to %(color:reset) after each atom. Additionally, to make sure that\nan invocation like the following doesn't leak color,\n\n  $ git for-each-ref --format='%(subject)%(color:green)'\n\nauto-reset at the end of the format string as well.\n\nSigned-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n---\n builtin/for-each-ref.c  | 22 ++++++++++++++++++----\n t/t6300-for-each-ref.sh |  2 +-\n 2 files changed, 19 insertions(+), 5 deletions(-)\n\ndiff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\nindex 2ff4e54..1050aea 100644\n--- a/builtin/for-each-ref.c\n+++ b/builtin/for-each-ref.c\n@@ -23,6 +23,7 @@ typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type;\n struct atom_value {\n \tconst char *s;\n \tunsigned long ul; /* used for sorting when not FIELD_STR */\n+\tint color : 1;\n };\n \n struct ref_sort {\n@@ -669,6 +670,7 @@ static void populate_value(struct refinfo *ref)\n \n \t\t\tcolor_parse(name + 6, \"--format\", color);\n \t\t\tv->s = xstrdup(color);\n+\t\t\tv->color = 1;\n \t\t\tcontinue;\n \t\t} else if (!strcmp(name, \"flag\")) {\n \t\t\tchar buf[256], *cp = buf;\n@@ -914,11 +916,9 @@ static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs\n \tqsort(refs, num_refs, sizeof(struct refinfo *), compare_refs);\n }\n \n-static void print_value(struct refinfo *ref, int atom, int quote_style)\n+static void print_value(struct atom_value *v, int quote_style)\n {\n-\tstruct atom_value *v;\n \tstruct strbuf sb = STRBUF_INIT;\n-\tget_value(ref, atom, &v);\n \tswitch (quote_style) {\n \tcase QUOTE_NONE:\n \t\tfputs(v->s, stdout);\n@@ -983,17 +983,31 @@ static void emit(const char *cp, const char *ep)\n static void show_ref(struct refinfo *info, const char *format, int quote_style)\n {\n \tconst char *cp, *sp, *ep;\n+\tstruct atom_value *atomv, resetv;\n+\tint reset_color = 0;\n+\tchar color[COLOR_MAXLEN] = \"\";\n \n+\tcolor_parse(\"reset\", \"--format\", color);\n+\tresetv.s = color;\n \tfor (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) {\n \t\tep = strchr(sp, ')');\n \t\tif (cp < sp)\n \t\t\temit(cp, sp);\n-\t\tprint_value(info, parse_atom(sp + 2, ep), quote_style);\n+\t\tget_value(info, parse_atom(sp + 2, ep), &atomv);\n+\t\tprint_value(atomv, quote_style);\n+\t\tif (reset_color) {\n+\t\t\tprint_value(&resetv, quote_style);\n+\t\t\treset_color = 0;\n+\t\t}\n+\t\tif (atomv->color)\n+\t\t\treset_color = 1;\n \t}\n \tif (*cp) {\n \t\tsp = cp + strlen(cp);\n \t\temit(cp, sp);\n \t}\n+\tif (reset_color)\n+\t\tprint_value(&resetv, quote_style);\n \tputchar('\\n');\n }\n \ndiff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\nindex 35ca991..2bf2bea 100755\n--- a/t/t6300-for-each-ref.sh\n+++ b/t/t6300-for-each-ref.sh\n@@ -348,7 +348,7 @@ $(get_color red)$(git rev-parse --short HEAD)$(get_color reset) origin/master\n EOF\n \n test_expect_success 'Check %(color:...) ' '\n-\tgit for-each-ref --format=\"%(color:red)%(objectname:short)%(color:reset) %(upstream:short)\" refs/heads >actual &&\n+\tgit for-each-ref --format=\"%(color:red)%(objectname:short) %(upstream:short)\" refs/heads >actual &&\n \ttest_cmp expected actual\n '\n \n-- \n1.8.5.rc0.6.gfd75b41\n"},{"id":"230715","messageId":"CAPig+cRz3_JSLJCu5AndSnreT91OqcQf_p+9-bUE5MUO_E066g@mail.gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-2-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH v3 1/6] t6300 (for-each-ref): clearly demarcate setup","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-11-16T23:11:48Z","receivedAt":"2013-11-16T23:11:48Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Nov 15, 2013 at 5:59 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Condense the two-step setup into one step, and give it an appropriate\n> name.\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  t/t6300-for-each-ref.sh | 5 +----\n>  1 file changed, 1 insertion(+), 4 deletions(-)\n>\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 752f5cb..72d282f 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -18,16 +18,13 @@ setdate_and_increment () {\n>      export GIT_COMMITTER_DATE GIT_AUTHOR_DATE\n>  }\n>\n> -test_expect_success 'Create sample commit with known timestamp' '\n> +test_expect_success setup '\n>         setdate_and_increment &&\n>         echo \"Using $datestamp\" > one &&\n>         git add one &&\n>         git commit -m \"Initial\" &&\n>         setdate_and_increment &&\n>         git tag -a -m \"Tagging at $datestamp\" testtag\n\nBroken &&-chain here after these functions are combined.\n\n> -'\n> -\n> -test_expect_success 'Create upstream config' '\n>         git update-ref refs/remotes/origin/master master &&\n>         git remote add origin nowhere &&\n>         git config branch.master.remote origin &&\n> --\n> 1.8.5.rc0.6.gfd75b41\n"},{"id":"230716","messageId":"CAPig+cRkaQAZm76tzuPMnddaZE2qaN=yOF8O9dSaB4pzbBq9ww@mail.gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-4-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH v3 3/6] for-each-ref: introduce %(HEAD) asterisk marker","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-11-16T23:23:23Z","receivedAt":"2013-11-16T23:23:23Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Nov 15, 2013 at 5:59 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> 'git branch' shows which branch you are currently on with an '*', but\n> 'git for-each-ref' misses this feature.  So, extend its format with\n> %(HEAD) for the same effect.\n>\n> Now you can use the following format in for-each-ref:\n>\n>   %(HEAD) %(refname:short)\n>\n> to display an asterisk next to the current ref.\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  4 ++++\n>  builtin/for-each-ref.c             | 13 +++++++++++--\n>  t/t6300-for-each-ref.sh            |  2 ++\n>  3 files changed, 17 insertions(+), 2 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index f2e08d1..ab3da0e 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -93,6 +93,10 @@ upstream::\n>         from the displayed ref. Respects `:short` in the same way as\n>         `refname` above.\n>\n> +HEAD::\n> +       Used to indicate the currently checked out branch.  Is '*' if\n> +       HEAD points to the current ref, and ' ' otherwise.\n\n\"Used to\" sounds like it is explaining something it did in the past.\nNone of the other field names are described in this fashion. Perhaps\nsomething simpler along the lines of:\n\n    Asterisk \"*\" if HEAD matches the current ref (the checked out branch),\n    otherwise space \" \".\n\n> +\n>  In addition to the above, for commit and tag objects, the header\n>  field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n>  be used to specify the value in the header field.\n"},{"id":"230717","messageId":"CAPig+cRqwOgE8by8DwuGc1nNA3C_bA07ob-GuRzETKoRPBe+CQ@mail.gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-5-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH v3 4/6] for-each-ref: introduce %(upstream:track[short])","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-11-16T23:53:04Z","receivedAt":"2013-11-16T23:53:04Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Nov 15, 2013 at 5:59 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Introduce %(upstream:track) to display \"[ahead M, behind N]\" and\n> %(upstream:trackshort) to display \"=\", \">\", \"<\", or \"<>\"\n> appropriately (inspired by contrib/completion/git-prompt.sh).\n>\n> Now you can use the following format in for-each-ref:\n>\n>   %(refname:short)%(upstream:trackshort)\n>\n> to display refs with terse tracking information.\n>\n> Note that :track and :trackshort only work with \"upstream\", and error\n> out when used with anything else.\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  6 +++++-\n>  builtin/for-each-ref.c             | 40 +++++++++++++++++++++++++++++++++++---\n>  t/t6300-for-each-ref.sh            | 22 +++++++++++++++++++++\n>  3 files changed, 64 insertions(+), 4 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index ab3da0e..c9b192e 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -91,7 +91,11 @@ objectname::\n>  upstream::\n>         The name of a local ref which can be considered ``upstream''\n>         from the displayed ref. Respects `:short` in the same way as\n> -       `refname` above.\n> +       `refname` above.  Additionally respects `:track` to show\n> +       \"[ahead N, behind M]\" and `:trackshort` to show the terse\n> +       version (like the prompt) \">\", \"<\", \"<>\", or \"=\".  Has no\n\nThe \"prompt\" is not mentioned elsewhere in for-each-ref documentation,\nand a person not familiar with contrib/completion/ may be confused by\nthis reference. It might make sense instead to explain the meanings of\n\">\", \"<\", \"<>\", and \"=\" directly since they are not necessarily\nobvious to the casual reader.\n\n> +       effect if the ref does not have tracking information\n> +       associated with it.\n>\n>  HEAD::\n>         Used to indicate the currently checked out branch.  Is '*' if\n> diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n> index 5f1842f..ed81407 100644\n> --- a/builtin/for-each-ref.c\n> +++ b/builtin/for-each-ref.c\n> @@ -689,13 +690,46 @@ static void populate_value(struct refinfo *ref)\n>                         continue;\n>\n>                 formatp = strchr(name, ':');\n> -               /* look for \"short\" refname format */\n>                 if (formatp) {\n> +                       int num_ours, num_theirs;\n> +\n>                         formatp++;\n>                         if (!strcmp(formatp, \"short\"))\n>                                 refname = shorten_unambiguous_ref(refname,\n>                                                       warn_ambiguous_refs);\n> -                       else\n> +                       else if (!strcmp(formatp, \"track\") &&\n> +                               !prefixcmp(name, \"upstream\")) {\n> +                               char buf[40];\n> +\n> +                               stat_tracking_info(branch, &num_ours, &num_theirs);\n> +                               if (!num_ours && !num_theirs)\n> +                                       v->s = \"\";\n> +                               else if (!num_ours) {\n> +                                       sprintf(buf, \"[behind %d]\", num_theirs);\n> +                                       v->s = xstrdup(buf);\n> +                               } else if (!num_theirs) {\n> +                                       sprintf(buf, \"[ahead %d]\", num_ours);\n> +                                       v->s = xstrdup(buf);\n> +                               } else {\n> +                                       sprintf(buf, \"[ahead %d, behind %d]\",\n\nIs the intention that these strings (\"[ahead %d]\", etc.) will be\ninternationalized in the future? If so, the allocated 40-character\nbuffer may be insufficient.\n\n> +                                               num_ours, num_theirs);\n> +                                       v->s = xstrdup(buf);\n> +                               }\n> +                               continue;\n> +                       } else if (!strcmp(formatp, \"trackshort\") &&\n> +                               !prefixcmp(name, \"upstream\")) {\n> +\n> +                               stat_tracking_info(branch, &num_ours, &num_theirs);\n> +                               if (!num_ours && !num_theirs)\n> +                                       v->s = \"=\";\n> +                               else if (!num_ours)\n> +                                       v->s = \"<\";\n> +                               else if (!num_theirs)\n> +                                       v->s = \">\";\n> +                               else\n> +                                       v->s = \"<>\";\n> +                               continue;\n> +                       } else\n>                                 die(\"unknown %.*s format %s\",\n>                                     (int)(formatp - name), name, formatp);\n\nIs it still accurate to call this a \"format\" in the error message?\n'track' and 'trackshort' seem more like decorations.\n\n>                 }\n> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n> index 5e29ffc..9d874fd 100755\n> --- a/t/t6300-for-each-ref.sh\n> +++ b/t/t6300-for-each-ref.sh\n> @@ -303,6 +303,28 @@ test_expect_success 'Check short upstream format' '\n>         test_cmp expected actual\n>  '\n>\n> +test_expect_success 'setup for upstream:track[short]' '\n> +       test_commit two\n> +'\n> +\n> +cat >expected <<EOF\n> +[ahead 1]\n> +EOF\n> +\n> +test_expect_success 'Check upstream:track format' '\n> +       git for-each-ref --format=\"%(upstream:track)\" refs/heads >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n> +cat >expected <<EOF\n> +>\n> +EOF\n> +\n> +test_expect_success 'Check upstream:trackshort format' '\n> +       git for-each-ref --format=\"%(upstream:trackshort)\" refs/heads >actual &&\n> +       test_cmp expected actual\n> +'\n> +\n>  cat >expected <<EOF\n>  $(git rev-parse --short HEAD)\n>  EOF\n\nWould it make sense also to add tests verifying that :track and\n:trackshort correctly fail when applied to a key other than\n\"upstream\"?\n"},{"id":"230718","messageId":"CAPig+cT2LYTRwVfkNEdH4U+eJ4STcRd3tKYMDekb239qLw=zvA@mail.gmail.com","threadId":"35344","inReplyTo":"1384513148-22633-6-git-send-email-artagnon@gmail.com","subject":"Re: [PATCH v3 5/6] for-each-ref: introduce %(color:...) for color","fromName":"Eric Sunshine","fromEmail":"sunshine@sunshineco.com","sentAt":"2013-11-17T00:04:44Z","receivedAt":"2013-11-17T00:04:44Z","isPatch":true,"sender":{"key":"sunshine@sunshineco.com","avatar":"https://avatars.githubusercontent.com/u/163641?v=4"},"body":"On Fri, Nov 15, 2013 at 5:59 AM, Ramkumar Ramachandra\n<artagnon@gmail.com> wrote:\n> Enhance 'git for-each-ref' with color formatting options.  You can now\n> use the following format in for-each-ref:\n>\n>   %(color:green)%(refname:short)%(color:reset)\n>\n> Signed-off-by: Ramkumar Ramachandra <artagnon@gmail.com>\n> ---\n>  Documentation/git-for-each-ref.txt |  4 ++++\n>  builtin/for-each-ref.c             | 11 +++++++++--\n>  t/t6300-for-each-ref.sh            | 14 ++++++++++++++\n>  3 files changed, 27 insertions(+), 2 deletions(-)\n>\n> diff --git a/Documentation/git-for-each-ref.txt b/Documentation/git-for-each-ref.txt\n> index c9b192e..2f3ac22 100644\n> --- a/Documentation/git-for-each-ref.txt\n> +++ b/Documentation/git-for-each-ref.txt\n> @@ -101,6 +101,10 @@ HEAD::\n>         Used to indicate the currently checked out branch.  Is '*' if\n>         HEAD points to the current ref, and ' ' otherwise.\n>\n> +color::\n> +       Used to change color of output.  Followed by `:<colorname>`,\n> +       where names are described in `color.branch.*`.\n\n\"Used to\" sounds past tense. Perhaps:\n\n    color:<colorname>::\n      Change output color to `<colorname>`, where...\n\n>  In addition to the above, for commit and tag objects, the header\n>  field names (`tree`, `parent`, `object`, `type`, and `tag`) can\n>  be used to specify the value in the header field.\n"},{"id":"230722","messageId":"CALkWK0=3=a5hiU7zxDAk=077CePkmN+MaLZFcuYdeZJM8QXApw@mail.gmail.com","threadId":"35344","inReplyTo":"CAPig+cRqwOgE8by8DwuGc1nNA3C_bA07ob-GuRzETKoRPBe+CQ@mail.gmail.com","subject":"Re: [PATCH v3 4/6] for-each-ref: introduce %(upstream:track[short])","fromName":"Ramkumar Ramachandra","fromEmail":"artagnon@gmail.com","sentAt":"2013-11-17T05:58:28Z","receivedAt":"2013-11-17T05:58:28Z","isPatch":true,"sender":{"key":"r@artagnon.com","avatar":"https://avatars.githubusercontent.com/u/37226?v=4"},"body":"Eric Sunshine wrote:\n> The \"prompt\" is not mentioned elsewhere in for-each-ref documentation,\n> and a person not familiar with contrib/completion/ may be confused by\n> this reference. It might make sense instead to explain the meanings of\n> \">\", \"<\", \"<>\", and \"=\" directly since they are not necessarily\n> obvious to the casual reader.\n\nSomeone else raised the same point earlier. Okay, I'll elaborate.\n\n>> diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c\n>> index 5f1842f..ed81407 100644\n>> --- a/builtin/for-each-ref.c\n>> +++ b/builtin/for-each-ref.c\n>> @@ -689,13 +690,46 @@ static void populate_value(struct refinfo *ref)\n>>                         continue;\n>>\n>>                 formatp = strchr(name, ':');\n>> -               /* look for \"short\" refname format */\n>>                 if (formatp) {\n>> +                       int num_ours, num_theirs;\n>> +\n>>                         formatp++;\n>>                         if (!strcmp(formatp, \"short\"))\n>>                                 refname = shorten_unambiguous_ref(refname,\n>>                                                       warn_ambiguous_refs);\n>> -                       else\n>> +                       else if (!strcmp(formatp, \"track\") &&\n>> +                               !prefixcmp(name, \"upstream\")) {\n>> +                               char buf[40];\n>> +\n>> +                               stat_tracking_info(branch, &num_ours, &num_theirs);\n>> +                               if (!num_ours && !num_theirs)\n>> +                                       v->s = \"\";\n>> +                               else if (!num_ours) {\n>> +                                       sprintf(buf, \"[behind %d]\", num_theirs);\n>> +                                       v->s = xstrdup(buf);\n>> +                               } else if (!num_theirs) {\n>> +                                       sprintf(buf, \"[ahead %d]\", num_ours);\n>> +                                       v->s = xstrdup(buf);\n>> +                               } else {\n>> +                                       sprintf(buf, \"[ahead %d, behind %d]\",\n>\n> Is the intention that these strings (\"[ahead %d]\", etc.) will be\n> internationalized in the future? If so, the allocated 40-character\n> buffer may be insufficient.\n\nSimilar strings in wt-status.c aren't ready for internationalization\neither. Besides, these xstrdup() calls leak memory and are quite\nsuboptimal: if I allocate more memory, I'm going to be leaking more;\nso, I'd defer the internationalization discussion until we restructure\nthis.\n\n>> +                                               num_ours, num_theirs);\n>> +                                       v->s = xstrdup(buf);\n>> +                               }\n>> +                               continue;\n>> +                       } else if (!strcmp(formatp, \"trackshort\") &&\n>> +                               !prefixcmp(name, \"upstream\")) {\n>> +\n>> +                               stat_tracking_info(branch, &num_ours, &num_theirs);\n>> +                               if (!num_ours && !num_theirs)\n>> +                                       v->s = \"=\";\n>> +                               else if (!num_ours)\n>> +                                       v->s = \"<\";\n>> +                               else if (!num_theirs)\n>> +                                       v->s = \">\";\n>> +                               else\n>> +                                       v->s = \"<>\";\n>> +                               continue;\n>> +                       } else\n>>                                 die(\"unknown %.*s format %s\",\n>>                                     (int)(formatp - name), name, formatp);\n>\n> Is it still accurate to call this a \"format\" in the error message?\n> 'track' and 'trackshort' seem more like decorations.\n\nBy convention, all f-e-r formatting errors are reported as fatal\nerrors (see color errors too, for instance), unlike pretty-formats. We\nmight want to change this in a later series.\n\n>> diff --git a/t/t6300-for-each-ref.sh b/t/t6300-for-each-ref.sh\n>> index 5e29ffc..9d874fd 100755\n>> --- a/t/t6300-for-each-ref.sh\n>> +++ b/t/t6300-for-each-ref.sh\n>> @@ -303,6 +303,28 @@ test_expect_success 'Check short upstream format' '\n>>         test_cmp expected actual\n>>  '\n>>\n>> +test_expect_success 'setup for upstream:track[short]' '\n>> +       test_commit two\n>> +'\n>> +\n>> +cat >expected <<EOF\n>> +[ahead 1]\n>> +EOF\n>> +\n>> +test_expect_success 'Check upstream:track format' '\n>> +       git for-each-ref --format=\"%(upstream:track)\" refs/heads >actual &&\n>> +       test_cmp expected actual\n>> +'\n>> +\n>> +cat >expected <<EOF\n>> +>\n>> +EOF\n>> +\n>> +test_expect_success 'Check upstream:trackshort format' '\n>> +       git for-each-ref --format=\"%(upstream:trackshort)\" refs/heads >actual &&\n>> +       test_cmp expected actual\n>> +'\n>> +\n>>  cat >expected <<EOF\n>>  $(git rev-parse --short HEAD)\n>>  EOF\n>\n> Would it make sense also to add tests verifying that :track and\n> :trackshort correctly fail when applied to a key other than\n> \"upstream\"?\n\nSure.\n\nAnd thanks for the detailed review.\n"}]}