13 struct commit
** blame_lines
;
20 unsigned char sha1
[20]; /* blob sha, not commit! */
28 int off1
, len1
; // ---
29 int off2
, len2
; // +++
38 static void get_blob(struct commit
* commit
);
40 int num_get_patch
= 0;
43 struct patch
* get_patch(struct commit
* commit
, struct commit
* other
)
45 struct patch
* ret
= xmalloc(sizeof(struct patch
));
49 struct util_info
* info_c
= (struct util_info
*) commit
->object
.util
;
50 struct util_info
* info_o
= (struct util_info
*) other
->object
.util
;
52 if(!memcmp(info_c
->sha1
, info_o
->sha1
, 20))
58 FILE* fout
= fopen("/tmp/git-blame-tmp1", "w");
60 die("fopen tmp1 failed: %s", strerror(errno
));
62 if(fwrite(info_c
->buf
, info_c
->size
, 1, fout
) != 1)
63 die("fwrite 1 failed: %s", strerror(errno
));
66 fout
= fopen("/tmp/git-blame-tmp2", "w");
68 die("fopen tmp2 failed: %s", strerror(errno
));
70 if(fwrite(info_o
->buf
, info_o
->size
, 1, fout
) != 1)
71 die("fwrite 2 failed: %s", strerror(errno
));
74 FILE* fin
= popen("diff -u0 /tmp/git-blame-tmp1 /tmp/git-blame-tmp2", "r");
76 die("popen failed: %s", strerror(errno
));
79 while(fgets(buf
, sizeof(buf
), fin
)) {
80 if(buf
[0] != '@' || buf
[1] != '@')
84 printf("chunk line: %s", buf
);
86 ret
->chunks
= xrealloc(ret
->chunks
, sizeof(struct chunk
)*ret
->num
);
87 struct chunk
* chunk
= &ret
->chunks
[ret
->num
-1];
89 assert(!strncmp(buf
, "@@ -", 4));
92 char* sp
= index(start
, ' ');
94 if(index(start
, ',')) {
95 int ret
= sscanf(start
, "%d,%d", &chunk
->off1
, &chunk
->len1
);
98 int ret
= sscanf(start
, "%d", &chunk
->off1
);
105 sp
= index(start
, ' ');
107 if(index(start
, ',')) {
108 int ret
= sscanf(start
, "%d,%d", &chunk
->off2
, &chunk
->len2
);
111 int ret
= sscanf(start
, "%d", &chunk
->off2
);
122 assert(chunk
->off1
>= 0);
123 assert(chunk
->off2
>= 0);
131 void free_patch(struct patch
* p
)
137 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
, int baselen
,
138 const char *pathname
, unsigned mode
, int stage
);
141 static unsigned char blob_sha1
[20];
142 static int get_blob_sha1(struct tree
* t
, const char* pathname
, unsigned char* sha1
)
144 const char *pathspec
[2];
145 pathspec
[0] = pathname
;
147 memset(blob_sha1
, 0, sizeof(blob_sha1
));
148 read_tree_recursive(t
, "", 0, 0, pathspec
, get_blob_sha1_internal
);
151 for(i
= 0; i
< 20; i
++) {
152 if(blob_sha1
[i
] != 0)
159 memcpy(sha1
, blob_sha1
, 20);
163 static int get_blob_sha1_internal(unsigned char *sha1
, const char *base
, int baselen
,
164 const char *pathname
, unsigned mode
, int stage
)
166 // printf("Got blob: %s base: '%s' baselen: %d pathname: '%s' mode: %o stage: %d\n",
167 // sha1_to_hex(sha1), base, baselen, pathname, mode, stage);
170 return READ_TREE_RECURSIVE
;
172 memcpy(blob_sha1
, sha1
, 20);
176 static void get_blob(struct commit
* commit
)
178 struct util_info
* info
= commit
->object
.util
;
184 info
->buf
= read_sha1_file(info
->sha1
, type
, &info
->size
);
185 assert(!strcmp(type
, "blob"));
188 void print_patch(struct patch
* p
)
190 printf("Num chunks: %d\n", p
->num
);
192 for(i
= 0; i
< p
->num
; i
++) {
193 printf("%d,%d %d,%d\n", p
->chunks
[i
].off1
, p
->chunks
[i
].len1
, p
->chunks
[i
].off2
, p
->chunks
[i
].len2
);
198 // p is a patch from commit to other.
199 void fill_line_map(struct commit
* commit
, struct commit
* other
, struct patch
* p
)
201 int num_lines
= ((struct util_info
*) commit
->object
.util
)->num_lines
;
202 int* line_map
= ((struct util_info
*) commit
->object
.util
)->line_map
;
203 int num_lines2
= ((struct util_info
*) other
->object
.util
)->num_lines
;
204 int* line_map2
= ((struct util_info
*) other
->object
.util
)->line_map
;
211 for(i1
= 0; i1
< num_lines
; i1
++)
215 printf("num lines 1: %d num lines 2: %d\n", num_lines
, num_lines2
);
217 for(i1
= 0, i2
= 0; i1
< num_lines
; i1
++, i2
++) {
219 printf("%d %d\n", i1
, i2
);
224 line_map
[i1
] = line_map2
[i2
];
226 struct chunk
* chunk
= NULL
;
227 if(cur_chunk
< p
->num
)
228 chunk
= &p
->chunks
[cur_chunk
];
230 if(chunk
&& chunk
->off1
== i1
) {
242 int map_line(struct commit
* commit
, int line
)
244 struct util_info
* info
= commit
->object
.util
;
245 assert(line
>= 0 && line
< info
->num_lines
);
246 return info
->line_map
[line
];
249 int fill_util_info(struct commit
* commit
, const char* path
)
251 if(commit
->object
.util
)
254 struct util_info
* util
= xmalloc(sizeof(struct util_info
));
257 util
->num_lines
= -1;
258 util
->line_map
= NULL
;
260 commit
->object
.util
= util
;
262 if(get_blob_sha1(commit
->tree
, path
, util
->sha1
))
268 void alloc_line_map(struct commit
* commit
)
270 struct util_info
* util
= commit
->object
.util
;
279 for(i
= 0; i
< util
->size
; i
++) {
280 if(util
->buf
[i
] == '\n')
283 util
->line_map
= xmalloc(sizeof(int)*util
->num_lines
);
286 void copy_line_map(struct commit
* dst
, struct commit
* src
)
288 struct util_info
* u_dst
= dst
->object
.util
;
289 struct util_info
* u_src
= src
->object
.util
;
291 u_dst
->line_map
= u_src
->line_map
;
292 u_dst
->num_lines
= u_src
->num_lines
;
293 u_dst
->buf
= u_src
->buf
;
294 u_dst
->size
= u_src
->size
;
297 void process_commits(struct commit_list
* list
, const char* path
)
302 struct commit
* commit
= pop_commit(&list
);
303 struct commit_list
* parents
;
304 struct util_info
* info
;
306 info
= commit
->object
.util
;
309 printf("\nProcessing commit: %d %s\n", num_commits
, sha1_to_hex(commit
->object
.sha1
));
310 for(parents
= commit
->parents
;
311 parents
!= NULL
; parents
= parents
->next
) {
312 struct commit
* parent
= parents
->item
;
314 if(parse_commit(parent
) < 0)
315 die("parse_commit error");
318 printf("parent: %s\n", sha1_to_hex(parent
->object
.sha1
));
320 if(fill_util_info(parent
, path
))
323 // Temporarily assign everything to the parent.
325 for(i
= 0; i
< num_blame_lines
; i
++) {
326 if(blame_lines
[i
] == commit
) {
328 blame_lines
[i
] = parent
;
335 struct patch
* patch
= get_patch(parent
, commit
);
336 if(patch
->num
== 0) {
337 copy_line_map(parent
, commit
);
339 alloc_line_map(parent
);
340 fill_line_map(parent
, commit
, patch
);
343 for(i
= 0; i
< patch
->num
; i
++) {
345 for(l
= 0; l
< patch
->chunks
[i
].len2
; l
++) {
346 int mapped_line
= map_line(commit
, patch
->chunks
[i
].off2
+ l
);
347 if(mapped_line
!= -1 && blame_lines
[mapped_line
] == parent
)
348 blame_lines
[mapped_line
] = commit
;
357 struct commit_list
* get_commit_list(struct commit
* commit
, const char* pathname
)
359 struct commit_list
* ret
= NULL
;
360 struct commit_list
* process
= NULL
;
361 unsigned char sha1
[20];
363 commit_list_insert(commit
, &process
);
366 struct commit
* com
= pop_commit(&process
);
367 if(com
->object
.flags
& SEEN
)
370 com
->object
.flags
|= SEEN
;
371 commit_list_insert(com
, &ret
);
372 struct commit_list
* parents
;
376 for(parents
= com
->parents
;
377 parents
!= NULL
; parents
= parents
->next
) {
378 struct commit
* parent
= parents
->item
;
380 parse_commit(parent
);
382 if(!get_blob_sha1(parent
->tree
, pathname
, sha1
))
383 commit_list_insert(parent
, &process
);
390 int main(int argc
, const char **argv
)
392 unsigned char sha1
[20];
393 struct commit
*commit
;
394 const char* filename
;
397 setup_git_directory();
400 die("Usage: blame commit-ish file");
402 if (get_sha1(argv
[1], sha1
))
403 die("get_sha1 failed");
405 commit
= lookup_commit_reference(sha1
);
409 struct commit_list
* list
= get_commit_list(commit
, filename
);
410 sort_in_topological_order(&list
, 1);
412 if(fill_util_info(commit
, filename
)) {
413 printf("%s not found in %s\n", filename
, argv
[1]);
416 alloc_line_map(commit
);
418 struct util_info
* util
= commit
->object
.util
;
419 num_blame_lines
= util
->num_lines
;
420 blame_lines
= xmalloc(sizeof(struct commit
*)*num_blame_lines
);
423 for(i
= 0; i
< num_blame_lines
; i
++) {
424 blame_lines
[i
] = commit
;
426 ((struct util_info
*) commit
->object
.util
)->line_map
[i
] = i
;
429 process_commits(list
, filename
);
431 for(i
= 0; i
< num_blame_lines
; i
++) {
432 printf("%d %s\n", i
+1-1, sha1_to_hex(blame_lines
[i
]->object
.sha1
));
433 // printf("%d %s\n", i+1-1, find_unique_abbrev(blame_lines[i]->object.sha1, 6));
437 printf("num get patch: %d\n", num_get_patch
);
438 printf("num commits: %d\n", num_commits
);