14 struct commit
** blame_lines
;
21 unsigned char sha1
[20]; /* blob sha, not commit! */
29 int off1
, len1
; // ---
30 int off2
, len2
; // +++
39 static void get_blob(struct commit
* commit
);
41 int num_get_patch
= 0;
44 struct patch
* get_patch(struct commit
* commit
, struct commit
* other
)
46 struct patch
* ret
= xmalloc(sizeof(struct patch
));
50 struct util_info
* info_c
= (struct util_info
*) commit
->object
.util
;
51 struct util_info
* info_o
= (struct util_info
*) other
->object
.util
;
53 if(!memcmp(info_c
->sha1
, info_o
->sha1
, 20))
59 FILE* fout
= fopen("/tmp/git-blame-tmp1", "w");
61 die("fopen tmp1 failed: %s", strerror(errno
));
63 if(fwrite(info_c
->buf
, info_c
->size
, 1, fout
) != 1)
64 die("fwrite 1 failed: %s", strerror(errno
));
67 fout
= fopen("/tmp/git-blame-tmp2", "w");
69 die("fopen tmp2 failed: %s", strerror(errno
));
71 if(fwrite(info_o
->buf
, info_o
->size
, 1, fout
) != 1)
72 die("fwrite 2 failed: %s", strerror(errno
));
75 FILE* fin
= popen("diff -u0 /tmp/git-blame-tmp1 /tmp/git-blame-tmp2", "r");
77 die("popen failed: %s", strerror(errno
));
80 while(fgets(buf
, sizeof(buf
), fin
)) {
81 if(buf
[0] != '@' || buf
[1] != '@')
85 printf("chunk line: %s", buf
);
87 ret
->chunks
= xrealloc(ret
->chunks
, sizeof(struct chunk
)*ret
->num
);
88 struct chunk
* chunk
= &ret
->chunks
[ret
->num
-1];
90 assert(!strncmp(buf
, "@@ -", 4));
93 char* sp
= index(start
, ' ');
95 if(index(start
, ',')) {
96 int ret
= sscanf(start
, "%d,%d", &chunk
->off1
, &chunk
->len1
);
99 int ret
= sscanf(start
, "%d", &chunk
->off1
);
106 sp
= index(start
, ' ');
108 if(index(start
, ',')) {
109 int ret
= sscanf(start
, "%d,%d", &chunk
->off2
, &chunk
->len2
);
112 int ret
= sscanf(start
, "%d", &chunk
->off2
);
123 assert(chunk
->off1
>= 0);
124 assert(chunk
->off2
>= 0);
132 void free_patch(struct patch
* p
)
138 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
, int baselen
,
139 const char *pathname
, unsigned mode
, int stage
);
142 static unsigned char blob_sha1
[20];
143 static int get_blob_sha1(struct tree
* t
, const char* pathname
, unsigned char* sha1
)
145 const char *pathspec
[2];
146 pathspec
[0] = pathname
;
148 memset(blob_sha1
, 0, sizeof(blob_sha1
));
149 read_tree_recursive(t
, "", 0, 0, pathspec
, get_blob_sha1_internal
);
152 for(i
= 0; i
< 20; i
++) {
153 if(blob_sha1
[i
] != 0)
160 memcpy(sha1
, blob_sha1
, 20);
164 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
, int baselen
,
165 const char *pathname
, unsigned mode
, int stage
)
167 // printf("Got blob: %s base: '%s' baselen: %d pathname: '%s' mode: %o stage: %d\n",
168 // sha1_to_hex(sha1), base, baselen, pathname, mode, stage);
171 return READ_TREE_RECURSIVE
;
173 memcpy(blob_sha1
, sha1
, 20);
177 static void get_blob(struct commit
* commit
)
179 struct util_info
* info
= commit
->object
.util
;
185 info
->buf
= read_sha1_file(info
->sha1
, type
, &info
->size
);
186 assert(!strcmp(type
, "blob"));
189 void print_patch(struct patch
* p
)
191 printf("Num chunks: %d\n", p
->num
);
193 for(i
= 0; i
< p
->num
; i
++) {
194 printf("%d,%d %d,%d\n", p
->chunks
[i
].off1
, p
->chunks
[i
].len1
, p
->chunks
[i
].off2
, p
->chunks
[i
].len2
);
199 // p is a patch from commit to other.
200 void fill_line_map(struct commit
* commit
, struct commit
* other
, struct patch
* p
)
202 int num_lines
= ((struct util_info
*) commit
->object
.util
)->num_lines
;
203 int* line_map
= ((struct util_info
*) commit
->object
.util
)->line_map
;
204 int num_lines2
= ((struct util_info
*) other
->object
.util
)->num_lines
;
205 int* line_map2
= ((struct util_info
*) other
->object
.util
)->line_map
;
212 for(i1
= 0; i1
< num_lines
; i1
++)
216 printf("num lines 1: %d num lines 2: %d\n", num_lines
, num_lines2
);
218 for(i1
= 0, i2
= 0; i1
< num_lines
; i1
++, i2
++) {
220 printf("%d %d\n", i1
, i2
);
225 line_map
[i1
] = line_map2
[i2
];
227 struct chunk
* chunk
= NULL
;
228 if(cur_chunk
< p
->num
)
229 chunk
= &p
->chunks
[cur_chunk
];
231 if(chunk
&& chunk
->off1
== i1
) {
243 int map_line(struct commit
* commit
, int line
)
245 struct util_info
* info
= commit
->object
.util
;
246 assert(line
>= 0 && line
< info
->num_lines
);
247 return info
->line_map
[line
];
250 int fill_util_info(struct commit
* commit
, const char* path
)
252 if(commit
->object
.util
)
255 struct util_info
* util
= xmalloc(sizeof(struct util_info
));
258 util
->num_lines
= -1;
259 util
->line_map
= NULL
;
261 commit
->object
.util
= util
;
263 if(get_blob_sha1(commit
->tree
, path
, util
->sha1
))
269 void alloc_line_map(struct commit
* commit
)
271 struct util_info
* util
= commit
->object
.util
;
280 for(i
= 0; i
< util
->size
; i
++) {
281 if(util
->buf
[i
] == '\n')
284 util
->line_map
= xmalloc(sizeof(int)*util
->num_lines
);
287 void copy_line_map(struct commit
* dst
, struct commit
* src
)
289 struct util_info
* u_dst
= dst
->object
.util
;
290 struct util_info
* u_src
= src
->object
.util
;
292 u_dst
->line_map
= u_src
->line_map
;
293 u_dst
->num_lines
= u_src
->num_lines
;
294 u_dst
->buf
= u_src
->buf
;
295 u_dst
->size
= u_src
->size
;
298 void process_commits(struct commit_list
* list
, const char* path
)
303 struct commit
* commit
= pop_commit(&list
);
304 struct commit_list
* parents
;
305 struct util_info
* info
;
307 info
= commit
->object
.util
;
310 printf("\nProcessing commit: %d %s\n", num_commits
, sha1_to_hex(commit
->object
.sha1
));
311 for(parents
= commit
->parents
;
312 parents
!= NULL
; parents
= parents
->next
) {
313 struct commit
* parent
= parents
->item
;
315 if(parse_commit(parent
) < 0)
316 die("parse_commit error");
319 printf("parent: %s\n", sha1_to_hex(parent
->object
.sha1
));
321 if(fill_util_info(parent
, path
))
324 // Temporarily assign everything to the parent.
326 for(i
= 0; i
< num_blame_lines
; i
++) {
327 if(blame_lines
[i
] == commit
) {
329 blame_lines
[i
] = parent
;
336 struct patch
* patch
= get_patch(parent
, commit
);
337 if(patch
->num
== 0) {
338 copy_line_map(parent
, commit
);
340 alloc_line_map(parent
);
341 fill_line_map(parent
, commit
, patch
);
344 for(i
= 0; i
< patch
->num
; i
++) {
346 for(l
= 0; l
< patch
->chunks
[i
].len2
; l
++) {
347 int mapped_line
= map_line(commit
, patch
->chunks
[i
].off2
+ l
);
348 if(mapped_line
!= -1 && blame_lines
[mapped_line
] == parent
)
349 blame_lines
[mapped_line
] = commit
;
358 struct commit_list
* get_commit_list(struct commit
* commit
, const char* pathname
)
360 struct commit_list
* ret
= NULL
;
361 struct commit_list
* process
= NULL
;
362 unsigned char sha1
[20];
364 commit_list_insert(commit
, &process
);
367 struct commit
* com
= pop_commit(&process
);
368 if(com
->object
.flags
& SEEN
)
371 com
->object
.flags
|= SEEN
;
372 commit_list_insert(com
, &ret
);
373 struct commit_list
* parents
;
377 for(parents
= com
->parents
;
378 parents
!= NULL
; parents
= parents
->next
) {
379 struct commit
* parent
= parents
->item
;
381 parse_commit(parent
);
383 if(!get_blob_sha1(parent
->tree
, pathname
, sha1
))
384 commit_list_insert(parent
, &process
);
391 int main(int argc
, const char **argv
)
393 unsigned char sha1
[20];
394 struct commit
*commit
;
395 const char* filename
;
398 setup_git_directory();
401 die("Usage: blame commit-ish file");
403 if (get_sha1(argv
[1], sha1
))
404 die("get_sha1 failed");
406 commit
= lookup_commit_reference(sha1
);
410 struct commit_list
* list
= get_commit_list(commit
, filename
);
411 sort_in_topological_order(&list
, 1);
413 if(fill_util_info(commit
, filename
)) {
414 printf("%s not found in %s\n", filename
, argv
[1]);
417 alloc_line_map(commit
);
419 struct util_info
* util
= commit
->object
.util
;
420 num_blame_lines
= util
->num_lines
;
421 blame_lines
= xmalloc(sizeof(struct commit
*)*num_blame_lines
);
424 for(i
= 0; i
< num_blame_lines
; i
++) {
425 blame_lines
[i
] = commit
;
427 ((struct util_info
*) commit
->object
.util
)->line_map
[i
] = i
;
430 process_commits(list
, filename
);
432 for(i
= 0; i
< num_blame_lines
; i
++) {
433 printf("%d %s\n", i
+1-1, sha1_to_hex(blame_lines
[i
]->object
.sha1
));
434 // printf("%d %s\n", i+1-1, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
438 printf("num get patch: %d\n", num_get_patch
);
439 printf("num commits: %d\n", num_commits
);