2 * Copyright (C) 2006, Fredrik Kuivinen <freku045@student.liu.se>
20 struct commit
**blame_lines
;
25 unsigned char sha1
[20]; /* blob sha, not commit! */
33 int off1
, len1
; // ---
34 int off2
, len2
; // +++
42 static void get_blob(struct commit
*commit
);
44 /* Only used for statistics */
45 static int num_get_patch
= 0;
46 static int num_commits
= 0;
47 static int patch_time
= 0;
49 #define TEMPFILE_PATH_LEN 60
50 static struct patch
*get_patch(struct commit
*commit
, struct commit
*other
)
53 struct util_info
*info_c
= (struct util_info
*)commit
->object
.util
;
54 struct util_info
*info_o
= (struct util_info
*)other
->object
.util
;
55 char tmp_path1
[TEMPFILE_PATH_LEN
], tmp_path2
[TEMPFILE_PATH_LEN
];
56 char diff_cmd
[TEMPFILE_PATH_LEN
*2 + 20];
57 struct timeval tv_start
, tv_end
;
62 ret
= xmalloc(sizeof(struct patch
));
69 gettimeofday(&tv_start
, NULL
);
71 fd
= git_mkstemp(tmp_path1
, TEMPFILE_PATH_LEN
, "git-blame-XXXXXX");
73 die("unable to create temp-file: %s", strerror(errno
));
75 if (xwrite(fd
, info_c
->buf
, info_c
->size
) != info_c
->size
)
76 die("write failed: %s", strerror(errno
));
79 fd
= git_mkstemp(tmp_path2
, TEMPFILE_PATH_LEN
, "git-blame-XXXXXX");
81 die("unable to create temp-file: %s", strerror(errno
));
83 if (xwrite(fd
, info_o
->buf
, info_o
->size
) != info_o
->size
)
84 die("write failed: %s", strerror(errno
));
87 sprintf(diff_cmd
, "diff -u0 %s %s", tmp_path1
, tmp_path2
);
88 fin
= popen(diff_cmd
, "r");
90 die("popen failed: %s", strerror(errno
));
92 while (fgets(buf
, sizeof(buf
), fin
)) {
96 if (buf
[0] != '@' || buf
[1] != '@')
100 printf("chunk line: %s", buf
);
102 ret
->chunks
= xrealloc(ret
->chunks
,
103 sizeof(struct chunk
) * ret
->num
);
104 chunk
= &ret
->chunks
[ret
->num
- 1];
106 assert(!strncmp(buf
, "@@ -", 4));
109 sp
= index(start
, ' ');
111 if (index(start
, ',')) {
113 sscanf(start
, "%d,%d", &chunk
->off1
, &chunk
->len1
);
116 int ret
= sscanf(start
, "%d", &chunk
->off1
);
123 sp
= index(start
, ' ');
125 if (index(start
, ',')) {
127 sscanf(start
, "%d,%d", &chunk
->off2
, &chunk
->len2
);
130 int ret
= sscanf(start
, "%d", &chunk
->off2
);
136 if (chunk
->len1
== 0)
138 if (chunk
->len2
== 0)
146 assert(chunk
->off1
>= 0);
147 assert(chunk
->off2
>= 0);
153 gettimeofday(&tv_end
, NULL
);
154 patch_time
+= 1000000 * (tv_end
.tv_sec
- tv_start
.tv_sec
) +
155 tv_end
.tv_usec
- tv_start
.tv_usec
;
161 static void free_patch(struct patch
*p
)
167 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
,
168 int baselen
, const char *pathname
,
169 unsigned mode
, int stage
);
171 static unsigned char blob_sha1
[20];
172 static int get_blob_sha1(struct tree
*t
, const char *pathname
,
176 const char *pathspec
[2];
177 pathspec
[0] = pathname
;
179 memset(blob_sha1
, 0, sizeof(blob_sha1
));
180 read_tree_recursive(t
, "", 0, 0, pathspec
, get_blob_sha1_internal
);
182 for (i
= 0; i
< 20; i
++) {
183 if (blob_sha1
[i
] != 0)
190 memcpy(sha1
, blob_sha1
, 20);
194 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
,
195 int baselen
, const char *pathname
,
196 unsigned mode
, int stage
)
199 return READ_TREE_RECURSIVE
;
201 memcpy(blob_sha1
, sha1
, 20);
205 static void get_blob(struct commit
*commit
)
207 struct util_info
*info
= commit
->object
.util
;
213 info
->buf
= read_sha1_file(info
->sha1
, type
, &info
->size
);
215 assert(!strcmp(type
, "blob"));
218 /* For debugging only */
219 static void print_patch(struct patch
*p
)
222 printf("Num chunks: %d\n", p
->num
);
223 for (i
= 0; i
< p
->num
; i
++) {
224 printf("%d,%d %d,%d\n", p
->chunks
[i
].off1
, p
->chunks
[i
].len1
,
225 p
->chunks
[i
].off2
, p
->chunks
[i
].len2
);
229 /* For debugging only */
230 static void print_map(struct commit
*cmit
, struct commit
*other
)
232 struct util_info
*util
= cmit
->object
.util
;
233 struct util_info
*util2
= other
->object
.util
;
238 util2
->num_lines
? util
->num_lines
: util2
->num_lines
;
241 for (i
= 0; i
< max
; i
++) {
245 if (i
< util
->num_lines
) {
246 num
= util
->line_map
[i
];
251 if (i
< util2
->num_lines
) {
252 int num2
= util2
->line_map
[i
];
253 printf("%d\t", num2
);
254 if (num
!= -1 && num2
!= num
)
263 // p is a patch from commit to other.
264 static void fill_line_map(struct commit
*commit
, struct commit
*other
,
267 struct util_info
*util
= commit
->object
.util
;
268 struct util_info
*util2
= other
->object
.util
;
269 int *map
= util
->line_map
;
270 int *map2
= util2
->line_map
;
278 printf("num lines 1: %d num lines 2: %d\n", util
->num_lines
,
281 for (i1
= 0, i2
= 0; i1
< util
->num_lines
; i1
++, i2
++) {
282 struct chunk
*chunk
= NULL
;
283 if (cur_chunk
< p
->num
)
284 chunk
= &p
->chunks
[cur_chunk
];
286 if (chunk
&& chunk
->off1
== i1
) {
287 if (DEBUG
&& i2
!= chunk
->off2
)
288 printf("i2: %d off2: %d\n", i2
, chunk
->off2
);
290 assert(i2
== chunk
->off2
);
302 if (i2
>= util2
->num_lines
)
305 if (map
[i1
] != map2
[i2
] && map
[i1
] != -1) {
307 printf("map: i1: %d %d %p i2: %d %d %p\n",
309 i1
!= -1 ? blame_lines
[map
[i1
]] : NULL
,
311 i2
!= -1 ? blame_lines
[map2
[i2
]] : NULL
);
312 if (map2
[i2
] != -1 &&
313 blame_lines
[map
[i1
]] &&
314 !blame_lines
[map2
[i2
]])
318 if (map
[i1
] == -1 && map2
[i2
] != -1)
323 printf("l1: %d l2: %d i1: %d i2: %d\n",
324 map
[i1
], map2
[i2
], i1
, i2
);
328 static int map_line(struct commit
*commit
, int line
)
330 struct util_info
*info
= commit
->object
.util
;
331 assert(line
>= 0 && line
< info
->num_lines
);
332 return info
->line_map
[line
];
335 static int fill_util_info(struct commit
*commit
, const char *path
)
337 struct util_info
*util
;
338 if (commit
->object
.util
)
341 util
= xmalloc(sizeof(struct util_info
));
343 if (get_blob_sha1(commit
->tree
, path
, util
->sha1
)) {
349 util
->line_map
= NULL
;
350 util
->num_lines
= -1;
351 commit
->object
.util
= util
;
356 static void alloc_line_map(struct commit
*commit
)
358 struct util_info
*util
= commit
->object
.util
;
367 for (i
= 0; i
< util
->size
; i
++) {
368 if (util
->buf
[i
] == '\n')
371 if(util
->buf
[util
->size
- 1] != '\n')
374 util
->line_map
= xmalloc(sizeof(int) * util
->num_lines
);
376 for (i
= 0; i
< util
->num_lines
; i
++)
377 util
->line_map
[i
] = -1;
380 static void init_first_commit(struct commit
* commit
, const char* filename
)
382 struct util_info
* util
;
385 if (fill_util_info(commit
, filename
))
386 die("fill_util_info failed");
388 alloc_line_map(commit
);
390 util
= commit
->object
.util
;
391 num_blame_lines
= util
->num_lines
;
393 for (i
= 0; i
< num_blame_lines
; i
++)
394 util
->line_map
[i
] = i
;
398 static void process_commits(struct rev_info
*rev
, const char *path
,
399 struct commit
** initial
)
402 struct util_info
* util
;
408 struct commit
* commit
= get_revision(rev
);
410 init_first_commit(commit
, path
);
412 util
= commit
->object
.util
;
413 num_blame_lines
= util
->num_lines
;
414 blame_lines
= xmalloc(sizeof(struct commit
*) * num_blame_lines
);
415 for (i
= 0; i
< num_blame_lines
; i
++)
416 blame_lines
[i
] = NULL
;
418 lines_left
= num_blame_lines
;
419 blame_p
= xmalloc(sizeof(int) * num_blame_lines
);
420 new_lines
= xmalloc(sizeof(int) * num_blame_lines
);
422 struct commit_list
*parents
;
424 struct util_info
*util
;
427 printf("\nProcessing commit: %d %s\n", num_commits
,
428 sha1_to_hex(commit
->object
.sha1
));
434 memset(blame_p
, 0, sizeof(int) * num_blame_lines
);
437 for (parents
= commit
->parents
;
438 parents
!= NULL
; parents
= parents
->next
)
444 if(fill_util_info(commit
, path
))
447 alloc_line_map(commit
);
448 util
= commit
->object
.util
;
450 for (parents
= commit
->parents
;
451 parents
!= NULL
; parents
= parents
->next
) {
452 struct commit
*parent
= parents
->item
;
455 if (parse_commit(parent
) < 0)
456 die("parse_commit error");
459 printf("parent: %s\n",
460 sha1_to_hex(parent
->object
.sha1
));
462 if(fill_util_info(parent
, path
)) {
467 patch
= get_patch(parent
, commit
);
468 alloc_line_map(parent
);
469 fill_line_map(parent
, commit
, patch
);
471 for (i
= 0; i
< patch
->num
; i
++) {
473 for (l
= 0; l
< patch
->chunks
[i
].len2
; l
++) {
475 map_line(commit
, patch
->chunks
[i
].off2
+ l
);
476 if (mapped_line
!= -1) {
477 blame_p
[mapped_line
]++;
478 if (blame_p
[mapped_line
] == num_parents
)
479 new_lines
[new_lines_len
++] = mapped_line
;
487 printf("parents: %d\n", num_parents
);
489 for (i
= 0; i
< new_lines_len
; i
++) {
490 int mapped_line
= new_lines
[i
];
491 if (blame_lines
[mapped_line
] == NULL
) {
492 blame_lines
[mapped_line
] = commit
;
495 printf("blame: mapped: %d i: %d\n",
499 } while ((commit
= get_revision(rev
)) != NULL
);
502 int main(int argc
, const char **argv
)
505 struct commit
*initial
= NULL
;
506 unsigned char sha1
[20];
507 const char* filename
;
509 const char* args
[10];
512 setup_git_directory();
515 die("Usage: blame commit-ish file");
521 struct commit
* commit
;
522 if (get_sha1(argv
[1], sha1
))
523 die("get_sha1 failed");
524 commit
= lookup_commit_reference(sha1
);
526 if (fill_util_info(commit
, filename
)) {
527 printf("%s not found in %s\n", filename
, argv
[1]);
533 args
[num_args
++] = NULL
;
534 args
[num_args
++] = "--topo-order";
535 args
[num_args
++] = "--remove-empty";
536 args
[num_args
++] = argv
[1];
537 args
[num_args
++] = "--";
538 args
[num_args
++] = filename
;
539 args
[num_args
] = NULL
;
541 setup_revisions(num_args
, args
, &rev
, "HEAD");
542 prepare_revision_walk(&rev
);
543 process_commits(&rev
, filename
, &initial
);
545 for (i
= 0; i
< num_blame_lines
; i
++) {
546 struct commit
*c
= blame_lines
[i
];
550 printf("%d %.8s\n", i
, sha1_to_hex(c
->object
.sha1
));
551 // printf("%d %s\n", i, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
555 printf("num get patch: %d\n", num_get_patch
);
556 printf("num commits: %d\n", num_commits
);
557 printf("patch time: %f\n", patch_time
/ 1000000.0);
558 printf("initial: %s\n", sha1_to_hex(initial
->object
.sha1
));