X-Git-Url: https://git.tokkee.org/?a=blobdiff_plain;f=blame.c;h=e664813cb48f555fa8968995eda4170a02a99f42;hb=9ee93dceb5dbef8052b0abde3eea94181bd9d95b;hp=bf4a1a1ff06d2a923dac63792a4da647831111d5;hpb=cf54a029ff82ce9b565e075bfa5d97ec82841283;p=git.git diff --git a/blame.c b/blame.c index bf4a1a1ff..e664813cb 100644 --- a/blame.c +++ b/blame.c @@ -17,15 +17,19 @@ #include "diffcore.h" #include "revision.h" #include "xdiff-interface.h" +#include "quote.h" #define DEBUG 0 static const char blame_usage[] = -"git-blame [-c] [-l] [-t] [-S ] [--] file [commit]\n" +"git-blame [-c] [-l] [-t] [-f] [-n] [-p] [-S ] [--] file [commit]\n" " -c, --compatibility Use the same output mode as git-annotate (Default: off)\n" " -l, --long Show long commit SHA1 (Default: off)\n" " -t, --time Show raw timestamp (Default: off)\n" -" -S, --revs-file Use revisions from revs-file instead of calling git-rev-list\n" +" -f, --show-name Show original filename (Default: auto)\n" +" -n, --show-number Show original linenumber (Default: off)\n" +" -p, --porcelain Show in a format designed for machine consumption\n" +" -S revs-file Use revisions from revs-file instead of calling git-rev-list\n" " -h, --help This message"; static struct commit **blame_lines; @@ -40,6 +44,7 @@ struct util_info { unsigned long size; int num_lines; const char *pathname; + unsigned meta_given:1; void *topo_data; }; @@ -332,12 +337,8 @@ static struct util_info *get_util(struct commit *commit) if (util) return util; - util = xmalloc(sizeof(struct util_info)); - util->buf = NULL; - util->size = 0; - util->line_map = NULL; + util = xcalloc(1, sizeof(struct util_info)); util->num_lines = -1; - util->pathname = NULL; commit->util = util; return util; } @@ -642,39 +643,99 @@ struct commit_info char *author_mail; unsigned long author_time; char *author_tz; + + /* filled only when asked for details */ + char *committer; + char *committer_mail; + unsigned long committer_time; + char *committer_tz; + + char *summary; }; -static void get_commit_info(struct commit *commit, struct commit_info *ret) +static void get_ac_line(const char *inbuf, const char *what, + int bufsz, char *person, char **mail, + unsigned long *time, char **tz) { int len; - char *tmp; - static char author_buf[1024]; - - tmp = strstr(commit->buffer, "\nauthor ") + 8; - len = strchr(tmp, '\n') - tmp; - ret->author = author_buf; - memcpy(ret->author, tmp, len); + char *tmp, *endp; + + tmp = strstr(inbuf, what); + if (!tmp) + goto error_out; + tmp += strlen(what); + endp = strchr(tmp, '\n'); + if (!endp) + len = strlen(tmp); + else + len = endp - tmp; + if (bufsz <= len) { + error_out: + /* Ugh */ + person = *mail = *tz = "(unknown)"; + *time = 0; + return; + } + memcpy(person, tmp, len); - tmp = ret->author; + tmp = person; tmp += len; *tmp = 0; while (*tmp != ' ') tmp--; - ret->author_tz = tmp+1; + *tz = tmp+1; *tmp = 0; while (*tmp != ' ') tmp--; - ret->author_time = strtoul(tmp, NULL, 10); + *time = strtoul(tmp, NULL, 10); *tmp = 0; while (*tmp != ' ') tmp--; - ret->author_mail = tmp + 1; - + *mail = tmp + 1; *tmp = 0; } +static void get_commit_info(struct commit *commit, struct commit_info *ret, int detailed) +{ + int len; + char *tmp, *endp; + static char author_buf[1024]; + static char committer_buf[1024]; + static char summary_buf[1024]; + + ret->author = author_buf; + get_ac_line(commit->buffer, "\nauthor ", + sizeof(author_buf), author_buf, &ret->author_mail, + &ret->author_time, &ret->author_tz); + + if (!detailed) + return; + + ret->committer = committer_buf; + get_ac_line(commit->buffer, "\ncommitter ", + sizeof(committer_buf), committer_buf, &ret->committer_mail, + &ret->committer_time, &ret->committer_tz); + + ret->summary = summary_buf; + tmp = strstr(commit->buffer, "\n\n"); + if (!tmp) { + error_out: + sprintf(summary_buf, "(%s)", sha1_to_hex(commit->object.sha1)); + return; + } + tmp += 2; + endp = strchr(tmp, '\n'); + if (!endp) + goto error_out; + len = endp - tmp; + if (len >= sizeof(summary_buf)) + goto error_out; + memcpy(summary_buf, tmp, len); + summary_buf[len] = 0; +} + static const char *format_time(unsigned long time, const char *tz_str, int show_raw_time) { @@ -750,6 +811,82 @@ static int find_orig_linenum(struct util_info *u, int lineno) return 0; } +static void emit_meta(struct commit *c, int lno, + int sha1_len, int compatibility, int porcelain, + int show_name, int show_number, int show_raw_time, + int longest_file, int longest_author, + int max_digits, int max_orig_digits) +{ + struct util_info *u; + int lineno; + struct commit_info ci; + + u = c->util; + lineno = find_orig_linenum(u, lno); + + if (porcelain) { + int group_size = -1; + struct commit *cc = (lno == 0) ? NULL : blame_lines[lno-1]; + if (cc != c) { + /* This is the beginning of this group */ + int i; + for (i = lno + 1; i < num_blame_lines; i++) + if (blame_lines[i] != c) + break; + group_size = i - lno; + } + if (0 < group_size) + printf("%s %d %d %d\n", sha1_to_hex(c->object.sha1), + lineno, lno + 1, group_size); + else + printf("%s %d %d\n", sha1_to_hex(c->object.sha1), + lineno, lno + 1); + if (!u->meta_given) { + get_commit_info(c, &ci, 1); + printf("author %s\n", ci.author); + printf("author-mail %s\n", ci.author_mail); + printf("author-time %lu\n", ci.author_time); + printf("author-tz %s\n", ci.author_tz); + printf("committer %s\n", ci.committer); + printf("committer-mail %s\n", ci.committer_mail); + printf("committer-time %lu\n", ci.committer_time); + printf("committer-tz %s\n", ci.committer_tz); + printf("filename "); + if (quote_c_style(u->pathname, NULL, NULL, 0)) + quote_c_style(u->pathname, NULL, stdout, 0); + else + fputs(u->pathname, stdout); + printf("\nsummary %s\n", ci.summary); + + u->meta_given = 1; + } + putchar('\t'); + return; + } + + get_commit_info(c, &ci, 0); + fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout); + if (compatibility) { + printf("\t(%10s\t%10s\t%d)", ci.author, + format_time(ci.author_time, ci.author_tz, + show_raw_time), + lno + 1); + } + else { + if (show_name) + printf(" %-*.*s", longest_file, longest_file, + u->pathname); + if (show_number) + printf(" %*d", max_orig_digits, + lineno); + printf(" (%-*.*s %10s %*d) ", + longest_author, longest_author, ci.author, + format_time(ci.author_time, ci.author_tz, + show_raw_time), + max_digits, lno + 1); + } +} + int main(int argc, const char **argv) { int i; @@ -773,6 +910,7 @@ int main(int argc, const char **argv) int longest_file, longest_author, longest_file_lines; int show_name = 0; int show_number = 0; + int porcelain = 0; const char *prefix = setup_git_directory(); git_config(git_default_config); @@ -816,6 +954,13 @@ int main(int argc, const char **argv) show_number = 1; continue; } + if (!strcmp(argv[i], "-p") || + !strcmp(argv[i], "--porcelain")) { + porcelain = 1; + sha1_len = 40; + show_raw_time = 1; + continue; + } if (!strcmp(argv[i], "--")) { options = 0; continue; @@ -877,6 +1022,10 @@ int main(int argc, const char **argv) prepare_revision_walk(&rev); process_commits(&rev, filename, &initial); + for (i = 0; i < num_blame_lines; i++) + if (!blame_lines[i]) + blame_lines[i] = initial; + buf = blame_contents; max_digits = lineno_width(num_blame_lines); @@ -886,8 +1035,6 @@ int main(int argc, const char **argv) for (i = 0; i < num_blame_lines; i++) { struct commit *c = blame_lines[i]; struct util_info *u; - if (!c) - c = initial; u = c->util; if (!show_name && strcmp(filename, u->pathname)) @@ -896,7 +1043,7 @@ int main(int argc, const char **argv) longest_file = strlen(u->pathname); if (longest_file_lines < u->num_lines) longest_file_lines = u->num_lines; - get_commit_info(c, &ci); + get_commit_info(c, &ci, 0); if (longest_author < strlen(ci.author)) longest_author = strlen(ci.author); } @@ -904,35 +1051,11 @@ int main(int argc, const char **argv) max_orig_digits = lineno_width(longest_file_lines); for (i = 0; i < num_blame_lines; i++) { - struct commit *c = blame_lines[i]; - struct util_info *u; - int lineno; - if (!c) - c = initial; - u = c->util; - lineno = find_orig_linenum(u, i); - - get_commit_info(c, &ci); - fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout); - if (compatibility) { - printf("\t(%10s\t%10s\t%d)", ci.author, - format_time(ci.author_time, ci.author_tz, - show_raw_time), - i+1); - } - else { - if (show_name) - printf(" %-*.*s", longest_file, longest_file, - u->pathname); - if (show_number) - printf(" %*d", max_orig_digits, - lineno); - printf(" (%-*.*s %10s %*d) ", - longest_author, longest_author, ci.author, - format_time(ci.author_time, ci.author_tz, - show_raw_time), - max_digits, i+1); - } + emit_meta(blame_lines[i], i, + sha1_len, compatibility, porcelain, + show_name, show_number, show_raw_time, + longest_file, longest_author, + max_digits, max_orig_digits); if (i == num_blame_lines - 1) { fwrite(buf, blame_len - (buf - blame_contents),