Skip to content

Commit b5c698d

Browse files
author
Junio C Hamano
committed
git-blame --porcelain
The new option makes the command's native output format to emit output that is easier to handle by Porcelain. Each line is output after a header. The header at the minimum has the first line which has: - 40-byte SHA-1 of the commit the line is attributed to; - the line number of the line in the original file; - the line number of the line in the final file; - on a line that starts a group of line from a different commit than the previous one, the number of lines in this group. On subsequent lines this field is absent. This header line is followed by the following information once for each commit: - author name ("author"), email ("author-mail"), time ("author-time"), and timezone ("author-tz"); similarly for committer. - filename in the commit the line is attributed to. - the first line of the commit log message ("summary"). The contents of the actual line is output after the above header, prefixed by a TAB. This is to allow adding more header elements later. Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent c137f40 commit b5c698d

1 file changed

Lines changed: 127 additions & 22 deletions

File tree

blame.c

Lines changed: 127 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#include "diffcore.h"
1818
#include "revision.h"
1919
#include "xdiff-interface.h"
20+
#include "quote.h"
2021

2122
#define DEBUG 0
2223

@@ -40,6 +41,7 @@ struct util_info {
4041
unsigned long size;
4142
int num_lines;
4243
const char *pathname;
44+
unsigned meta_given:1;
4345

4446
void *topo_data;
4547
};
@@ -332,12 +334,8 @@ static struct util_info *get_util(struct commit *commit)
332334
if (util)
333335
return util;
334336

335-
util = xmalloc(sizeof(struct util_info));
336-
util->buf = NULL;
337-
util->size = 0;
338-
util->line_map = NULL;
337+
util = xcalloc(1, sizeof(struct util_info));
339338
util->num_lines = -1;
340-
util->pathname = NULL;
341339
commit->util = util;
342340
return util;
343341
}
@@ -642,39 +640,99 @@ struct commit_info
642640
char *author_mail;
643641
unsigned long author_time;
644642
char *author_tz;
643+
644+
/* filled only when asked for details */
645+
char *committer;
646+
char *committer_mail;
647+
unsigned long committer_time;
648+
char *committer_tz;
649+
650+
char *summary;
645651
};
646652

647-
static void get_commit_info(struct commit *commit, struct commit_info *ret)
653+
static void get_ac_line(const char *inbuf, const char *what,
654+
int bufsz, char *person, char **mail,
655+
unsigned long *time, char **tz)
648656
{
649657
int len;
650-
char *tmp;
651-
static char author_buf[1024];
652-
653-
tmp = strstr(commit->buffer, "\nauthor ") + 8;
654-
len = strchr(tmp, '\n') - tmp;
655-
ret->author = author_buf;
656-
memcpy(ret->author, tmp, len);
658+
char *tmp, *endp;
659+
660+
tmp = strstr(inbuf, what);
661+
if (!tmp)
662+
goto error_out;
663+
tmp += strlen(what);
664+
endp = strchr(tmp, '\n');
665+
if (!endp)
666+
len = strlen(tmp);
667+
else
668+
len = endp - tmp;
669+
if (bufsz <= len) {
670+
error_out:
671+
/* Ugh */
672+
person = *mail = *tz = "(unknown)";
673+
*time = 0;
674+
return;
675+
}
676+
memcpy(person, tmp, len);
657677

658-
tmp = ret->author;
678+
tmp = person;
659679
tmp += len;
660680
*tmp = 0;
661681
while (*tmp != ' ')
662682
tmp--;
663-
ret->author_tz = tmp+1;
683+
*tz = tmp+1;
664684

665685
*tmp = 0;
666686
while (*tmp != ' ')
667687
tmp--;
668-
ret->author_time = strtoul(tmp, NULL, 10);
688+
*time = strtoul(tmp, NULL, 10);
669689

670690
*tmp = 0;
671691
while (*tmp != ' ')
672692
tmp--;
673-
ret->author_mail = tmp + 1;
674-
693+
*mail = tmp + 1;
675694
*tmp = 0;
676695
}
677696

697+
static void get_commit_info(struct commit *commit, struct commit_info *ret, int detailed)
698+
{
699+
int len;
700+
char *tmp, *endp;
701+
static char author_buf[1024];
702+
static char committer_buf[1024];
703+
static char summary_buf[1024];
704+
705+
ret->author = author_buf;
706+
get_ac_line(commit->buffer, "\nauthor ",
707+
sizeof(author_buf), author_buf, &ret->author_mail,
708+
&ret->author_time, &ret->author_tz);
709+
710+
if (!detailed)
711+
return;
712+
713+
ret->committer = committer_buf;
714+
get_ac_line(commit->buffer, "\ncommitter ",
715+
sizeof(committer_buf), committer_buf, &ret->committer_mail,
716+
&ret->committer_time, &ret->committer_tz);
717+
718+
ret->summary = summary_buf;
719+
tmp = strstr(commit->buffer, "\n\n");
720+
if (!tmp) {
721+
error_out:
722+
sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1));
723+
return;
724+
}
725+
tmp += 2;
726+
endp = strchr(tmp, '\n');
727+
if (!endp)
728+
goto error_out;
729+
len = endp - tmp;
730+
if (len >= sizeof(summary_buf))
731+
goto error_out;
732+
memcpy(summary_buf, tmp, len);
733+
summary_buf[len] = 0;
734+
}
735+
678736
static const char *format_time(unsigned long time, const char *tz_str,
679737
int show_raw_time)
680738
{
@@ -751,7 +809,7 @@ static int find_orig_linenum(struct util_info *u, int lineno)
751809
}
752810

753811
static void emit_meta(struct commit *c, int lno,
754-
int sha1_len, int compatibility,
812+
int sha1_len, int compatibility, int porcelain,
755813
int show_name, int show_number, int show_raw_time,
756814
int longest_file, int longest_author,
757815
int max_digits, int max_orig_digits)
@@ -763,7 +821,47 @@ static void emit_meta(struct commit *c, int lno,
763821
u = c->util;
764822
lineno = find_orig_linenum(u, lno);
765823

766-
get_commit_info(c, &ci);
824+
if (porcelain) {
825+
int group_size = -1;
826+
struct commit *cc = (lno == 0) ? NULL : blame_lines[lno-1];
827+
if (cc != c) {
828+
/* This is the beginning of this group */
829+
int i;
830+
for (i = lno + 1; i < num_blame_lines; i++)
831+
if (blame_lines[i] != c)
832+
break;
833+
group_size = i - lno;
834+
}
835+
if (0 < group_size)
836+
printf("%s %d %d %d\n", sha1_to_hex(c->object.sha1),
837+
lineno, lno + 1, group_size);
838+
else
839+
printf("%s %d %d\n", sha1_to_hex(c->object.sha1),
840+
lineno, lno + 1);
841+
if (!u->meta_given) {
842+
get_commit_info(c, &ci, 1);
843+
printf("author %s\n", ci.author);
844+
printf("author-mail %s\n", ci.author_mail);
845+
printf("author-time %lu\n", ci.author_time);
846+
printf("author-tz %s\n", ci.author_tz);
847+
printf("committer %s\n", ci.committer);
848+
printf("committer-mail %s\n", ci.committer_mail);
849+
printf("committer-time %lu\n", ci.committer_time);
850+
printf("committer-tz %s\n", ci.committer_tz);
851+
printf("filename ");
852+
if (quote_c_style(u->pathname, NULL, NULL, 0))
853+
quote_c_style(u->pathname, NULL, stdout, 0);
854+
else
855+
fputs(u->pathname, stdout);
856+
printf("\nsummary %s\n", ci.summary);
857+
858+
u->meta_given = 1;
859+
}
860+
putchar('\t');
861+
return;
862+
}
863+
864+
get_commit_info(c, &ci, 0);
767865
fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
768866
if (compatibility) {
769867
printf("\t(%10s\t%10s\t%d)", ci.author,
@@ -809,6 +907,7 @@ int main(int argc, const char **argv)
809907
int longest_file, longest_author, longest_file_lines;
810908
int show_name = 0;
811909
int show_number = 0;
910+
int porcelain = 0;
812911

813912
const char *prefix = setup_git_directory();
814913
git_config(git_default_config);
@@ -852,6 +951,12 @@ int main(int argc, const char **argv)
852951
show_number = 1;
853952
continue;
854953
}
954+
if (!strcmp(argv[i], "--porcelain")) {
955+
porcelain = 1;
956+
sha1_len = 40;
957+
show_raw_time = 1;
958+
continue;
959+
}
855960
if (!strcmp(argv[i], "--")) {
856961
options = 0;
857962
continue;
@@ -934,7 +1039,7 @@ int main(int argc, const char **argv)
9341039
longest_file = strlen(u->pathname);
9351040
if (longest_file_lines < u->num_lines)
9361041
longest_file_lines = u->num_lines;
937-
get_commit_info(c, &ci);
1042+
get_commit_info(c, &ci, 0);
9381043
if (longest_author < strlen(ci.author))
9391044
longest_author = strlen(ci.author);
9401045
}
@@ -943,7 +1048,7 @@ int main(int argc, const char **argv)
9431048

9441049
for (i = 0; i < num_blame_lines; i++) {
9451050
emit_meta(blame_lines[i], i,
946-
sha1_len, compatibility,
1051+
sha1_len, compatibility, porcelain,
9471052
show_name, show_number, show_raw_time,
9481053
longest_file, longest_author,
9491054
max_digits, max_orig_digits);

0 commit comments

Comments
 (0)