blame: add --line-porcelain output format

This is just like --porcelain, except that we always output the commit information for each line, not just the first time it is referenced. This can make quick and dirty scripts much easier to write; see the example added to the blame documentation. Signed-off-by: Jeff King <peff@peff.net> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Jeff King committed May 9, 2011 at 09:34 UTC ed747dd5216ba1fe6fa59da9585dcd6cced202bb
4 files changed +45 -2
Documentation/blame-options.txt
+5
@@ -52,6 +52,11 @@ of lines before or after the line given by <start>.
52 --porcelain::
53 Show in a format designed for machine consumption.
54
55 +--line-porcelain::
56 + Show the porcelain format, but output commit information for
57 + each line, not just the first time a commit is referenced.
58 + Implies --porcelain.
59 +
60 --incremental::
61 Show the result incrementally in a format designed for
62 machine consumption.
Documentation/git-blame.txt
+13
@@ -105,6 +105,19 @@ The contents of the actual line is output after the above
105 header, prefixed by a TAB. This is to allow adding more
106 header elements later.
107
108 +The porcelain format generally suppresses commit information that has
109 +already been seen. For example, two lines that are blamed to the same
110 +commit will both be shown, but the details for that commit will be shown
111 +only once. This is more efficient, but may require more state be kept by
112 +the reader. The `--line-porcelain` option can be used to output full
113 +commit information for each line, allowing simpler (but less efficient)
114 +usage like:
115 +
116 + # count the number of lines attributed to each author
117 + git blame --line-porcelain file |
118 + sed -n 's/^author //p' |
119 + sort | uniq -c | sort -rn
120 +
121
122 SPECIFYING RANGES
123 -----------------
builtin/blame.c
+8 -2
@@ -1619,6 +1619,7 @@ static const char *format_time(unsigned long time, const char *tz_str,
1619 #define OUTPUT_SHOW_SCORE 0100
1620 #define OUTPUT_NO_AUTHOR 0200
1621 #define OUTPUT_SHOW_EMAIL 0400
1622 +#define OUTPUT_LINE_PORCELAIN 01000
1623
1624 static void emit_porcelain_details(struct origin *suspect, int repeat)
1625 {
@@ -1630,6 +1631,7 @@ static void emit_porcelain_details(struct origin *suspect, int repeat)
1631 static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
1632 int opt)
1633 {
1634 + int repeat = opt & OUTPUT_LINE_PORCELAIN;
1635 int cnt;
1636 const char *cp;
1637 struct origin *suspect = ent->suspect;
@@ -1642,15 +1644,18 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
1644 ent->s_lno + 1,
1645 ent->lno + 1,
1646 ent->num_lines);
1645 - emit_porcelain_details(suspect, 0);
1647 + emit_porcelain_details(suspect, repeat);
1648
1649 cp = nth_line(sb, ent->lno);
1650 for (cnt = 0; cnt < ent->num_lines; cnt++) {
1651 char ch;
1650 - if (cnt)
1652 + if (cnt) {
1653 printf("%s %d %d\n", hex,
1654 ent->s_lno + 1 + cnt,
1655 ent->lno + 1 + cnt);
1656 + if (repeat)
1657 + emit_porcelain_details(suspect, 1);
1658 + }
1659 putchar('\t');
1660 do {
1661 ch = *cp++;
@@ -2307,6 +2312,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
2312 OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
2313 OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
2314 OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
2315 + OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
2316 OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
2317 OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
2318 OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
t/t8008-blame-formats.sh
+19
@@ -68,4 +68,23 @@ test_expect_success 'blame --porcelain output' '
68 test_cmp expect actual
69 '
70
71 +cat >expect <<EOF
72 +$ID1 1 1 1
73 +$COMMIT1
74 + a
75 +$ID2 2 2 3
76 +$COMMIT2
77 + b
78 +$ID2 3 3
79 +$COMMIT2
80 + c
81 +$ID2 4 4
82 +$COMMIT2
83 + d
84 +EOF
85 +test_expect_success 'blame --line-porcelain output' '
86 + git blame --line-porcelain file >actual &&
87 + test_cmp expect actual
88 +'
89 +
90 test_done