blame.c 20.4 KB
Newer Older
F
Fredrik Kuivinen 已提交
1 2 3 4
/*
 * Copyright (C) 2006, Fredrik Kuivinen <freku045@student.liu.se>
 */

5
#include <assert.h>
F
Fredrik Kuivinen 已提交
6 7
#include <time.h>
#include <sys/time.h>
8
#include <math.h>
9 10 11 12 13 14 15 16

#include "cache.h"
#include "refs.h"
#include "tag.h"
#include "commit.h"
#include "tree.h"
#include "blob.h"
#include "diff.h"
17
#include "diffcore.h"
F
Fredrik Kuivinen 已提交
18
#include "revision.h"
J
Junio C Hamano 已提交
19
#include "xdiff-interface.h"
20 21 22

#define DEBUG 0

23
static const char blame_usage[] = "git-blame [-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
24 25 26 27 28
	"  -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"
	"  -h, --help          This message";
29 30 31 32 33

static struct commit **blame_lines;
static int num_blame_lines;
static char* blame_contents;
static int blame_len;
34

F
Fredrik Kuivinen 已提交
35 36 37 38 39 40
struct util_info {
	int *line_map;
	unsigned char sha1[20];	/* blob sha, not commit! */
	char *buf;
	unsigned long size;
	int num_lines;
41 42 43
	const char* pathname;

	void* topo_data;
44 45
};

F
Fredrik Kuivinen 已提交
46
struct chunk {
47 48
	int off1, len1;	/* --- */
	int off2, len2;	/* +++ */
49 50
};

F
Fredrik Kuivinen 已提交
51 52 53
struct patch {
	struct chunk *chunks;
	int num;
54 55
};

F
Fredrik Kuivinen 已提交
56
static void get_blob(struct commit *commit);
57

F
Fredrik Kuivinen 已提交
58
/* Only used for statistics */
59 60 61
static int num_get_patch;
static int num_commits;
static int patch_time;
62
static int num_read_blob;
63

J
Junio C Hamano 已提交
64 65
struct blame_diff_state {
	struct xdiff_emit_state xm;
F
Fredrik Kuivinen 已提交
66
	struct patch *ret;
J
Junio C Hamano 已提交
67
};
F
Fredrik Kuivinen 已提交
68

J
Junio C Hamano 已提交
69 70 71 72
static void process_u0_diff(void *state_, char *line, unsigned long len)
{
	struct blame_diff_state *state = state_;
	struct chunk *chunk;
F
Fredrik Kuivinen 已提交
73

J
Junio C Hamano 已提交
74 75
	if (len < 4 || line[0] != '@' || line[1] != '@')
		return;
F
Fredrik Kuivinen 已提交
76

J
Junio C Hamano 已提交
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91
	if (DEBUG)
		printf("chunk line: %.*s", (int)len, line);
	state->ret->num++;
	state->ret->chunks = xrealloc(state->ret->chunks,
				      sizeof(struct chunk) * state->ret->num);
	chunk = &state->ret->chunks[state->ret->num - 1];

	assert(!strncmp(line, "@@ -", 4));

	if (parse_hunk_header(line, len,
			      &chunk->off1, &chunk->len1,
			      &chunk->off2, &chunk->len2)) {
		state->ret->num--;
		return;
	}
F
Fredrik Kuivinen 已提交
92

J
Junio C Hamano 已提交
93 94 95 96
	if (chunk->len1 == 0)
		chunk->off1++;
	if (chunk->len2 == 0)
		chunk->off2++;
F
Fredrik Kuivinen 已提交
97

J
Junio C Hamano 已提交
98 99 100 101
	if (chunk->off1 > 0)
		chunk->off1--;
	if (chunk->off2 > 0)
		chunk->off2--;
F
Fredrik Kuivinen 已提交
102

J
Junio C Hamano 已提交
103 104 105
	assert(chunk->off1 >= 0);
	assert(chunk->off2 >= 0);
}
F
Fredrik Kuivinen 已提交
106

J
Junio C Hamano 已提交
107 108 109 110 111 112 113
static struct patch *get_patch(struct commit *commit, struct commit *other)
{
	struct blame_diff_state state;
	xpparam_t xpp;
	xdemitconf_t xecfg;
	mmfile_t file_c, file_o;
	xdemitcb_t ecb;
114 115
	struct util_info *info_c = (struct util_info *)commit->util;
	struct util_info *info_o = (struct util_info *)other->util;
J
Junio C Hamano 已提交
116
	struct timeval tv_start, tv_end;
F
Fredrik Kuivinen 已提交
117

J
Junio C Hamano 已提交
118 119 120
	get_blob(commit);
	file_c.ptr = info_c->buf;
	file_c.size = info_c->size;
F
Fredrik Kuivinen 已提交
121

J
Junio C Hamano 已提交
122 123 124
	get_blob(other);
	file_o.ptr = info_o->buf;
	file_o.size = info_o->size;
F
Fredrik Kuivinen 已提交
125

J
Junio C Hamano 已提交
126
	gettimeofday(&tv_start, NULL);
F
Fredrik Kuivinen 已提交
127

J
Junio C Hamano 已提交
128 129 130 131 132 133 134 135 136 137
	xpp.flags = XDF_NEED_MINIMAL;
	xecfg.ctxlen = 0;
	xecfg.flags = 0;
	ecb.outf = xdiff_outf;
	ecb.priv = &state;
	memset(&state, 0, sizeof(state));
	state.xm.consume = process_u0_diff;
	state.ret = xmalloc(sizeof(struct patch));
	state.ret->chunks = NULL;
	state.ret->num = 0;
F
Fredrik Kuivinen 已提交
138

J
Junio C Hamano 已提交
139
	xdl_diff(&file_c, &file_o, &xpp, &xecfg, &ecb);
F
Fredrik Kuivinen 已提交
140 141 142 143 144 145

	gettimeofday(&tv_end, NULL);
	patch_time += 1000000 * (tv_end.tv_sec - tv_start.tv_sec) +
		tv_end.tv_usec - tv_start.tv_usec;

	num_get_patch++;
J
Junio C Hamano 已提交
146
	return state.ret;
147 148
}

F
Fredrik Kuivinen 已提交
149
static void free_patch(struct patch *p)
150
{
F
Fredrik Kuivinen 已提交
151 152
	free(p->chunks);
	free(p);
153 154
}

155
static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
F
Fredrik Kuivinen 已提交
156 157
				  int baselen, const char *pathname,
				  unsigned mode, int stage);
158 159

static unsigned char blob_sha1[20];
160
static const char* blame_file;
F
Fredrik Kuivinen 已提交
161 162
static int get_blob_sha1(struct tree *t, const char *pathname,
			 unsigned char *sha1)
163
{
F
Fredrik Kuivinen 已提交
164 165
	int i;
	const char *pathspec[2];
166
	blame_file = pathname;
F
Fredrik Kuivinen 已提交
167 168
	pathspec[0] = pathname;
	pathspec[1] = NULL;
169
	hashclr(blob_sha1);
F
Fredrik Kuivinen 已提交
170 171 172 173 174 175 176 177 178 179
	read_tree_recursive(t, "", 0, 0, pathspec, get_blob_sha1_internal);

	for (i = 0; i < 20; i++) {
		if (blob_sha1[i] != 0)
			break;
	}

	if (i == 20)
		return -1;

180
	hashcpy(sha1, blob_sha1);
F
Fredrik Kuivinen 已提交
181
	return 0;
182 183
}

184
static int get_blob_sha1_internal(const unsigned char *sha1, const char *base,
F
Fredrik Kuivinen 已提交
185 186
				  int baselen, const char *pathname,
				  unsigned mode, int stage)
187
{
F
Fredrik Kuivinen 已提交
188 189
	if (S_ISDIR(mode))
		return READ_TREE_RECURSIVE;
190

191 192 193 194
	if (strncmp(blame_file, base, baselen) ||
	    strcmp(blame_file + baselen, pathname))
		return -1;

195
	hashcpy(blob_sha1, sha1);
F
Fredrik Kuivinen 已提交
196
	return -1;
197 198
}

F
Fredrik Kuivinen 已提交
199
static void get_blob(struct commit *commit)
200
{
201
	struct util_info *info = commit->util;
F
Fredrik Kuivinen 已提交
202
	char type[20];
203

F
Fredrik Kuivinen 已提交
204 205
	if (info->buf)
		return;
206

F
Fredrik Kuivinen 已提交
207
	info->buf = read_sha1_file(info->sha1, type, &info->size);
208
	num_read_blob++;
F
Fredrik Kuivinen 已提交
209

210
	assert(!strcmp(type, blob_type));
211 212
}

F
Fredrik Kuivinen 已提交
213 214
/* For debugging only */
static void print_patch(struct patch *p)
215
{
F
Fredrik Kuivinen 已提交
216 217 218 219 220 221
	int i;
	printf("Num chunks: %d\n", p->num);
	for (i = 0; i < p->num; i++) {
		printf("%d,%d %d,%d\n", p->chunks[i].off1, p->chunks[i].len1,
		       p->chunks[i].off2, p->chunks[i].len2);
	}
222 223
}

J
Junio C Hamano 已提交
224
#if DEBUG
F
Fredrik Kuivinen 已提交
225 226 227
/* For debugging only */
static void print_map(struct commit *cmit, struct commit *other)
{
228 229
	struct util_info *util = cmit->util;
	struct util_info *util2 = other->util;
F
Fredrik Kuivinen 已提交
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257

	int i;
	int max =
	    util->num_lines >
	    util2->num_lines ? util->num_lines : util2->num_lines;
	int num;

	for (i = 0; i < max; i++) {
		printf("i: %d ", i);
		num = -1;

		if (i < util->num_lines) {
			num = util->line_map[i];
			printf("%d\t", num);
		} else
			printf("\t");

		if (i < util2->num_lines) {
			int num2 = util2->line_map[i];
			printf("%d\t", num2);
			if (num != -1 && num2 != num)
				printf("---");
		} else
			printf("\t");

		printf("\n");
	}
}
J
Junio C Hamano 已提交
258
#endif
259

260
/* p is a patch from commit to other. */
F
Fredrik Kuivinen 已提交
261 262
static void fill_line_map(struct commit *commit, struct commit *other,
			  struct patch *p)
263
{
264 265
	struct util_info *util = commit->util;
	struct util_info *util2 = other->util;
F
Fredrik Kuivinen 已提交
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305
	int *map = util->line_map;
	int *map2 = util2->line_map;
	int cur_chunk = 0;
	int i1, i2;

	if (p->num && DEBUG)
		print_patch(p);

	if (DEBUG)
		printf("num lines 1: %d num lines 2: %d\n", util->num_lines,
		       util2->num_lines);

	for (i1 = 0, i2 = 0; i1 < util->num_lines; i1++, i2++) {
		struct chunk *chunk = NULL;
		if (cur_chunk < p->num)
			chunk = &p->chunks[cur_chunk];

		if (chunk && chunk->off1 == i1) {
			if (DEBUG && i2 != chunk->off2)
				printf("i2: %d off2: %d\n", i2, chunk->off2);

			assert(i2 == chunk->off2);

			i1--;
			i2--;
			if (chunk->len1 > 0)
				i1 += chunk->len1;

			if (chunk->len2 > 0)
				i2 += chunk->len2;

			cur_chunk++;
		} else {
			if (i2 >= util2->num_lines)
				break;

			if (map[i1] != map2[i2] && map[i1] != -1) {
				if (DEBUG)
					printf("map: i1: %d %d %p i2: %d %d %p\n",
					       i1, map[i1],
306
					       (void *) (i1 != -1 ? blame_lines[map[i1]] : NULL),
F
Fredrik Kuivinen 已提交
307
					       i2, map2[i2],
308
					       (void *) (i2 != -1 ? blame_lines[map2[i2]] : NULL));
F
Fredrik Kuivinen 已提交
309 310 311 312 313 314 315 316 317 318 319 320 321 322
				if (map2[i2] != -1 &&
				    blame_lines[map[i1]] &&
				    !blame_lines[map2[i2]])
					map[i1] = map2[i2];
			}

			if (map[i1] == -1 && map2[i2] != -1)
				map[i1] = map2[i2];
		}

		if (DEBUG > 1)
			printf("l1: %d l2: %d i1: %d i2: %d\n",
			       map[i1], map2[i2], i1, i2);
	}
323 324
}

F
Fredrik Kuivinen 已提交
325
static int map_line(struct commit *commit, int line)
326
{
327
	struct util_info *info = commit->util;
F
Fredrik Kuivinen 已提交
328 329
	assert(line >= 0 && line < info->num_lines);
	return info->line_map[line];
330 331
}

332
static struct util_info* get_util(struct commit *commit)
333
{
334
	struct util_info *util = commit->util;
335 336 337

	if (util)
		return util;
F
Fredrik Kuivinen 已提交
338 339

	util = xmalloc(sizeof(struct util_info));
340 341 342 343 344
	util->buf = NULL;
	util->size = 0;
	util->line_map = NULL;
	util->num_lines = -1;
	util->pathname = NULL;
345
	commit->util = util;
346 347 348 349 350
	return util;
}

static int fill_util_info(struct commit *commit)
{
351
	struct util_info *util = commit->util;
352 353 354

	assert(util);
	assert(util->pathname);
F
Fredrik Kuivinen 已提交
355

D
David Rientjes 已提交
356
	return !!get_blob_sha1(commit->tree, util->pathname, util->sha1);
357 358
}

F
Fredrik Kuivinen 已提交
359
static void alloc_line_map(struct commit *commit)
360
{
361
	struct util_info *util = commit->util;
F
Fredrik Kuivinen 已提交
362
	int i;
363

F
Fredrik Kuivinen 已提交
364 365
	if (util->line_map)
		return;
366

F
Fredrik Kuivinen 已提交
367
	get_blob(commit);
368

F
Fredrik Kuivinen 已提交
369 370 371 372 373 374 375
	util->num_lines = 0;
	for (i = 0; i < util->size; i++) {
		if (util->buf[i] == '\n')
			util->num_lines++;
	}
	if(util->buf[util->size - 1] != '\n')
		util->num_lines++;
376

F
Fredrik Kuivinen 已提交
377
	util->line_map = xmalloc(sizeof(int) * util->num_lines);
378

F
Fredrik Kuivinen 已提交
379 380
	for (i = 0; i < util->num_lines; i++)
		util->line_map[i] = -1;
381 382
}

F
Fredrik Kuivinen 已提交
383
static void init_first_commit(struct commit* commit, const char* filename)
384
{
385
	struct util_info* util = commit->util;
F
Fredrik Kuivinen 已提交
386
	int i;
387

388 389
	util->pathname = filename;
	if (fill_util_info(commit))
F
Fredrik Kuivinen 已提交
390
		die("fill_util_info failed");
391

F
Fredrik Kuivinen 已提交
392
	alloc_line_map(commit);
393

394
	util = commit->util;
395

396
	for (i = 0; i < util->num_lines; i++)
F
Fredrik Kuivinen 已提交
397 398
		util->line_map[i] = i;
}
399 400


F
Fredrik Kuivinen 已提交
401 402 403 404 405 406 407 408 409 410 411 412 413 414
static void process_commits(struct rev_info *rev, const char *path,
			    struct commit** initial)
{
	int i;
	struct util_info* util;
	int lines_left;
	int *blame_p;
	int *new_lines;
	int new_lines_len;

	struct commit* commit = get_revision(rev);
	assert(commit);
	init_first_commit(commit, path);

415
	util = commit->util;
F
Fredrik Kuivinen 已提交
416 417
	num_blame_lines = util->num_lines;
	blame_lines = xmalloc(sizeof(struct commit *) * num_blame_lines);
418 419 420
	blame_contents = util->buf;
	blame_len = util->size;

F
Fredrik Kuivinen 已提交
421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449
	for (i = 0; i < num_blame_lines; i++)
		blame_lines[i] = NULL;

	lines_left = num_blame_lines;
	blame_p = xmalloc(sizeof(int) * num_blame_lines);
	new_lines = xmalloc(sizeof(int) * num_blame_lines);
	do {
		struct commit_list *parents;
		int num_parents;
		struct util_info *util;

		if (DEBUG)
			printf("\nProcessing commit: %d %s\n", num_commits,
			       sha1_to_hex(commit->object.sha1));

		if (lines_left == 0)
			return;

		num_commits++;
		memset(blame_p, 0, sizeof(int) * num_blame_lines);
		new_lines_len = 0;
		num_parents = 0;
		for (parents = commit->parents;
		     parents != NULL; parents = parents->next)
			num_parents++;

		if(num_parents == 0)
			*initial = commit;

450
		if (fill_util_info(commit))
F
Fredrik Kuivinen 已提交
451 452 453
			continue;

		alloc_line_map(commit);
454
		util = commit->util;
F
Fredrik Kuivinen 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467

		for (parents = commit->parents;
		     parents != NULL; parents = parents->next) {
			struct commit *parent = parents->item;
			struct patch *patch;

			if (parse_commit(parent) < 0)
				die("parse_commit error");

			if (DEBUG)
				printf("parent: %s\n",
				       sha1_to_hex(parent->object.sha1));

468
			if (fill_util_info(parent)) {
F
Fredrik Kuivinen 已提交
469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505
				num_parents--;
				continue;
			}

			patch = get_patch(parent, commit);
                        alloc_line_map(parent);
                        fill_line_map(parent, commit, patch);

                        for (i = 0; i < patch->num; i++) {
                            int l;
                            for (l = 0; l < patch->chunks[i].len2; l++) {
                                int mapped_line =
                                    map_line(commit, patch->chunks[i].off2 + l);
                                if (mapped_line != -1) {
                                    blame_p[mapped_line]++;
                                    if (blame_p[mapped_line] == num_parents)
                                        new_lines[new_lines_len++] = mapped_line;
                                }
                            }
			}
                        free_patch(patch);
		}

		if (DEBUG)
			printf("parents: %d\n", num_parents);

		for (i = 0; i < new_lines_len; i++) {
			int mapped_line = new_lines[i];
			if (blame_lines[mapped_line] == NULL) {
				blame_lines[mapped_line] = commit;
				lines_left--;
				if (DEBUG)
					printf("blame: mapped: %d i: %d\n",
					       mapped_line, i);
			}
		}
	} while ((commit = get_revision(rev)) != NULL);
506 507
}

508 509 510 511

static int compare_tree_path(struct rev_info* revs,
			     struct commit* c1, struct commit* c2)
{
512
	int ret;
513
	const char* paths[2];
514
	struct util_info* util = c2->util;
515 516 517
	paths[0] = util->pathname;
	paths[1] = NULL;

518
	diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
F
Fredrik Kuivinen 已提交
519
			      &revs->pruning);
520
	ret = rev_compare_tree(revs, c1->tree, c2->tree);
F
Fredrik Kuivinen 已提交
521
	diff_tree_release_paths(&revs->pruning);
522
	return ret;
523 524 525 526 527 528
}


static int same_tree_as_empty_path(struct rev_info *revs, struct tree* t1,
				   const char* path)
{
529
	int ret;
530 531 532 533
	const char* paths[2];
	paths[0] = path;
	paths[1] = NULL;

534
	diff_tree_setup_paths(get_pathspec(revs->prefix, paths),
F
Fredrik Kuivinen 已提交
535
			      &revs->pruning);
536
	ret = rev_same_tree_as_empty(revs, t1);
F
Fredrik Kuivinen 已提交
537
	diff_tree_release_paths(&revs->pruning);
538
	return ret;
539 540 541 542
}

static const char* find_rename(struct commit* commit, struct commit* parent)
{
543
	struct util_info* cutil = commit->util;
544 545 546 547 548 549 550 551 552 553 554 555 556 557
	struct diff_options diff_opts;
	const char *paths[1];
	int i;

	if (DEBUG) {
		printf("find_rename commit: %s ",
		       sha1_to_hex(commit->object.sha1));
		puts(sha1_to_hex(parent->object.sha1));
	}

	diff_setup(&diff_opts);
	diff_opts.recursive = 1;
	diff_opts.detect_rename = DIFF_DETECT_RENAME;
	paths[0] = NULL;
558
	diff_tree_setup_paths(paths, &diff_opts);
559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586
	if (diff_setup_done(&diff_opts) < 0)
		die("diff_setup_done failed");

	diff_tree_sha1(commit->tree->object.sha1, parent->tree->object.sha1,
		       "", &diff_opts);
	diffcore_std(&diff_opts);

	for (i = 0; i < diff_queued_diff.nr; i++) {
		struct diff_filepair *p = diff_queued_diff.queue[i];

		if (p->status == 'R' && !strcmp(p->one->path, cutil->pathname)) {
			if (DEBUG)
				printf("rename %s -> %s\n", p->one->path, p->two->path);
			return p->two->path;
		}
	}

	return 0;
}

static void simplify_commit(struct rev_info *revs, struct commit *commit)
{
	struct commit_list **pp, *parent;

	if (!commit->tree)
		return;

	if (!commit->parents) {
587
		struct util_info* util = commit->util;
588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613
		if (!same_tree_as_empty_path(revs, commit->tree,
					     util->pathname))
			commit->object.flags |= TREECHANGE;
		return;
	}

	pp = &commit->parents;
	while ((parent = *pp) != NULL) {
		struct commit *p = parent->item;

		if (p->object.flags & UNINTERESTING) {
			pp = &parent->next;
			continue;
		}

		parse_commit(p);
		switch (compare_tree_path(revs, p, commit)) {
		case REV_TREE_SAME:
			parent->next = NULL;
			commit->parents = parent;
			get_util(p)->pathname = get_util(commit)->pathname;
			return;

		case REV_TREE_NEW:
		{

614
			struct util_info* util = commit->util;
615 616 617 618 619 620 621
			if (revs->remove_empty_trees &&
			    same_tree_as_empty_path(revs, p->tree,
						    util->pathname)) {
				const char* new_name = find_rename(commit, p);
				if (new_name) {
					struct util_info* putil = get_util(p);
					if (!putil->pathname)
622
						putil->pathname = xstrdup(new_name);
623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644
				} else {
					*pp = parent->next;
					continue;
				}
			}
		}

		/* fallthrough */
		case REV_TREE_DIFFERENT:
			pp = &parent->next;
			if (!get_util(p)->pathname)
				get_util(p)->pathname =
					get_util(commit)->pathname;
			continue;
		}
		die("bad tree compare for commit %s",
		    sha1_to_hex(commit->object.sha1));
	}
	commit->object.flags |= TREECHANGE;
}


645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
struct commit_info
{
	char* author;
	char* author_mail;
	unsigned long author_time;
	char* author_tz;
};

static void get_commit_info(struct commit* commit, struct commit_info* ret)
{
	int len;
	char* tmp;
	static char author_buf[1024];

	tmp = strstr(commit->buffer, "\nauthor ") + 8;
J
Junio C Hamano 已提交
660
	len = strchr(tmp, '\n') - tmp;
661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683
	ret->author = author_buf;
	memcpy(ret->author, tmp, len);

	tmp = ret->author;
	tmp += len;
	*tmp = 0;
	while(*tmp != ' ')
		tmp--;
	ret->author_tz = tmp+1;

	*tmp = 0;
	while(*tmp != ' ')
		tmp--;
	ret->author_time = strtoul(tmp, NULL, 10);

	*tmp = 0;
	while(*tmp != ' ')
		tmp--;
	ret->author_mail = tmp + 1;

	*tmp = 0;
}

684 685
static const char* format_time(unsigned long time, const char* tz_str,
			       int show_raw_time)
686 687 688
{
	static char time_buf[128];
	time_t t = time;
689 690 691
	int minutes, tz;
	struct tm *tm;

692 693 694 695 696
	if (show_raw_time) {
		sprintf(time_buf, "%lu %s", time, tz_str);
		return time_buf;
	}

697 698 699 700 701 702 703 704 705
	tz = atoi(tz_str);
	minutes = tz < 0 ? -tz : tz;
	minutes = (minutes / 100)*60 + (minutes % 100);
	minutes = tz < 0 ? -minutes : minutes;
	t = time + minutes * 60;
	tm = gmtime(&t);

	strftime(time_buf, sizeof(time_buf), "%Y-%m-%d %H:%M:%S ", tm);
	strcat(time_buf, tz_str);
706 707 708
	return time_buf;
}

709 710
static void topo_setter(struct commit* c, void* data)
{
711
	struct util_info* util = c->util;
712 713 714 715 716
	util->topo_data = data;
}

static void* topo_getter(struct commit* c)
{
717
	struct util_info* util = c->util;
718 719 720
	return util->topo_data;
}

J
Junio C Hamano 已提交
721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739
static int read_ancestry(const char *graft_file,
			 unsigned char **start_sha1)
{
	FILE *fp = fopen(graft_file, "r");
	char buf[1024];
	if (!fp)
		return -1;
	while (fgets(buf, sizeof(buf), fp)) {
		/* The format is just "Commit Parent1 Parent2 ...\n" */
		int len = strlen(buf);
		struct commit_graft *graft = read_graft_line(buf, len);
		register_commit_graft(graft, 0);
		if (!*start_sha1)
			*start_sha1 = graft->sha1;
	}
	fclose(fp);
	return 0;
}

740 741
int main(int argc, const char **argv)
{
F
Fredrik Kuivinen 已提交
742 743
	int i;
	struct commit *initial = NULL;
J
Junio C Hamano 已提交
744
	unsigned char sha1[20], *sha1_p = NULL;
745 746 747 748

	const char *filename = NULL, *commit = NULL;
	char filename_buf[256];
	int sha1_len = 8;
749
	int compatibility = 0;
750
	int show_raw_time = 0;
751
	int options = 1;
752
	struct commit* start_commit;
753

F
Fredrik Kuivinen 已提交
754 755 756
	const char* args[10];
	struct rev_info rev;

757 758 759
	struct commit_info ci;
	const char *buf;
	int max_digits;
760
	int longest_file, longest_author;
F
Fredrik Kuivinen 已提交
761
	int found_rename;
F
Fredrik Kuivinen 已提交
762

763
	const char* prefix = setup_git_directory();
764
	git_config(git_default_config);
F
Fredrik Kuivinen 已提交
765

766 767 768 769 770 771 772
	for(i = 1; i < argc; i++) {
		if(options) {
			if(!strcmp(argv[i], "-h") ||
			   !strcmp(argv[i], "--help"))
				usage(blame_usage);
			else if(!strcmp(argv[i], "-l") ||
				!strcmp(argv[i], "--long")) {
773
				sha1_len = 40;
774 775
				continue;
			} else if(!strcmp(argv[i], "-c") ||
776 777
				  !strcmp(argv[i], "--compatibility")) {
				compatibility = 1;
778
				continue;
779 780 781 782
			} else if(!strcmp(argv[i], "-t") ||
				  !strcmp(argv[i], "--time")) {
				show_raw_time = 1;
				continue;
J
Junio C Hamano 已提交
783 784 785
			} else if(!strcmp(argv[i], "-S")) {
				if (i + 1 < argc &&
				    !read_ancestry(argv[i + 1], &sha1_p)) {
786
					compatibility = 1;
J
Junio C Hamano 已提交
787 788 789 790
					i++;
					continue;
				}
				usage(blame_usage);
791 792 793 794 795 796 797 798
			} else if(!strcmp(argv[i], "--")) {
				options = 0;
				continue;
			} else if(argv[i][0] == '-')
				usage(blame_usage);
			else
				options = 0;
		}
F
Fredrik Kuivinen 已提交
799

800 801 802 803 804 805 806 807 808 809 810 811
		if(!options) {
			if(!filename)
				filename = argv[i];
			else if(!commit)
				commit = argv[i];
			else
				usage(blame_usage);
		}
	}

	if(!filename)
		usage(blame_usage);
J
Junio C Hamano 已提交
812 813 814
	if (commit && sha1_p)
		usage(blame_usage);
	else if(!commit)
815 816 817 818 819 820 821
		commit = "HEAD";

	if(prefix)
		sprintf(filename_buf, "%s%s", prefix, filename);
	else
		strcpy(filename_buf, filename);
	filename = filename_buf;
F
Fredrik Kuivinen 已提交
822

J
Junio C Hamano 已提交
823 824 825 826 827 828
	if (!sha1_p) {
		if (get_sha1(commit, sha1))
			die("get_sha1 failed, commit '%s' not found", commit);
		sha1_p = sha1;
	}
	start_commit = lookup_commit_reference(sha1_p);
829 830 831 832
	get_util(start_commit)->pathname = filename;
	if (fill_util_info(start_commit)) {
		printf("%s not found in %s\n", filename, commit);
		return 1;
F
Fredrik Kuivinen 已提交
833 834 835
	}


836
	init_revisions(&rev, setup_git_directory());
837 838 839 840 841
	rev.remove_empty_trees = 1;
	rev.topo_order = 1;
	rev.prune_fn = simplify_commit;
	rev.topo_setter = topo_setter;
	rev.topo_getter = topo_getter;
842
	rev.parents = 1;
843 844 845 846 847 848
	rev.limited = 1;

	commit_list_insert(start_commit, &rev.commits);

	args[0] = filename;
	args[1] = NULL;
F
Fredrik Kuivinen 已提交
849
	diff_tree_setup_paths(args, &rev.pruning);
F
Fredrik Kuivinen 已提交
850 851 852
	prepare_revision_walk(&rev);
	process_commits(&rev, filename, &initial);

853
	buf = blame_contents;
854 855 856
	for (max_digits = 1, i = 10; i <= num_blame_lines + 1; max_digits++)
		i *= 10;

F
Fredrik Kuivinen 已提交
857 858 859 860 861 862 863 864
	longest_file = 0;
	longest_author = 0;
	found_rename = 0;
	for (i = 0; i < num_blame_lines; i++) {
		struct commit *c = blame_lines[i];
		struct util_info* u;
		if (!c)
			c = initial;
865
		u = c->util;
F
Fredrik Kuivinen 已提交
866 867 868 869 870 871 872 873 874 875

		if (!found_rename && strcmp(filename, u->pathname))
			found_rename = 1;
		if (longest_file < strlen(u->pathname))
			longest_file = strlen(u->pathname);
		get_commit_info(c, &ci);
		if (longest_author < strlen(ci.author))
			longest_author = strlen(ci.author);
	}

F
Fredrik Kuivinen 已提交
876 877
	for (i = 0; i < num_blame_lines; i++) {
		struct commit *c = blame_lines[i];
878 879
		struct util_info* u;

F
Fredrik Kuivinen 已提交
880 881 882
		if (!c)
			c = initial;

883
		u = c->util;
884 885
		get_commit_info(c, &ci);
		fwrite(sha1_to_hex(c->object.sha1), sha1_len, 1, stdout);
886
		if(compatibility) {
887
			printf("\t(%10s\t%10s\t%d)", ci.author,
888 889 890
			       format_time(ci.author_time, ci.author_tz,
					   show_raw_time),
			       i+1);
F
Fredrik Kuivinen 已提交
891 892 893 894 895 896
		} else {
			if (found_rename)
				printf(" %-*.*s", longest_file, longest_file,
				       u->pathname);
			printf(" (%-*.*s %10s %*d) ",
			       longest_author, longest_author, ci.author,
897 898
			       format_time(ci.author_time, ci.author_tz,
					   show_raw_time),
899
			       max_digits, i+1);
F
Fredrik Kuivinen 已提交
900
		}
901 902 903 904 905 906 907

		if(i == num_blame_lines - 1) {
			fwrite(buf, blame_len - (buf - blame_contents),
			       1, stdout);
			if(blame_contents[blame_len-1] != '\n')
				putc('\n', stdout);
		} else {
J
Junio C Hamano 已提交
908
			char* next_buf = strchr(buf, '\n') + 1;
909 910 911
			fwrite(buf, next_buf - buf, 1, stdout);
			buf = next_buf;
		}
F
Fredrik Kuivinen 已提交
912 913 914
	}

	if (DEBUG) {
915
		printf("num read blob: %d\n", num_read_blob);
F
Fredrik Kuivinen 已提交
916 917 918 919 920 921 922
		printf("num get patch: %d\n", num_get_patch);
		printf("num commits: %d\n", num_commits);
		printf("patch time: %f\n", patch_time / 1000000.0);
		printf("initial: %s\n", sha1_to_hex(initial->object.sha1));
	}

	return 0;
923
}