From: Junio C Hamano Date: Fri, 6 Dec 2013 19:07:20 +0000 (-0800) Subject: Merge branch 'rr/for-each-ref-decoration' X-Git-Tag: v1.9-rc0~85 X-Git-Url: https://git.lorimer.id.au/gitweb.git/diff_plain/cb6bd5722f430b225b894920fd80975ffa1d0bb5?hp=-c Merge branch 'rr/for-each-ref-decoration' Add a few formatting directives to "git for-each-ref --format=...", to paint them in color, etc. * rr/for-each-ref-decoration: for-each-ref: avoid color leakage for-each-ref: introduce %(color:...) for color for-each-ref: introduce %(upstream:track[short]) for-each-ref: introduce %(HEAD) asterisk marker t6300 (for-each-ref): don't hardcode SHA-1 hexes t6300 (for-each-ref): clearly demarcate setup --- cb6bd5722f430b225b894920fd80975ffa1d0bb5 diff --combined builtin/for-each-ref.c index d096051b15,5ff51d1d32..875bd813d5 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@@ -9,6 -9,7 +9,7 @@@ #include "quote.h" #include "parse-options.h" #include "remote.h" + #include "color.h" /* Quoting styles */ #define QUOTE_NONE 0 @@@ -75,6 -76,8 +76,8 @@@ static struct { "upstream" }, { "symref" }, { "flag" }, + { "HEAD" }, + { "color" }, }; /* @@@ -90,6 -93,7 +93,7 @@@ static const char **used_atom; static cmp_type *used_atom_type; static int used_atom_cnt, sort_atom_limit, need_tagged, need_symref; + static int need_color_reset_at_eol; /* * Used to parse format string and sort specifiers @@@ -176,13 -180,21 +180,21 @@@ static const char *find_next(const cha static int verify_format(const char *format) { const char *cp, *sp; + static const char color_reset[] = "color:reset"; + + need_color_reset_at_eol = 0; for (cp = format; *cp && (sp = find_next(cp)); ) { const char *ep = strchr(sp, ')'); + int at; + if (!ep) return error("malformed format string %s", sp); /* sp points at "%(" and ep points at the closing ")" */ - parse_atom(sp + 2, ep); + at = parse_atom(sp + 2, ep); cp = ep + 1; + + if (!memcmp(used_atom[at], "color:", 6)) + need_color_reset_at_eol = !!strcmp(used_atom[at], color_reset); } return 0; } @@@ -205,22 -217,6 +217,22 @@@ static void *get_obj(const unsigned cha return buf; } +static int grab_objectname(const char *name, const unsigned char *sha1, + struct atom_value *v) +{ + if (!strcmp(name, "objectname")) { + char *s = xmalloc(41); + strcpy(s, sha1_to_hex(sha1)); + v->s = s; + return 1; + } + if (!strcmp(name, "objectname:short")) { + v->s = xstrdup(find_unique_abbrev(sha1, DEFAULT_ABBREV)); + return 1; + } + return 0; +} + /* See grab_values */ static void grab_common_values(struct atom_value *val, int deref, struct object *obj, void *buf, unsigned long sz) { @@@ -241,8 -237,15 +253,8 @@@ v->ul = sz; v->s = s; } - else if (!strcmp(name, "objectname")) { - char *s = xmalloc(41); - strcpy(s, sha1_to_hex(obj->sha1)); - v->s = s; - } - else if (!strcmp(name, "objectname:short")) { - v->s = xstrdup(find_unique_abbrev(obj->sha1, - DEFAULT_ABBREV)); - } + else if (deref) + grab_objectname(name, obj->sha1, v); } } @@@ -649,6 -652,7 +661,7 @@@ static void populate_value(struct refin int deref = 0; const char *refname; const char *formatp; + struct branch *branch = NULL; if (*name == '*') { deref = 1; @@@ -660,7 -664,6 +673,6 @@@ 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; @@@ -670,8 -673,13 +682,13 @@@ !branch->merge[0]->dst) continue; refname = branch->merge[0]->dst; - } - else if (!strcmp(name, "flag")) { + } else if (!prefixcmp(name, "color:")) { + char color[COLOR_MAXLEN] = ""; + + color_parse(name + 6, "--format", color); + v->s = xstrdup(color); + continue; + } else if (!strcmp(name, "flag")) { char buf[256], *cp = buf; if (ref->flag & REF_ISSYMREF) cp = copy_advance(cp, ",symref"); @@@ -684,20 -692,60 +701,62 @@@ v->s = xstrdup(buf + 1); } continue; - } - else if (!deref && grab_objectname(name, ref->objectname, v)) ++ } else if (!deref && grab_objectname(name, ref->objectname, v)) { + 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, ':'); - /* 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); } @@@ -875,11 -923,9 +934,9 @@@ static void sort_refs(struct ref_sort * qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs); } - static void print_value(struct refinfo *ref, int atom, int quote_style) + static void print_value(struct atom_value *v, int quote_style) { - struct atom_value *v; struct strbuf sb = STRBUF_INIT; - get_value(ref, atom, &v); switch (quote_style) { case QUOTE_NONE: fputs(v->s, stdout); @@@ -946,15 -992,26 +1003,26 @@@ static void show_ref(struct refinfo *in const char *cp, *sp, *ep; for (cp = format; *cp && (sp = find_next(cp)); cp = ep + 1) { + struct atom_value *atomv; + ep = strchr(sp, ')'); if (cp < sp) emit(cp, sp); - print_value(info, parse_atom(sp + 2, ep), quote_style); + get_value(info, parse_atom(sp + 2, ep), &atomv); + print_value(atomv, quote_style); } if (*cp) { sp = cp + strlen(cp); emit(cp, sp); } + if (need_color_reset_at_eol) { + struct atom_value resetv; + char color[COLOR_MAXLEN] = ""; + + color_parse("reset", "--format", color); + resetv.s = color; + print_value(&resetv, quote_style); + } putchar('\n'); } diff --combined t/t6300-for-each-ref.sh index da5fb6c917,46866bab01..bda354c1c4 --- a/t/t6300-for-each-ref.sh +++ b/t/t6300-for-each-ref.sh @@@ -18,16 -18,13 +18,13 @@@ setdate_and_increment () export GIT_COMMITTER_DATE GIT_AUTHOR_DATE } - test_expect_success 'Create sample commit with known timestamp' ' + test_expect_success setup ' setdate_and_increment && echo "Using $datestamp" > one && git add one && git commit -m "Initial" && setdate_and_increment && - git tag -a -m "Tagging at $datestamp" testtag - ' - - test_expect_success 'Create upstream config' ' + git tag -a -m "Tagging at $datestamp" testtag && git update-ref refs/remotes/origin/master master && git remote add origin nowhere && git config branch.master.remote origin && @@@ -52,14 -49,12 +49,14 @@@ test_atom head refname refs/heads/maste test_atom head upstream refs/remotes/origin/master test_atom head objecttype commit test_atom head objectsize 171 - test_atom head objectname 67a36f10722846e891fbada1ba48ed035de75581 - test_atom head tree 0e51c00fcb93dffc755546f27593d511e1bdb46f + test_atom head objectname $(git rev-parse refs/heads/master) + test_atom head tree $(git rev-parse refs/heads/master^{tree}) test_atom head parent '' test_atom head numparent 0 test_atom head object '' test_atom head type '' +test_atom head '*objectname' '' +test_atom head '*objecttype' '' test_atom head author 'A U Thor 1151939924 +0200' test_atom head authorname 'A U Thor' test_atom head authoremail '' @@@ -82,19 -77,18 +79,20 @@@ test_atom head contents:body ' test_atom head contents:signature '' test_atom head contents 'Initial ' + test_atom head HEAD '*' test_atom tag refname refs/tags/testtag test_atom tag upstream '' test_atom tag objecttype tag test_atom tag objectsize 154 - test_atom tag objectname 98b46b1d36e5b07909de1b3886224e3e81e87322 + test_atom tag objectname $(git rev-parse refs/tags/testtag) test_atom tag tree '' test_atom tag parent '' test_atom tag numparent '' - test_atom tag object '67a36f10722846e891fbada1ba48ed035de75581' + test_atom tag object $(git rev-parse refs/tags/testtag^0) test_atom tag type 'commit' +test_atom tag '*objectname' '67a36f10722846e891fbada1ba48ed035de75581' +test_atom tag '*objecttype' 'commit' test_atom tag author '' test_atom tag authorname '' test_atom tag authoremail '' @@@ -117,6 -111,7 +115,7 @@@ test_atom tag contents:body ' test_atom tag contents:signature '' test_atom tag contents 'Tagging at 1151939927 ' + test_atom tag HEAD ' ' test_expect_success 'Check invalid atoms names are errors' ' test_must_fail git for-each-ref --format="%(INVALID)" refs/heads @@@ -308,8 -303,35 +307,35 @@@ test_expect_success 'Check short upstre test_cmp expected actual ' + test_expect_success 'setup for upstream:track[short]' ' + test_commit two + ' + + cat >expected <actual && + test_cmp expected actual + ' + + cat >expected < + 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 <expected <actual && + test_cmp expected actual + ' + cat >expected <<\EOF heads/master tags/master @@@ -460,9 -499,9 +503,9 @@@ test_atom refs/tags/signed-long content body contents $sig" - cat >expected <<\EOF - 408fe76d02a785a006c2e9c669b7be5589ede96d refs/tags/master - 90b5ebede4899eda64893bc2a4c8f1d6fb6dfc40 refs/tags/bogo + cat >expected < refs/tags/master + $(git rev-parse refs/tags/bogo) refs/tags/bogo EOF test_expect_success 'Verify sort with multiple keys' '