2 * Copyright (C) 2006, Fredrik Kuivinen <freku045@student.liu.se>
19 #include "xdiff-interface.h"
23 static const char blame_usage
[] = "git-blame [-c] [-l] [-t] [-S <revs-file>] [--] file [commit]\n"
24 " -c, --compatibility Use the same output mode as git-annotate (Default: off)\n"
25 " -l, --long Show long commit SHA1 (Default: off)\n"
26 " -t, --time Show raw timestamp (Default: off)\n"
27 " -S, --revs-file Use revisions from revs-file instead of calling git-rev-list\n"
28 " -h, --help This message";
30 static struct commit
**blame_lines
;
31 static int num_blame_lines
;
32 static char* blame_contents
;
37 unsigned char sha1
[20]; /* blob sha, not commit! */
47 int off1
, len1
; /* --- */
48 int off2
, len2
; /* +++ */
56 static void get_blob(struct commit
*commit
);
58 /* Only used for statistics */
59 static int num_get_patch
;
60 static int num_commits
;
61 static int patch_time
;
62 static int num_read_blob
;
64 struct blame_diff_state
{
65 struct xdiff_emit_state xm
;
69 static void process_u0_diff(void *state_
, char *line
, unsigned long len
)
71 struct blame_diff_state
*state
= state_
;
74 if (len
< 4 || line
[0] != '@' || line
[1] != '@')
78 printf("chunk line: %.*s", (int)len
, line
);
80 state
->ret
->chunks
= xrealloc(state
->ret
->chunks
,
81 sizeof(struct chunk
) * state
->ret
->num
);
82 chunk
= &state
->ret
->chunks
[state
->ret
->num
- 1];
84 assert(!strncmp(line
, "@@ -", 4));
86 if (parse_hunk_header(line
, len
,
87 &chunk
->off1
, &chunk
->len1
,
88 &chunk
->off2
, &chunk
->len2
)) {
103 assert(chunk
->off1
>= 0);
104 assert(chunk
->off2
>= 0);
107 static struct patch
*get_patch(struct commit
*commit
, struct commit
*other
)
109 struct blame_diff_state state
;
112 mmfile_t file_c
, file_o
;
114 struct util_info
*info_c
= (struct util_info
*)commit
->util
;
115 struct util_info
*info_o
= (struct util_info
*)other
->util
;
116 struct timeval tv_start
, tv_end
;
119 file_c
.ptr
= info_c
->buf
;
120 file_c
.size
= info_c
->size
;
123 file_o
.ptr
= info_o
->buf
;
124 file_o
.size
= info_o
->size
;
126 gettimeofday(&tv_start
, NULL
);
128 xpp
.flags
= XDF_NEED_MINIMAL
;
131 ecb
.outf
= xdiff_outf
;
133 memset(&state
, 0, sizeof(state
));
134 state
.xm
.consume
= process_u0_diff
;
135 state
.ret
= xmalloc(sizeof(struct patch
));
136 state
.ret
->chunks
= NULL
;
139 xdl_diff(&file_c
, &file_o
, &xpp
, &xecfg
, &ecb
);
141 gettimeofday(&tv_end
, NULL
);
142 patch_time
+= 1000000 * (tv_end
.tv_sec
- tv_start
.tv_sec
) +
143 tv_end
.tv_usec
- tv_start
.tv_usec
;
149 static void free_patch(struct patch
*p
)
155 static int get_blob_sha1_internal(const unsigned char *sha1
, const char *base
,
156 int baselen
, const char *pathname
,
157 unsigned mode
, int stage
);
159 static unsigned char blob_sha1
[20];
160 static const char* blame_file
;
161 static int get_blob_sha1(struct tree
*t
, const char *pathname
,
165 const char *pathspec
[2];
166 blame_file
= pathname
;
167 pathspec
[0] = pathname
;
170 read_tree_recursive(t
, "", 0, 0, pathspec
, get_blob_sha1_internal
);
172 for (i
= 0; i
< 20; i
++) {
173 if (blob_sha1
[i
] != 0)
180 hashcpy(sha1
, blob_sha1
);
184 static int get_blob_sha1_internal(const unsigned char *sha1
, const char *base
,
185 int baselen
, const char *pathname
,
186 unsigned mode
, int stage
)
189 return READ_TREE_RECURSIVE
;
191 if (strncmp(blame_file
, base
, baselen
) ||
192 strcmp(blame_file
+ baselen
, pathname
))
195 hashcpy(blob_sha1
, sha1
);
199 static void get_blob(struct commit
*commit
)
201 struct util_info
*info
= commit
->util
;
207 info
->buf
= read_sha1_file(info
->sha1
, type
, &info
->size
);
210 assert(!strcmp(type
, blob_type
));
213 /* For debugging only */
214 static void print_patch(struct patch
*p
)
217 printf("Num chunks: %d\n", p
->num
);
218 for (i
= 0; i
< p
->num
; i
++) {
219 printf("%d,%d %d,%d\n", p
->chunks
[i
].off1
, p
->chunks
[i
].len1
,
220 p
->chunks
[i
].off2
, p
->chunks
[i
].len2
);
225 /* For debugging only */
226 static void print_map(struct commit
*cmit
, struct commit
*other
)
228 struct util_info
*util
= cmit
->util
;
229 struct util_info
*util2
= other
->util
;
234 util2
->num_lines
? util
->num_lines
: util2
->num_lines
;
237 for (i
= 0; i
< max
; i
++) {
241 if (i
< util
->num_lines
) {
242 num
= util
->line_map
[i
];
247 if (i
< util2
->num_lines
) {
248 int num2
= util2
->line_map
[i
];
249 printf("%d\t", num2
);
250 if (num
!= -1 && num2
!= num
)
260 /* p is a patch from commit to other. */
261 static void fill_line_map(struct commit
*commit
, struct commit
*other
,
264 struct util_info
*util
= commit
->util
;
265 struct util_info
*util2
= other
->util
;
266 int *map
= util
->line_map
;
267 int *map2
= util2
->line_map
;
275 printf("num lines 1: %d num lines 2: %d\n", util
->num_lines
,
278 for (i1
= 0, i2
= 0; i1
< util
->num_lines
; i1
++, i2
++) {
279 struct chunk
*chunk
= NULL
;
280 if (cur_chunk
< p
->num
)
281 chunk
= &p
->chunks
[cur_chunk
];
283 if (chunk
&& chunk
->off1
== i1
) {
284 if (DEBUG
&& i2
!= chunk
->off2
)
285 printf("i2: %d off2: %d\n", i2
, chunk
->off2
);
287 assert(i2
== chunk
->off2
);
299 if (i2
>= util2
->num_lines
)
302 if (map
[i1
] != map2
[i2
] && map
[i1
] != -1) {
304 printf("map: i1: %d %d %p i2: %d %d %p\n",
306 (void *) (i1
!= -1 ? blame_lines
[map
[i1
]] : NULL
),
308 (void *) (i2
!= -1 ? blame_lines
[map2
[i2
]] : NULL
));
309 if (map2
[i2
] != -1 &&
310 blame_lines
[map
[i1
]] &&
311 !blame_lines
[map2
[i2
]])
315 if (map
[i1
] == -1 && map2
[i2
] != -1)
320 printf("l1: %d l2: %d i1: %d i2: %d\n",
321 map
[i1
], map2
[i2
], i1
, i2
);
325 static int map_line(struct commit
*commit
, int line
)
327 struct util_info
*info
= commit
->util
;
328 assert(line
>= 0 && line
< info
->num_lines
);
329 return info
->line_map
[line
];
332 static struct util_info
* get_util(struct commit
*commit
)
334 struct util_info
*util
= commit
->util
;
339 util
= xmalloc(sizeof(struct util_info
));
342 util
->line_map
= NULL
;
343 util
->num_lines
= -1;
344 util
->pathname
= NULL
;
349 static int fill_util_info(struct commit
*commit
)
351 struct util_info
*util
= commit
->util
;
354 assert(util
->pathname
);
356 return !!get_blob_sha1(commit
->tree
, util
->pathname
, util
->sha1
);
359 static void alloc_line_map(struct commit
*commit
)
361 struct util_info
*util
= commit
->util
;
370 for (i
= 0; i
< util
->size
; i
++) {
371 if (util
->buf
[i
] == '\n')
374 if(util
->buf
[util
->size
- 1] != '\n')
377 util
->line_map
= xmalloc(sizeof(int) * util
->num_lines
);
379 for (i
= 0; i
< util
->num_lines
; i
++)
380 util
->line_map
[i
] = -1;
383 static void init_first_commit(struct commit
* commit
, const char* filename
)
385 struct util_info
* util
= commit
->util
;
388 util
->pathname
= filename
;
389 if (fill_util_info(commit
))
390 die("fill_util_info failed");
392 alloc_line_map(commit
);
396 for (i
= 0; i
< util
->num_lines
; i
++)
397 util
->line_map
[i
] = i
;
401 static void process_commits(struct rev_info
*rev
, const char *path
,
402 struct commit
** initial
)
405 struct util_info
* util
;
411 struct commit
* commit
= get_revision(rev
);
413 init_first_commit(commit
, path
);
416 num_blame_lines
= util
->num_lines
;
417 blame_lines
= xmalloc(sizeof(struct commit
*) * num_blame_lines
);
418 blame_contents
= util
->buf
;
419 blame_len
= util
->size
;
421 for (i
= 0; i
< num_blame_lines
; i
++)
422 blame_lines
[i
] = NULL
;
424 lines_left
= num_blame_lines
;
425 blame_p
= xmalloc(sizeof(int) * num_blame_lines
);
426 new_lines
= xmalloc(sizeof(int) * num_blame_lines
);
428 struct commit_list
*parents
;
430 struct util_info
*util
;
433 printf("\nProcessing commit: %d %s\n", num_commits
,
434 sha1_to_hex(commit
->object
.sha1
));
440 memset(blame_p
, 0, sizeof(int) * num_blame_lines
);
443 for (parents
= commit
->parents
;
444 parents
!= NULL
; parents
= parents
->next
)
450 if (fill_util_info(commit
))
453 alloc_line_map(commit
);
456 for (parents
= commit
->parents
;
457 parents
!= NULL
; parents
= parents
->next
) {
458 struct commit
*parent
= parents
->item
;
461 if (parse_commit(parent
) < 0)
462 die("parse_commit error");
465 printf("parent: %s\n",
466 sha1_to_hex(parent
->object
.sha1
));
468 if (fill_util_info(parent
)) {
473 patch
= get_patch(parent
, commit
);
474 alloc_line_map(parent
);
475 fill_line_map(parent
, commit
, patch
);
477 for (i
= 0; i
< patch
->num
; i
++) {
479 for (l
= 0; l
< patch
->chunks
[i
].len2
; l
++) {
481 map_line(commit
, patch
->chunks
[i
].off2
+ l
);
482 if (mapped_line
!= -1) {
483 blame_p
[mapped_line
]++;
484 if (blame_p
[mapped_line
] == num_parents
)
485 new_lines
[new_lines_len
++] = mapped_line
;
493 printf("parents: %d\n", num_parents
);
495 for (i
= 0; i
< new_lines_len
; i
++) {
496 int mapped_line
= new_lines
[i
];
497 if (blame_lines
[mapped_line
] == NULL
) {
498 blame_lines
[mapped_line
] = commit
;
501 printf("blame: mapped: %d i: %d\n",
505 } while ((commit
= get_revision(rev
)) != NULL
);
509 static int compare_tree_path(struct rev_info
* revs
,
510 struct commit
* c1
, struct commit
* c2
)
513 const char* paths
[2];
514 struct util_info
* util
= c2
->util
;
515 paths
[0] = util
->pathname
;
518 diff_tree_setup_paths(get_pathspec(revs
->prefix
, paths
),
520 ret
= rev_compare_tree(revs
, c1
->tree
, c2
->tree
);
521 diff_tree_release_paths(&revs
->pruning
);
526 static int same_tree_as_empty_path(struct rev_info
*revs
, struct tree
* t1
,
530 const char* paths
[2];
534 diff_tree_setup_paths(get_pathspec(revs
->prefix
, paths
),
536 ret
= rev_same_tree_as_empty(revs
, t1
);
537 diff_tree_release_paths(&revs
->pruning
);
541 static const char* find_rename(struct commit
* commit
, struct commit
* parent
)
543 struct util_info
* cutil
= commit
->util
;
544 struct diff_options diff_opts
;
545 const char *paths
[1];
549 printf("find_rename commit: %s ",
550 sha1_to_hex(commit
->object
.sha1
));
551 puts(sha1_to_hex(parent
->object
.sha1
));
554 diff_setup(&diff_opts
);
555 diff_opts
.recursive
= 1;
556 diff_opts
.detect_rename
= DIFF_DETECT_RENAME
;
558 diff_tree_setup_paths(paths
, &diff_opts
);
559 if (diff_setup_done(&diff_opts
) < 0)
560 die("diff_setup_done failed");
562 diff_tree_sha1(commit
->tree
->object
.sha1
, parent
->tree
->object
.sha1
,
564 diffcore_std(&diff_opts
);
566 for (i
= 0; i
< diff_queued_diff
.nr
; i
++) {
567 struct diff_filepair
*p
= diff_queued_diff
.queue
[i
];
569 if (p
->status
== 'R' && !strcmp(p
->one
->path
, cutil
->pathname
)) {
571 printf("rename %s -> %s\n", p
->one
->path
, p
->two
->path
);
579 static void simplify_commit(struct rev_info
*revs
, struct commit
*commit
)
581 struct commit_list
**pp
, *parent
;
586 if (!commit
->parents
) {
587 struct util_info
* util
= commit
->util
;
588 if (!same_tree_as_empty_path(revs
, commit
->tree
,
590 commit
->object
.flags
|= TREECHANGE
;
594 pp
= &commit
->parents
;
595 while ((parent
= *pp
) != NULL
) {
596 struct commit
*p
= parent
->item
;
598 if (p
->object
.flags
& UNINTERESTING
) {
604 switch (compare_tree_path(revs
, p
, commit
)) {
607 commit
->parents
= parent
;
608 get_util(p
)->pathname
= get_util(commit
)->pathname
;
614 struct util_info
* util
= commit
->util
;
615 if (revs
->remove_empty_trees
&&
616 same_tree_as_empty_path(revs
, p
->tree
,
618 const char* new_name
= find_rename(commit
, p
);
620 struct util_info
* putil
= get_util(p
);
621 if (!putil
->pathname
)
622 putil
->pathname
= xstrdup(new_name
);
631 case REV_TREE_DIFFERENT
:
633 if (!get_util(p
)->pathname
)
634 get_util(p
)->pathname
=
635 get_util(commit
)->pathname
;
638 die("bad tree compare for commit %s",
639 sha1_to_hex(commit
->object
.sha1
));
641 commit
->object
.flags
|= TREECHANGE
;
649 unsigned long author_time
;
653 static void get_commit_info(struct commit
* commit
, struct commit_info
* ret
)
657 static char author_buf
[1024];
659 tmp
= strstr(commit
->buffer
, "\nauthor ") + 8;
660 len
= strchr(tmp
, '\n') - tmp
;
661 ret
->author
= author_buf
;
662 memcpy(ret
->author
, tmp
, len
);
669 ret
->author_tz
= tmp
+1;
674 ret
->author_time
= strtoul(tmp
, NULL
, 10);
679 ret
->author_mail
= tmp
+ 1;
684 static const char* format_time(unsigned long time
, const char* tz_str
,
687 static char time_buf
[128];
693 sprintf(time_buf
, "%lu %s", time
, tz_str
);
698 minutes
= tz
< 0 ? -tz
: tz
;
699 minutes
= (minutes
/ 100)*60 + (minutes
% 100);
700 minutes
= tz
< 0 ? -minutes
: minutes
;
701 t
= time
+ minutes
* 60;
704 strftime(time_buf
, sizeof(time_buf
), "%Y-%m-%d %H:%M:%S ", tm
);
705 strcat(time_buf
, tz_str
);
709 static void topo_setter(struct commit
* c
, void* data
)
711 struct util_info
* util
= c
->util
;
712 util
->topo_data
= data
;
715 static void* topo_getter(struct commit
* c
)
717 struct util_info
* util
= c
->util
;
718 return util
->topo_data
;
721 static int read_ancestry(const char *graft_file
,
722 unsigned char **start_sha1
)
724 FILE *fp
= fopen(graft_file
, "r");
728 while (fgets(buf
, sizeof(buf
), fp
)) {
729 /* The format is just "Commit Parent1 Parent2 ...\n" */
730 int len
= strlen(buf
);
731 struct commit_graft
*graft
= read_graft_line(buf
, len
);
732 register_commit_graft(graft
, 0);
734 *start_sha1
= graft
->sha1
;
740 int main(int argc
, const char **argv
)
743 struct commit
*initial
= NULL
;
744 unsigned char sha1
[20], *sha1_p
= NULL
;
746 const char *filename
= NULL
, *commit
= NULL
;
747 char filename_buf
[256];
749 int compatibility
= 0;
750 int show_raw_time
= 0;
752 struct commit
* start_commit
;
754 const char* args
[10];
757 struct commit_info ci
;
760 int longest_file
, longest_author
;
763 const char* prefix
= setup_git_directory();
764 git_config(git_default_config
);
766 for(i
= 1; i
< argc
; i
++) {
768 if(!strcmp(argv
[i
], "-h") ||
769 !strcmp(argv
[i
], "--help"))
771 else if(!strcmp(argv
[i
], "-l") ||
772 !strcmp(argv
[i
], "--long")) {
775 } else if(!strcmp(argv
[i
], "-c") ||
776 !strcmp(argv
[i
], "--compatibility")) {
779 } else if(!strcmp(argv
[i
], "-t") ||
780 !strcmp(argv
[i
], "--time")) {
783 } else if(!strcmp(argv
[i
], "-S")) {
785 !read_ancestry(argv
[i
+ 1], &sha1_p
)) {
791 } else if(!strcmp(argv
[i
], "--")) {
794 } else if(argv
[i
][0] == '-')
812 if (commit
&& sha1_p
)
818 sprintf(filename_buf
, "%s%s", prefix
, filename
);
820 strcpy(filename_buf
, filename
);
821 filename
= filename_buf
;
824 if (get_sha1(commit
, sha1
))
825 die("get_sha1 failed, commit '%s' not found", commit
);
828 start_commit
= lookup_commit_reference(sha1_p
);
829 get_util(start_commit
)->pathname
= filename
;
830 if (fill_util_info(start_commit
)) {
831 printf("%s not found in %s\n", filename
, commit
);
836 init_revisions(&rev
, setup_git_directory());
837 rev
.remove_empty_trees
= 1;
839 rev
.prune_fn
= simplify_commit
;
840 rev
.topo_setter
= topo_setter
;
841 rev
.topo_getter
= topo_getter
;
845 commit_list_insert(start_commit
, &rev
.commits
);
849 diff_tree_setup_paths(args
, &rev
.pruning
);
850 prepare_revision_walk(&rev
);
851 process_commits(&rev
, filename
, &initial
);
853 buf
= blame_contents
;
854 for (max_digits
= 1, i
= 10; i
<= num_blame_lines
+ 1; max_digits
++)
860 for (i
= 0; i
< num_blame_lines
; i
++) {
861 struct commit
*c
= blame_lines
[i
];
867 if (!found_rename
&& strcmp(filename
, u
->pathname
))
869 if (longest_file
< strlen(u
->pathname
))
870 longest_file
= strlen(u
->pathname
);
871 get_commit_info(c
, &ci
);
872 if (longest_author
< strlen(ci
.author
))
873 longest_author
= strlen(ci
.author
);
876 for (i
= 0; i
< num_blame_lines
; i
++) {
877 struct commit
*c
= blame_lines
[i
];
884 get_commit_info(c
, &ci
);
885 fwrite(sha1_to_hex(c
->object
.sha1
), sha1_len
, 1, stdout
);
887 printf("\t(%10s\t%10s\t%d)", ci
.author
,
888 format_time(ci
.author_time
, ci
.author_tz
,
893 printf(" %-*.*s", longest_file
, longest_file
,
895 printf(" (%-*.*s %10s %*d) ",
896 longest_author
, longest_author
, ci
.author
,
897 format_time(ci
.author_time
, ci
.author_tz
,
902 if(i
== num_blame_lines
- 1) {
903 fwrite(buf
, blame_len
- (buf
- blame_contents
),
905 if(blame_contents
[blame_len
-1] != '\n')
908 char* next_buf
= strchr(buf
, '\n') + 1;
909 fwrite(buf
, next_buf
- buf
, 1, stdout
);
915 printf("num read blob: %d\n", num_read_blob
);
916 printf("num get patch: %d\n", num_get_patch
);
917 printf("num commits: %d\n", num_commits
);
918 printf("patch time: %f\n", patch_time
/ 1000000.0);
919 printf("initial: %s\n", sha1_to_hex(initial
->object
.sha1
));