Merge branch 'jk/blame-line-porcelain'
* jk/blame-line-porcelain: blame: add --line-porcelain output format blame: refactor porcelain output add tests for various blame formats
This commit is contained in:
commit
b7aba2ef34
@ -52,6 +52,11 @@ of lines before or after the line given by <start>.
|
|||||||
--porcelain::
|
--porcelain::
|
||||||
Show in a format designed for machine consumption.
|
Show in a format designed for machine consumption.
|
||||||
|
|
||||||
|
--line-porcelain::
|
||||||
|
Show the porcelain format, but output commit information for
|
||||||
|
each line, not just the first time a commit is referenced.
|
||||||
|
Implies --porcelain.
|
||||||
|
|
||||||
--incremental::
|
--incremental::
|
||||||
Show the result incrementally in a format designed for
|
Show the result incrementally in a format designed for
|
||||||
machine consumption.
|
machine consumption.
|
||||||
|
@ -105,6 +105,19 @@ The contents of the actual line is output after the above
|
|||||||
header, prefixed by a TAB. This is to allow adding more
|
header, prefixed by a TAB. This is to allow adding more
|
||||||
header elements later.
|
header elements later.
|
||||||
|
|
||||||
|
The porcelain format generally suppresses commit information that has
|
||||||
|
already been seen. For example, two lines that are blamed to the same
|
||||||
|
commit will both be shown, but the details for that commit will be shown
|
||||||
|
only once. This is more efficient, but may require more state be kept by
|
||||||
|
the reader. The `--line-porcelain` option can be used to output full
|
||||||
|
commit information for each line, allowing simpler (but less efficient)
|
||||||
|
usage like:
|
||||||
|
|
||||||
|
# count the number of lines attributed to each author
|
||||||
|
git blame --line-porcelain file |
|
||||||
|
sed -n 's/^author //p' |
|
||||||
|
sort | uniq -c | sort -rn
|
||||||
|
|
||||||
|
|
||||||
SPECIFYING RANGES
|
SPECIFYING RANGES
|
||||||
-----------------
|
-----------------
|
||||||
|
@ -1484,13 +1484,14 @@ static void write_filename_info(const char *path)
|
|||||||
/*
|
/*
|
||||||
* Porcelain/Incremental format wants to show a lot of details per
|
* Porcelain/Incremental format wants to show a lot of details per
|
||||||
* commit. Instead of repeating this every line, emit it only once,
|
* commit. Instead of repeating this every line, emit it only once,
|
||||||
* the first time each commit appears in the output.
|
* the first time each commit appears in the output (unless the
|
||||||
|
* user has specifically asked for us to repeat).
|
||||||
*/
|
*/
|
||||||
static int emit_one_suspect_detail(struct origin *suspect)
|
static int emit_one_suspect_detail(struct origin *suspect, int repeat)
|
||||||
{
|
{
|
||||||
struct commit_info ci;
|
struct commit_info ci;
|
||||||
|
|
||||||
if (suspect->commit->object.flags & METAINFO_SHOWN)
|
if (!repeat && (suspect->commit->object.flags & METAINFO_SHOWN))
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
suspect->commit->object.flags |= METAINFO_SHOWN;
|
suspect->commit->object.flags |= METAINFO_SHOWN;
|
||||||
@ -1529,7 +1530,7 @@ static void found_guilty_entry(struct blame_entry *ent)
|
|||||||
printf("%s %d %d %d\n",
|
printf("%s %d %d %d\n",
|
||||||
sha1_to_hex(suspect->commit->object.sha1),
|
sha1_to_hex(suspect->commit->object.sha1),
|
||||||
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
|
ent->s_lno + 1, ent->lno + 1, ent->num_lines);
|
||||||
emit_one_suspect_detail(suspect);
|
emit_one_suspect_detail(suspect, 0);
|
||||||
write_filename_info(suspect->path);
|
write_filename_info(suspect->path);
|
||||||
maybe_flush_or_die(stdout, "stdout");
|
maybe_flush_or_die(stdout, "stdout");
|
||||||
}
|
}
|
||||||
@ -1618,9 +1619,19 @@ static const char *format_time(unsigned long time, const char *tz_str,
|
|||||||
#define OUTPUT_SHOW_SCORE 0100
|
#define OUTPUT_SHOW_SCORE 0100
|
||||||
#define OUTPUT_NO_AUTHOR 0200
|
#define OUTPUT_NO_AUTHOR 0200
|
||||||
#define OUTPUT_SHOW_EMAIL 0400
|
#define OUTPUT_SHOW_EMAIL 0400
|
||||||
|
#define OUTPUT_LINE_PORCELAIN 01000
|
||||||
|
|
||||||
static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
|
static void emit_porcelain_details(struct origin *suspect, int repeat)
|
||||||
{
|
{
|
||||||
|
if (emit_one_suspect_detail(suspect, repeat) ||
|
||||||
|
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
|
||||||
|
write_filename_info(suspect->path);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent,
|
||||||
|
int opt)
|
||||||
|
{
|
||||||
|
int repeat = opt & OUTPUT_LINE_PORCELAIN;
|
||||||
int cnt;
|
int cnt;
|
||||||
const char *cp;
|
const char *cp;
|
||||||
struct origin *suspect = ent->suspect;
|
struct origin *suspect = ent->suspect;
|
||||||
@ -1633,17 +1644,18 @@ static void emit_porcelain(struct scoreboard *sb, struct blame_entry *ent)
|
|||||||
ent->s_lno + 1,
|
ent->s_lno + 1,
|
||||||
ent->lno + 1,
|
ent->lno + 1,
|
||||||
ent->num_lines);
|
ent->num_lines);
|
||||||
if (emit_one_suspect_detail(suspect) ||
|
emit_porcelain_details(suspect, repeat);
|
||||||
(suspect->commit->object.flags & MORE_THAN_ONE_PATH))
|
|
||||||
write_filename_info(suspect->path);
|
|
||||||
|
|
||||||
cp = nth_line(sb, ent->lno);
|
cp = nth_line(sb, ent->lno);
|
||||||
for (cnt = 0; cnt < ent->num_lines; cnt++) {
|
for (cnt = 0; cnt < ent->num_lines; cnt++) {
|
||||||
char ch;
|
char ch;
|
||||||
if (cnt)
|
if (cnt) {
|
||||||
printf("%s %d %d\n", hex,
|
printf("%s %d %d\n", hex,
|
||||||
ent->s_lno + 1 + cnt,
|
ent->s_lno + 1 + cnt,
|
||||||
ent->lno + 1 + cnt);
|
ent->lno + 1 + cnt);
|
||||||
|
if (repeat)
|
||||||
|
emit_porcelain_details(suspect, 1);
|
||||||
|
}
|
||||||
putchar('\t');
|
putchar('\t');
|
||||||
do {
|
do {
|
||||||
ch = *cp++;
|
ch = *cp++;
|
||||||
@ -1756,7 +1768,7 @@ static void output(struct scoreboard *sb, int option)
|
|||||||
|
|
||||||
for (ent = sb->ent; ent; ent = ent->next) {
|
for (ent = sb->ent; ent; ent = ent->next) {
|
||||||
if (option & OUTPUT_PORCELAIN)
|
if (option & OUTPUT_PORCELAIN)
|
||||||
emit_porcelain(sb, ent);
|
emit_porcelain(sb, ent, option);
|
||||||
else {
|
else {
|
||||||
emit_other(sb, ent, option);
|
emit_other(sb, ent, option);
|
||||||
}
|
}
|
||||||
@ -2300,6 +2312,7 @@ int cmd_blame(int argc, const char **argv, const char *prefix)
|
|||||||
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
|
OPT_BIT('f', "show-name", &output_option, "Show original filename (Default: auto)", OUTPUT_SHOW_NAME),
|
||||||
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
|
OPT_BIT('n', "show-number", &output_option, "Show original linenumber (Default: off)", OUTPUT_SHOW_NUMBER),
|
||||||
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
|
OPT_BIT('p', "porcelain", &output_option, "Show in a format designed for machine consumption", OUTPUT_PORCELAIN),
|
||||||
|
OPT_BIT(0, "line-porcelain", &output_option, "Show porcelain format with per-line commit information", OUTPUT_PORCELAIN|OUTPUT_LINE_PORCELAIN),
|
||||||
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
|
OPT_BIT('c', NULL, &output_option, "Use the same output mode as git-annotate (Default: off)", OUTPUT_ANNOTATE_COMPAT),
|
||||||
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
|
OPT_BIT('t', NULL, &output_option, "Show raw timestamp (Default: off)", OUTPUT_RAW_TIMESTAMP),
|
||||||
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
|
OPT_BIT('l', NULL, &output_option, "Show long commit SHA1 (Default: off)", OUTPUT_LONG_OBJECT_NAME),
|
||||||
|
90
t/t8008-blame-formats.sh
Executable file
90
t/t8008-blame-formats.sh
Executable file
@ -0,0 +1,90 @@
|
|||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
test_description='blame output in various formats on a simple case'
|
||||||
|
. ./test-lib.sh
|
||||||
|
|
||||||
|
test_expect_success 'setup' '
|
||||||
|
echo a >file &&
|
||||||
|
git add file
|
||||||
|
test_tick &&
|
||||||
|
git commit -m one &&
|
||||||
|
echo b >>file &&
|
||||||
|
echo c >>file &&
|
||||||
|
echo d >>file &&
|
||||||
|
test_tick &&
|
||||||
|
git commit -a -m two
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<'EOF'
|
||||||
|
^baf5e0b (A U Thor 2005-04-07 15:13:13 -0700 1) a
|
||||||
|
8825379d (A U Thor 2005-04-07 15:14:13 -0700 2) b
|
||||||
|
8825379d (A U Thor 2005-04-07 15:14:13 -0700 3) c
|
||||||
|
8825379d (A U Thor 2005-04-07 15:14:13 -0700 4) d
|
||||||
|
EOF
|
||||||
|
test_expect_success 'normal blame output' '
|
||||||
|
git blame file >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
ID1=baf5e0b3869e0b2b2beb395a3720c7b51eac94fc
|
||||||
|
COMMIT1='author A U Thor
|
||||||
|
author-mail <author@example.com>
|
||||||
|
author-time 1112911993
|
||||||
|
author-tz -0700
|
||||||
|
committer C O Mitter
|
||||||
|
committer-mail <committer@example.com>
|
||||||
|
committer-time 1112911993
|
||||||
|
committer-tz -0700
|
||||||
|
summary one
|
||||||
|
boundary
|
||||||
|
filename file'
|
||||||
|
ID2=8825379dfb8a1267b58e8e5bcf69eec838f685ec
|
||||||
|
COMMIT2='author A U Thor
|
||||||
|
author-mail <author@example.com>
|
||||||
|
author-time 1112912053
|
||||||
|
author-tz -0700
|
||||||
|
committer C O Mitter
|
||||||
|
committer-mail <committer@example.com>
|
||||||
|
committer-time 1112912053
|
||||||
|
committer-tz -0700
|
||||||
|
summary two
|
||||||
|
previous baf5e0b3869e0b2b2beb395a3720c7b51eac94fc file
|
||||||
|
filename file'
|
||||||
|
|
||||||
|
cat >expect <<EOF
|
||||||
|
$ID1 1 1 1
|
||||||
|
$COMMIT1
|
||||||
|
a
|
||||||
|
$ID2 2 2 3
|
||||||
|
$COMMIT2
|
||||||
|
b
|
||||||
|
$ID2 3 3
|
||||||
|
c
|
||||||
|
$ID2 4 4
|
||||||
|
d
|
||||||
|
EOF
|
||||||
|
test_expect_success 'blame --porcelain output' '
|
||||||
|
git blame --porcelain file >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
cat >expect <<EOF
|
||||||
|
$ID1 1 1 1
|
||||||
|
$COMMIT1
|
||||||
|
a
|
||||||
|
$ID2 2 2 3
|
||||||
|
$COMMIT2
|
||||||
|
b
|
||||||
|
$ID2 3 3
|
||||||
|
$COMMIT2
|
||||||
|
c
|
||||||
|
$ID2 4 4
|
||||||
|
$COMMIT2
|
||||||
|
d
|
||||||
|
EOF
|
||||||
|
test_expect_success 'blame --line-porcelain output' '
|
||||||
|
git blame --line-porcelain file >actual &&
|
||||||
|
test_cmp expect actual
|
||||||
|
'
|
||||||
|
|
||||||
|
test_done
|
Loading…
Reference in New Issue
Block a user