Merge branch 'jc/web-blame' into next

* jc/web-blame:
  gitweb: use blame --porcelain
  git-blame --porcelain
  blame.c: move code to output metainfo into a separate function.
  git-blame: --show-number (and -n)
This commit is contained in:
Junio C Hamano
2006-10-09 20:29:41 -07:00
2 changed files with 245 additions and 69 deletions

249
blame.c
View File

@@ -17,6 +17,7 @@
#include "diffcore.h"
#include "revision.h"
#include "xdiff-interface.h"
#include "quote.h"
#define DEBUG 0
@@ -40,6 +41,7 @@ struct util_info {
unsigned long size;
int num_lines;
const char *pathname;
unsigned meta_given:1;
void *topo_data;
};
@@ -332,12 +334,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 +640,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];
char *tmp, *endp;
tmp = strstr(commit->buffer, "\nauthor ") + 8;
len = strchr(tmp, '\n') - tmp;
ret->author = author_buf;
memcpy(ret->author, tmp, len);
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)
{
@@ -731,6 +789,101 @@ static int read_ancestry(const char *graft_file,
return 0;
}
static int lineno_width(int lines)
{
int i, width;
for (width = 1, i = 10; i <= lines + 1; width++)
i *= 10;
return width;
}
static int find_orig_linenum(struct util_info *u, int lineno)
{
int i;
for (i = 0; i < u->num_lines; i++)
if (lineno == u->line_map[i])
return i + 1;
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;
@@ -750,9 +903,11 @@ int main(int argc, const char **argv)
struct commit_info ci;
const char *buf;
int max_digits;
int longest_file, longest_author;
int max_digits, max_orig_digits;
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);
@@ -791,6 +946,17 @@ int main(int argc, const char **argv)
show_name = 1;
continue;
}
if (!strcmp(argv[i], "-n") ||
!strcmp(argv[i], "--show-number")) {
show_number = 1;
continue;
}
if (!strcmp(argv[i], "--porcelain")) {
porcelain = 1;
sha1_len = 40;
show_raw_time = 1;
continue;
}
if (!strcmp(argv[i], "--")) {
options = 0;
continue;
@@ -852,53 +1018,40 @@ 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;
for (max_digits = 1, i = 10; i <= num_blame_lines + 1; max_digits++)
i *= 10;
max_digits = lineno_width(num_blame_lines);
longest_file = 0;
longest_author = 0;
longest_file_lines = 0;
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))
show_name = 1;
if (longest_file < strlen(u->pathname))
longest_file = strlen(u->pathname);
get_commit_info(c, &ci);
if (longest_file_lines < u->num_lines)
longest_file_lines = u->num_lines;
get_commit_info(c, &ci, 0);
if (longest_author < strlen(ci.author))
longest_author = strlen(ci.author);
}
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;
max_orig_digits = lineno_width(longest_file_lines);
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);
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);
}
for (i = 0; i < num_blame_lines; i++) {
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),

View File

@@ -1032,6 +1032,9 @@ sub parse_date {
$date{'hour_local'} = $hour;
$date{'minute_local'} = $min;
$date{'tz_local'} = $tz;
$date{'iso-tz'} = sprintf ("%04d-%02d-%02d %02d:%02d:%02d %s",
1900+$year, $mon+1, $mday,
$hour, $min, $sec, $tz);
return %date;
}
@@ -2562,7 +2565,8 @@ sub git_blame2 {
if ($ftype !~ "blob") {
die_error("400 Bad Request", "Object is not a blob");
}
open ($fd, "-|", git_cmd(), "blame", '-l', '--', $file_name, $hash_base)
open ($fd, "-|", git_cmd(), "blame", '--porcelain', '--',
$file_name, $hash_base)
or die_error(undef, "Open git-blame failed");
git_header_html();
my $formats_nav =
@@ -2586,33 +2590,52 @@ sub git_blame2 {
<table class="blame">
<tr><th>Commit</th><th>Line</th><th>Data</th></tr>
HTML
while (<$fd>) {
my ($full_rev, $author, $date, $lineno, $data) =
/^([0-9a-f]{40}).*?\s\((.*?)\s+([-\d]+ [:\d]+ [-+\d]+)\s+(\d+)\)\s(.*)/;
my %metainfo = ();
while (1) {
$_ = <$fd>;
last unless defined $_;
my ($full_rev, $lineno, $orig_lineno, $group_size) =
/^([0-9a-f]{40}) (\d+) (\d+)(?: (\d+))?$/;
if (!exists $metainfo{$full_rev}) {
$metainfo{$full_rev} = {};
}
my $meta = $metainfo{$full_rev};
while (<$fd>) {
last if (s/^\t//);
if (/^(\S+) (.*)$/) {
$meta->{$1} = $2;
}
}
my $data = $_;
my $rev = substr($full_rev, 0, 8);
my $print_c8 = 0;
if (!defined $last_rev) {
$last_rev = $full_rev;
$print_c8 = 1;
} elsif ($last_rev ne $full_rev) {
$last_rev = $full_rev;
my $author = $meta->{'author'};
my %date = parse_date($meta->{'author-time'},
$meta->{'author-tz'});
my $date = $date{'iso-tz'};
if ($group_size) {
$current_color = ++$current_color % $num_colors;
$print_c8 = 1;
}
print "<tr class=\"$rev_color[$current_color]\">\n";
print "<td class=\"sha1\"";
if ($print_c8 == 1) {
if ($group_size) {
print "<td class=\"sha1\"";
print " title=\"$author, $date\"";
}
print ">";
if ($print_c8 == 1) {
print $cgi->a({-href => href(action=>"commit", hash=>$full_rev, file_name=>$file_name)},
print " rowspan=\"$group_size\"" if ($group_size > 1);
print ">";
print $cgi->a({-href => href(action=>"commit",
hash=>$full_rev,
file_name=>$file_name)},
esc_html($rev));
print "</td>\n";
}
print "</td>\n";
print "<td class=\"linenr\"><a id=\"l$lineno\" href=\"#l$lineno\" class=\"linenr\">" .
esc_html($lineno) . "</a></td>\n";
my $blamed = href(action => 'blame',
file_name => $meta->{'filename'},
hash_base => $full_rev);
print "<td class=\"linenr\">";
print $cgi->a({ -href => "$blamed#l$orig_lineno",
-id => "l$lineno",
-class => "linenr" },
esc_html($lineno));
print "</td>";
print "<td class=\"pre\">" . esc_html($data) . "</td>\n";
print "</tr>\n";
}