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
);
230 /* For debugging only */
231 static void print_map(struct commit
*cmit
, struct commit
*other
)
233 struct util_info
*util
= cmit
->object
.util
;
234 struct util_info
*util2
= other
->object
.util
;
239 util2
->num_lines
? util
->num_lines
: util2
->num_lines
;
242 for (i
= 0; i
< max
; i
++) {
246 if (i
< util
->num_lines
) {
247 num
= util
->line_map
[i
];
252 if (i
< util2
->num_lines
) {
253 int num2
= util2
->line_map
[i
];
254 printf("%d\t", num2
);
255 if (num
!= -1 && num2
!= num
)
265 // p is a patch from commit to other.
266 static void fill_line_map(struct commit
*commit
, struct commit
*other
,
269 struct util_info
*util
= commit
->object
.util
;
270 struct util_info
*util2
= other
->object
.util
;
271 int *map
= util
->line_map
;
272 int *map2
= util2
->line_map
;
280 printf("num lines 1: %d num lines 2: %d\n", util
->num_lines
,
283 for (i1
= 0, i2
= 0; i1
< util
->num_lines
; i1
++, i2
++) {
284 struct chunk
*chunk
= NULL
;
285 if (cur_chunk
< p
->num
)
286 chunk
= &p
->chunks
[cur_chunk
];
288 if (chunk
&& chunk
->off1
== i1
) {
289 if (DEBUG
&& i2
!= chunk
->off2
)
290 printf("i2: %d off2: %d\n", i2
, chunk
->off2
);
292 assert(i2
== chunk
->off2
);
304 if (i2
>= util2
->num_lines
)
307 if (map
[i1
] != map2
[i2
] && map
[i1
] != -1) {
309 printf("map: i1: %d %d %p i2: %d %d %p\n",
311 i1
!= -1 ? blame_lines
[map
[i1
]] : NULL
,
313 i2
!= -1 ? blame_lines
[map2
[i2
]] : NULL
);
314 if (map2
[i2
] != -1 &&
315 blame_lines
[map
[i1
]] &&
316 !blame_lines
[map2
[i2
]])
320 if (map
[i1
] == -1 && map2
[i2
] != -1)
325 printf("l1: %d l2: %d i1: %d i2: %d\n",
326 map
[i1
], map2
[i2
], i1
, i2
);
330 static int map_line(struct commit
*commit
, int line
)
332 struct util_info
*info
= commit
->object
.util
;
333 assert(line
>= 0 && line
< info
->num_lines
);
334 return info
->line_map
[line
];
337 static int fill_util_info(struct commit
*commit
, const char *path
)
339 struct util_info
*util
;
340 if (commit
->object
.util
)
343 util
= xmalloc(sizeof(struct util_info
));
345 if (get_blob_sha1(commit
->tree
, path
, util
->sha1
)) {
351 util
->line_map
= NULL
;
352 util
->num_lines
= -1;
353 commit
->object
.util
= util
;
358 static void alloc_line_map(struct commit
*commit
)
360 struct util_info
*util
= commit
->object
.util
;
369 for (i
= 0; i
< util
->size
; i
++) {
370 if (util
->buf
[i
] == '\n')
373 if(util
->buf
[util
->size
- 1] != '\n')
376 util
->line_map
= xmalloc(sizeof(int) * util
->num_lines
);
378 for (i
= 0; i
< util
->num_lines
; i
++)
379 util
->line_map
[i
] = -1;
382 static void init_first_commit(struct commit
* commit
, const char* filename
)
384 struct util_info
* util
;
387 if (fill_util_info(commit
, filename
))
388 die("fill_util_info failed");
390 alloc_line_map(commit
);
392 util
= commit
->object
.util
;
393 num_blame_lines
= util
->num_lines
;
395 for (i
= 0; i
< num_blame_lines
; i
++)
396 util
->line_map
[i
] = i
;
400 static void process_commits(struct rev_info
*rev
, const char *path
,
401 struct commit
** initial
)
404 struct util_info
* util
;
410 struct commit
* commit
= get_revision(rev
);
412 init_first_commit(commit
, path
);
414 util
= commit
->object
.util
;
415 num_blame_lines
= util
->num_lines
;
416 blame_lines
= xmalloc(sizeof(struct commit
*) * num_blame_lines
);
417 for (i
= 0; i
< num_blame_lines
; i
++)
418 blame_lines
[i
] = NULL
;
420 lines_left
= num_blame_lines
;
421 blame_p
= xmalloc(sizeof(int) * num_blame_lines
);
422 new_lines
= xmalloc(sizeof(int) * num_blame_lines
);
424 struct commit_list
*parents
;
426 struct util_info
*util
;
429 printf("\nProcessing commit: %d %s\n", num_commits
,
430 sha1_to_hex(commit
->object
.sha1
));
436 memset(blame_p
, 0, sizeof(int) * num_blame_lines
);
439 for (parents
= commit
->parents
;
440 parents
!= NULL
; parents
= parents
->next
)
446 if(fill_util_info(commit
, path
))
449 alloc_line_map(commit
);
450 util
= commit
->object
.util
;
452 for (parents
= commit
->parents
;
453 parents
!= NULL
; parents
= parents
->next
) {
454 struct commit
*parent
= parents
->item
;
457 if (parse_commit(parent
) < 0)
458 die("parse_commit error");
461 printf("parent: %s\n",
462 sha1_to_hex(parent
->object
.sha1
));
464 if(fill_util_info(parent
, path
)) {
469 patch
= get_patch(parent
, commit
);
470 alloc_line_map(parent
);
471 fill_line_map(parent
, commit
, patch
);
473 for (i
= 0; i
< patch
->num
; i
++) {
475 for (l
= 0; l
< patch
->chunks
[i
].len2
; l
++) {
477 map_line(commit
, patch
->chunks
[i
].off2
+ l
);
478 if (mapped_line
!= -1) {
479 blame_p
[mapped_line
]++;
480 if (blame_p
[mapped_line
] == num_parents
)
481 new_lines
[new_lines_len
++] = mapped_line
;
489 printf("parents: %d\n", num_parents
);
491 for (i
= 0; i
< new_lines_len
; i
++) {
492 int mapped_line
= new_lines
[i
];
493 if (blame_lines
[mapped_line
] == NULL
) {
494 blame_lines
[mapped_line
] = commit
;
497 printf("blame: mapped: %d i: %d\n",
501 } while ((commit
= get_revision(rev
)) != NULL
);
504 int main(int argc
, const char **argv
)
507 struct commit
*initial
= NULL
;
508 unsigned char sha1
[20];
509 const char* filename
;
511 const char* args
[10];
514 setup_git_directory();
517 die("Usage: blame commit-ish file");
523 struct commit
* commit
;
524 if (get_sha1(argv
[1], sha1
))
525 die("get_sha1 failed");
526 commit
= lookup_commit_reference(sha1
);
528 if (fill_util_info(commit
, filename
)) {
529 printf("%s not found in %s\n", filename
, argv
[1]);
535 args
[num_args
++] = NULL
;
536 args
[num_args
++] = "--topo-order";
537 args
[num_args
++] = "--remove-empty";
538 args
[num_args
++] = argv
[1];
539 args
[num_args
++] = "--";
540 args
[num_args
++] = filename
;
541 args
[num_args
] = NULL
;
543 setup_revisions(num_args
, args
, &rev
, "HEAD");
544 prepare_revision_walk(&rev
);
545 process_commits(&rev
, filename
, &initial
);
547 for (i
= 0; i
< num_blame_lines
; i
++) {
548 struct commit
*c
= blame_lines
[i
];
552 printf("%d %.8s\n", i
, sha1_to_hex(c
->object
.sha1
));
553 // printf("%d %s\n", i, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
557 printf("num get patch: %d\n", num_get_patch
);
558 printf("num commits: %d\n", num_commits
);
559 printf("patch time: %f\n", patch_time
/ 1000000.0);
560 printf("initial: %s\n", sha1_to_hex(initial
->object
.sha1
));