source src/blame_git.c
Line | Flow | Count | Block(s) | Source |
---|---|---|---|---|
1 | - | /* | ||
2 | - | * Copyright (C) the libgit2 contributors. All rights reserved. | ||
3 | - | * | ||
4 | - | * This file is part of libgit2, distributed under the GNU GPL v2 with | ||
5 | - | * a Linking Exception. For full terms see the included COPYING file. | ||
6 | - | */ | ||
7 | - | |||
8 | - | #include "blame_git.h" | ||
9 | - | |||
10 | - | #include "commit.h" | ||
11 | - | #include "blob.h" | ||
12 | - | #include "xdiff/xinclude.h" | ||
13 | - | #include "diff_xdiff.h" | ||
14 | - | |||
15 | - | /* | ||
16 | - | * Origin is refcounted and usually we keep the blob contents to be | ||
17 | - | * reused. | ||
18 | - | */ | ||
19 | 33695 | 2 | static git_blame__origin *origin_incref(git_blame__origin *o) | |
20 | - | { | ||
21 | 33695 | 2 | if (o) | |
22 | 33695 | 3 | o->refcnt++; | |
23 | 33695 | 4 | return o; | |
24 | - | } | ||
25 | - | |||
26 | 38956 | 2 | static void origin_decref(git_blame__origin *o) | |
27 | - | { | ||
28 | 38956 | 2,3 | if (o && --o->refcnt <= 0) { | |
29 | 3386 | 4 | if (o->previous) | |
30 | 172 | 5 | origin_decref(o->previous); | |
31 | 3386 | 6 | git_blob_free(o->blob); | |
32 | 3386 | 7 | git_commit_free(o->commit); | |
33 | 3386 | 8 | git__free(o); | |
34 | - | } | ||
35 | 38956 | 9 | } | |
36 | - | |||
37 | - | /* Given a commit and a path in it, create a new origin structure. */ | ||
38 | 3393 | 2 | static int make_origin(git_blame__origin **out, git_commit *commit, const char *path) | |
39 | - | { | ||
40 | - | git_blame__origin *o; | ||
41 | - | git_object *blob; | ||
42 | 3393 | 2 | size_t path_len = strlen(path), alloc_len; | |
43 | 3393 | 2 | int error = 0; | |
44 | - | |||
45 | 3393 | 2,3 | if ((error = git_object_lookup_bypath(&blob, (git_object*)commit, | |
46 | - | path, GIT_OBJECT_BLOB)) < 0) | ||
47 | 7 | 4 | return error; | |
48 | - | |||
49 | 3386 | 5-11 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, sizeof(*o), path_len); | |
50 | 3386 | 12-18 | GIT_ERROR_CHECK_ALLOC_ADD(&alloc_len, alloc_len, 1); | |
51 | 3386 | 19 | o = git__calloc(1, alloc_len); | |
52 | 3386 | 20,21 | GIT_ERROR_CHECK_ALLOC(o); | |
53 | - | |||
54 | 3386 | 22 | o->commit = commit; | |
55 | 3386 | 22 | o->blob = (git_blob *) blob; | |
56 | 3386 | 22 | o->refcnt = 1; | |
57 | 3386 | 22 | strcpy(o->path, path); | |
58 | - | |||
59 | 3386 | 22 | *out = o; | |
60 | - | |||
61 | 3386 | 22 | return 0; | |
62 | - | } | ||
63 | - | |||
64 | - | /* Locate an existing origin or create a new one. */ | ||
65 | 3143 | 2 | int git_blame__get_origin( | |
66 | - | git_blame__origin **out, | ||
67 | - | git_blame *blame, | ||
68 | - | git_commit *commit, | ||
69 | - | const char *path) | ||
70 | - | { | ||
71 | - | git_blame__entry *e; | ||
72 | - | |||
73 | 85947 | 2,7,8 | for (e = blame->ent; e; e = e->next) { | |
74 | 82804 | 3,4 | if (e->suspect->commit == commit && !strcmp(e->suspect->path, path)) { | |
75 | ##### | 5,6 | *out = origin_incref(e->suspect); | |
76 | - | } | ||
77 | - | } | ||
78 | 3143 | 9 | return make_origin(out, commit, path); | |
79 | - | } | ||
80 | - | |||
81 | - | typedef struct blame_chunk_cb_data { | ||
82 | - | git_blame *blame; | ||
83 | - | git_blame__origin *target; | ||
84 | - | git_blame__origin *parent; | ||
85 | - | long tlno; | ||
86 | - | long plno; | ||
87 | - | }blame_chunk_cb_data; | ||
88 | - | |||
89 | 175619 | 2 | static bool same_suspect(git_blame__origin *a, git_blame__origin *b) | |
90 | - | { | ||
91 | 175619 | 2 | if (a == b) | |
92 | 31002 | 3 | return true; | |
93 | 144617 | 4-7 | if (git_oid_cmp(git_commit_id(a->commit), git_commit_id(b->commit))) | |
94 | 144617 | 8 | return false; | |
95 | ##### | 9 | return 0 == strcmp(a->path, b->path); | |
96 | - | } | ||
97 | - | |||
98 | - | /* find the line number of the last line the target is suspected for */ | ||
99 | 199 | 2 | static bool find_last_in_target(size_t *out, git_blame *blame, git_blame__origin *target) | |
100 | - | { | ||
101 | - | git_blame__entry *e; | ||
102 | 199 | 2 | size_t last_in_target = 0; | |
103 | 199 | 2 | bool found = false; | |
104 | - | |||
105 | 199 | 2 | *out = 0; | |
106 | - | |||
107 | 4034 | 2,9,10 | for (e=blame->ent; e; e=e->next) { | |
108 | 3835 | 3-5 | if (e->guilty || !same_suspect(e->suspect, target)) | |
109 | 2816 | 6 | continue; | |
110 | 1019 | 7 | if (last_in_target < e->s_lno + e->num_lines) { | |
111 | 1019 | 8 | found = true; | |
112 | 1019 | 8 | last_in_target = e->s_lno + e->num_lines; | |
113 | - | } | ||
114 | - | } | ||
115 | - | |||
116 | 199 | 11 | *out = last_in_target; | |
117 | 199 | 11 | return found; | |
118 | - | } | ||
119 | - | |||
120 | - | /* | ||
121 | - | * It is known that lines between tlno to same came from parent, and e | ||
122 | - | * has an overlap with that range. it also is known that parent's | ||
123 | - | * line plno corresponds to e's line tlno. | ||
124 | - | * | ||
125 | - | * <---- e -----> | ||
126 | - | * <------> (entirely within) | ||
127 | - | * <------------> (extends past) | ||
128 | - | * <------------> (starts before) | ||
129 | - | * <------------------> (entirely encloses) | ||
130 | - | * | ||
131 | - | * Split e into potentially three parts; before this chunk, the chunk | ||
132 | - | * to be blamed for the parent, and after that portion. | ||
133 | - | */ | ||
134 | 1013 | 2 | static void split_overlap(git_blame__entry *split, git_blame__entry *e, | |
135 | - | size_t tlno, size_t plno, size_t same, git_blame__origin *parent) | ||
136 | - | { | ||
137 | - | size_t chunk_end_lno; | ||
138 | - | |||
139 | 1013 | 2 | if (e->s_lno < tlno) { | |
140 | - | /* there is a pre-chunk part not blamed on the parent */ | ||
141 | 47 | 3 | split[0].suspect = origin_incref(e->suspect); | |
142 | 47 | 4 | split[0].lno = e->lno; | |
143 | 47 | 4 | split[0].s_lno = e->s_lno; | |
144 | 47 | 4 | split[0].num_lines = tlno - e->s_lno; | |
145 | 47 | 4 | split[1].lno = e->lno + tlno - e->s_lno; | |
146 | 47 | 4 | split[1].s_lno = plno; | |
147 | - | } else { | ||
148 | 966 | 5 | split[1].lno = e->lno; | |
149 | 966 | 5 | split[1].s_lno = plno + (e->s_lno - tlno); | |
150 | - | } | ||
151 | - | |||
152 | 1013 | 6 | if (same < e->s_lno + e->num_lines) { | |
153 | - | /* there is a post-chunk part not blamed on parent */ | ||
154 | 104 | 7 | split[2].suspect = origin_incref(e->suspect); | |
155 | 104 | 8 | split[2].lno = e->lno + (same - e->s_lno); | |
156 | 104 | 8 | split[2].s_lno = e->s_lno + (same - e->s_lno); | |
157 | 104 | 8 | split[2].num_lines = e->s_lno + e->num_lines - same; | |
158 | 104 | 8 | chunk_end_lno = split[2].lno; | |
159 | - | } else { | ||
160 | 909 | 9 | chunk_end_lno = e->lno + e->num_lines; | |
161 | - | } | ||
162 | 1013 | 10 | split[1].num_lines = chunk_end_lno - split[1].lno; | |
163 | - | |||
164 | - | /* | ||
165 | - | * if it turns out there is nothing to blame the parent for, forget about | ||
166 | - | * the splitting. !split[1].suspect signals this. | ||
167 | - | */ | ||
168 | 1013 | 10 | if (split[1].num_lines < 1) | |
169 | 1013 | 11,14 | return; | |
170 | 1013 | 12 | split[1].suspect = origin_incref(parent); | |
171 | - | } | ||
172 | - | |||
173 | - | /* | ||
174 | - | * Link in a new blame entry to the scoreboard. Entries that cover the same | ||
175 | - | * line range have been removed from the scoreboard previously. | ||
176 | - | */ | ||
177 | 151 | 2 | static void add_blame_entry(git_blame *blame, git_blame__entry *e) | |
178 | - | { | ||
179 | 151 | 2 | git_blame__entry *ent, *prev = NULL; | |
180 | - | |||
181 | 151 | 2 | origin_incref(e->suspect); | |
182 | - | |||
183 | 1244 | 3-6 | for (ent = blame->ent; ent && ent->lno < e->lno; ent = ent->next) | |
184 | 1093 | 4 | prev = ent; | |
185 | - | |||
186 | - | /* prev, if not NULL, is the last one that is below e */ | ||
187 | 151 | 7 | e->prev = prev; | |
188 | 151 | 7 | if (prev) { | |
189 | 151 | 8 | e->next = prev->next; | |
190 | 151 | 8 | prev->next = e; | |
191 | - | } else { | ||
192 | ##### | 9 | e->next = blame->ent; | |
193 | ##### | 9 | blame->ent = e; | |
194 | - | } | ||
195 | 151 | 10 | if (e->next) | |
196 | 116 | 11 | e->next->prev = e; | |
197 | 151 | 12 | } | |
198 | - | |||
199 | - | /* | ||
200 | - | * src typically is on-stack; we want to copy the information in it to | ||
201 | - | * a malloced blame_entry that is already on the linked list of the scoreboard. | ||
202 | - | * The origin of dst loses a refcnt while the origin of src gains one. | ||
203 | - | */ | ||
204 | 1013 | 2 | static void dup_entry(git_blame__entry *dst, git_blame__entry *src) | |
205 | - | { | ||
206 | - | git_blame__entry *p, *n; | ||
207 | - | |||
208 | 1013 | 2 | p = dst->prev; | |
209 | 1013 | 2 | n = dst->next; | |
210 | 1013 | 2 | origin_incref(src->suspect); | |
211 | 1013 | 3 | origin_decref(dst->suspect); | |
212 | 1013 | 4 | memcpy(dst, src, sizeof(*src)); | |
213 | 1013 | 4 | dst->prev = p; | |
214 | 1013 | 4 | dst->next = n; | |
215 | 1013 | 4 | dst->score = 0; | |
216 | 1013 | 4 | } | |
217 | - | |||
218 | - | /* | ||
219 | - | * split_overlap() divided an existing blame e into up to three parts in split. | ||
220 | - | * Adjust the linked list of blames in the scoreboard to reflect the split. | ||
221 | - | */ | ||
222 | 1013 | 2 | static int split_blame(git_blame *blame, git_blame__entry *split, git_blame__entry *e) | |
223 | - | { | ||
224 | - | git_blame__entry *new_entry; | ||
225 | - | |||
226 | 1013 | 2,3 | if (split[0].suspect && split[2].suspect) { | |
227 | - | /* The first part (reuse storage for the existing entry e */ | ||
228 | 4 | 4 | dup_entry(e, &split[0]); | |
229 | - | |||
230 | - | /* The last part -- me */ | ||
231 | 4 | 5 | new_entry = git__malloc(sizeof(*new_entry)); | |
232 | 4 | 6,7 | GIT_ERROR_CHECK_ALLOC(new_entry); | |
233 | 4 | 8 | memcpy(new_entry, &(split[2]), sizeof(git_blame__entry)); | |
234 | 4 | 8 | add_blame_entry(blame, new_entry); | |
235 | - | |||
236 | - | /* ... and the middle part -- parent */ | ||
237 | 4 | 9 | new_entry = git__malloc(sizeof(*new_entry)); | |
238 | 4 | 10,11 | GIT_ERROR_CHECK_ALLOC(new_entry); | |
239 | 4 | 12 | memcpy(new_entry, &(split[1]), sizeof(git_blame__entry)); | |
240 | 4 | 12 | add_blame_entry(blame, new_entry); | |
241 | 1009 | 13,14 | } else if (!split[0].suspect && !split[2].suspect) { | |
242 | - | /* | ||
243 | - | * The parent covers the entire area; reuse storage for e and replace it | ||
244 | - | * with the parent | ||
245 | - | */ | ||
246 | 866 | 15 | dup_entry(e, &split[1]); | |
247 | 143 | 16 | } else if (split[0].suspect) { | |
248 | - | /* me and then parent */ | ||
249 | 43 | 17 | dup_entry(e, &split[0]); | |
250 | 43 | 18 | new_entry = git__malloc(sizeof(*new_entry)); | |
251 | 43 | 19,20 | GIT_ERROR_CHECK_ALLOC(new_entry); | |
252 | 43 | 21 | memcpy(new_entry, &(split[1]), sizeof(git_blame__entry)); | |
253 | 43 | 21 | add_blame_entry(blame, new_entry); | |
254 | - | } else { | ||
255 | - | /* parent and then me */ | ||
256 | 100 | 22 | dup_entry(e, &split[1]); | |
257 | 100 | 23 | new_entry = git__malloc(sizeof(*new_entry)); | |
258 | 100 | 24,25 | GIT_ERROR_CHECK_ALLOC(new_entry); | |
259 | 100 | 26 | memcpy(new_entry, &(split[2]), sizeof(git_blame__entry)); | |
260 | 100 | 26 | add_blame_entry(blame, new_entry); | |
261 | - | } | ||
262 | - | |||
263 | 1013 | 27 | return 0; | |
264 | - | } | ||
265 | - | |||
266 | - | /* | ||
267 | - | * After splitting the blame, the origins used by the on-stack blame_entry | ||
268 | - | * should lose one refcnt each. | ||
269 | - | */ | ||
270 | 1013 | 2 | static void decref_split(git_blame__entry *split) | |
271 | - | { | ||
272 | - | int i; | ||
273 | 4052 | 2,4,5 | for (i=0; i<3; i++) | |
274 | 3039 | 3 | origin_decref(split[i].suspect); | |
275 | 1013 | 6 | } | |
276 | - | |||
277 | - | /* | ||
278 | - | * Helper for blame_chunk(). blame_entry e is known to overlap with the patch | ||
279 | - | * hunk; split it and pass blame to the parent. | ||
280 | - | */ | ||
281 | 1013 | 2 | static int blame_overlap( | |
282 | - | git_blame *blame, | ||
283 | - | git_blame__entry *e, | ||
284 | - | size_t tlno, | ||
285 | - | size_t plno, | ||
286 | - | size_t same, | ||
287 | - | git_blame__origin *parent) | ||
288 | - | { | ||
289 | 1013 | 2 | git_blame__entry split[3] = {{0}}; | |
290 | - | |||
291 | 1013 | 2 | split_overlap(split, e, tlno, plno, same, parent); | |
292 | 1013 | 3 | if (split[1].suspect) | |
293 | 1013 | 4,5 | if (split_blame(blame, split, e) < 0) | |
294 | ##### | 6 | return -1; | |
295 | 1013 | 7 | decref_split(split); | |
296 | - | |||
297 | 1013 | 8 | return 0; | |
298 | - | } | ||
299 | - | |||
300 | - | /* | ||
301 | - | * Process one hunk from the patch between the current suspect for blame_entry | ||
302 | - | * e and its parent. Find and split the overlap, and pass blame to the | ||
303 | - | * overlapping part to the parent. | ||
304 | - | */ | ||
305 | 407 | 2 | static int blame_chunk( | |
306 | - | git_blame *blame, | ||
307 | - | size_t tlno, | ||
308 | - | size_t plno, | ||
309 | - | size_t same, | ||
310 | - | git_blame__origin *target, | ||
311 | - | git_blame__origin *parent) | ||
312 | - | { | ||
313 | - | git_blame__entry *e; | ||
314 | - | |||
315 | 9001 | 2,13,14 | for (e = blame->ent; e; e = e->next) { | |
316 | 8594 | 3-5 | if (e->guilty || !same_suspect(e->suspect, target)) | |
317 | 6663 | 6 | continue; | |
318 | 1931 | 7 | if (same <= e->s_lno) | |
319 | 762 | 8 | continue; | |
320 | 1169 | 9 | if (tlno < e->s_lno + e->num_lines) { | |
321 | 1013 | 10,11 | if (blame_overlap(blame, e, tlno, plno, same, parent) < 0) | |
322 | ##### | 12 | return -1; | |
323 | - | } | ||
324 | - | } | ||
325 | - | |||
326 | 407 | 15 | return 0; | |
327 | - | } | ||
328 | - | |||
329 | 216 | 2 | static int my_emit( | |
330 | - | long start_a, long count_a, | ||
331 | - | long start_b, long count_b, | ||
332 | - | void *cb_data) | ||
333 | - | { | ||
334 | 216 | 2 | blame_chunk_cb_data *d = (blame_chunk_cb_data *)cb_data; | |
335 | - | |||
336 | 216 | 2,3 | if (blame_chunk(d->blame, d->tlno, d->plno, start_b, d->target, d->parent) < 0) | |
337 | ##### | 4 | return -1; | |
338 | 216 | 5 | d->plno = start_a + count_a; | |
339 | 216 | 5 | d->tlno = start_b + count_b; | |
340 | - | |||
341 | 216 | 5 | return 0; | |
342 | - | } | ||
343 | - | |||
344 | 191 | 2 | static void trim_common_tail(mmfile_t *a, mmfile_t *b, long ctx) | |
345 | - | { | ||
346 | 191 | 2 | const int blk = 1024; | |
347 | 191 | 2 | long trimmed = 0, recovered = 0; | |
348 | 191 | 2 | char *ap = a->ptr + a->size; | |
349 | 191 | 2 | char *bp = b->ptr + b->size; | |
350 | 191 | 2 | long smaller = (long)((a->size < b->size) ? a->size : b->size); | |
351 | - | |||
352 | 191 | 2 | if (ctx) | |
353 | 191 | 3,13 | return; | |
354 | - | |||
355 | 193 | 4,6,7 | while (blk + trimmed <= smaller && !memcmp(ap - blk, bp - blk, blk)) { | |
356 | 2 | 5 | trimmed += blk; | |
357 | 2 | 5 | ap -= blk; | |
358 | 2 | 5 | bp -= blk; | |
359 | - | } | ||
360 | - | |||
361 | 231 | 8,11 | while (recovered < trimmed) | |
362 | 42 | 9 | if (ap[recovered++] == '\n') | |
363 | 2 | 10 | break; | |
364 | 191 | 12 | a->size -= trimmed - recovered; | |
365 | 191 | 12 | b->size -= trimmed - recovered; | |
366 | - | } | ||
367 | - | |||
368 | 191 | 2 | static int diff_hunks(mmfile_t file_a, mmfile_t file_b, void *cb_data, git_blame_options *options) | |
369 | - | { | ||
370 | 191 | 2 | xdemitconf_t xecfg = {0}; | |
371 | 191 | 2 | xdemitcb_t ecb = {0}; | |
372 | 191 | 2 | xpparam_t xpp = {0}; | |
373 | - | |||
374 | 191 | 2 | if (options->flags & GIT_BLAME_IGNORE_WHITESPACE) | |
375 | 3 | 3 | xpp.flags |= XDF_IGNORE_WHITESPACE; | |
376 | - | |||
377 | 191 | 4 | xecfg.hunk_func = my_emit; | |
378 | 191 | 4 | ecb.priv = cb_data; | |
379 | - | |||
380 | 191 | 4 | trim_common_tail(&file_a, &file_b, 0); | |
381 | - | |||
382 | 191 | 5,6 | if (file_a.size > GIT_XDIFF_MAX_SIZE || | |
383 | 191 | 6 | file_b.size > GIT_XDIFF_MAX_SIZE) { | |
384 | ##### | 7 | git_error_set(GIT_ERROR_INVALID, "file too large to blame"); | |
385 | ##### | 8 | return -1; | |
386 | - | } | ||
387 | - | |||
388 | 191 | 9 | return xdl_diff(&file_a, &file_b, &xpp, &xecfg, &ecb); | |
389 | - | } | ||
390 | - | |||
391 | 382 | 2 | static void fill_origin_blob(git_blame__origin *o, mmfile_t *file) | |
392 | - | { | ||
393 | 382 | 2 | memset(file, 0, sizeof(*file)); | |
394 | 382 | 2 | if (o->blob) { | |
395 | 382 | 3 | file->ptr = (char*)git_blob_rawcontent(o->blob); | |
396 | 382 | 4 | file->size = (size_t)git_blob_rawsize(o->blob); | |
397 | - | } | ||
398 | 382 | 6 | } | |
399 | - | |||
400 | 199 | 2 | static int pass_blame_to_parent( | |
401 | - | git_blame *blame, | ||
402 | - | git_blame__origin *target, | ||
403 | - | git_blame__origin *parent) | ||
404 | - | { | ||
405 | - | size_t last_in_target; | ||
406 | - | mmfile_t file_p, file_o; | ||
407 | 199 | 2 | blame_chunk_cb_data d = { blame, target, parent, 0, 0 }; | |
408 | - | |||
409 | 199 | 2,3 | if (!find_last_in_target(&last_in_target, blame, target)) | |
410 | 8 | 4 | return 1; /* nothing remains for this target */ | |
411 | - | |||
412 | 191 | 5 | fill_origin_blob(parent, &file_p); | |
413 | 191 | 6 | fill_origin_blob(target, &file_o); | |
414 | - | |||
415 | 191 | 7,8 | if (diff_hunks(file_p, file_o, &d, &blame->options) < 0) | |
416 | ##### | 9 | return -1; | |
417 | - | |||
418 | - | /* The reset (i.e. anything after tlno) are the same as the parent */ | ||
419 | 191 | 10,11 | if (blame_chunk(blame, d.tlno, d.plno, last_in_target, target, parent) < 0) | |
420 | ##### | 12 | return -1; | |
421 | - | |||
422 | 191 | 13 | return 0; | |
423 | - | } | ||
424 | - | |||
425 | 246 | 2 | static int paths_on_dup(void **old, void *new) | |
426 | - | { | ||
427 | - | GIT_UNUSED(old); | ||
428 | 246 | 2 | git__free(new); | |
429 | 246 | 3 | return -1; | |
430 | - | } | ||
431 | - | |||
432 | 3371 | 2 | static git_blame__origin* find_origin( | |
433 | - | git_blame *blame, | ||
434 | - | git_commit *parent, | ||
435 | - | git_blame__origin *origin) | ||
436 | - | { | ||
437 | 3371 | 2 | git_blame__origin *porigin = NULL; | |
438 | 3371 | 2 | git_diff *difflist = NULL; | |
439 | 3371 | 2 | git_diff_options diffopts = GIT_DIFF_OPTIONS_INIT; | |
440 | 3371 | 2 | git_tree *otree=NULL, *ptree=NULL; | |
441 | - | |||
442 | - | /* Get the trees from this commit and its parent */ | ||
443 | 3371 | 2,3,5 | if (0 != git_commit_tree(&otree, origin->commit) || | |
444 | 3371 | 4 | 0 != git_commit_tree(&ptree, parent)) | |
445 | - | goto cleanup; | ||
446 | - | |||
447 | - | /* Configure the diff */ | ||
448 | 3371 | 6 | diffopts.context_lines = 0; | |
449 | 3371 | 6 | diffopts.flags = GIT_DIFF_SKIP_BINARY_CHECK; | |
450 | - | |||
451 | - | /* Check to see if files we're interested have changed */ | ||
452 | 3371 | 6 | diffopts.pathspec.count = blame->paths.length; | |
453 | 3371 | 6 | diffopts.pathspec.strings = (char**)blame->paths.contents; | |
454 | 3371 | 6,7 | if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts)) | |
455 | ##### | 8 | goto cleanup; | |
456 | - | |||
457 | 3371 | 9,10 | if (!git_diff_num_deltas(difflist)) { | |
458 | - | /* No changes; copy data */ | ||
459 | 3121 | 11 | git_blame__get_origin(&porigin, blame, parent, origin->path); | |
460 | - | } else { | ||
461 | 250 | 12 | git_diff_find_options findopts = GIT_DIFF_FIND_OPTIONS_INIT; | |
462 | - | int i; | ||
463 | - | |||
464 | - | /* Generate a full diff between the two trees */ | ||
465 | 250 | 12 | git_diff_free(difflist); | |
466 | 250 | 13 | diffopts.pathspec.count = 0; | |
467 | 250 | 13,14 | if (0 != git_diff_tree_to_tree(&difflist, blame->repository, ptree, otree, &diffopts)) | |
468 | ##### | 15,30 | goto cleanup; | |
469 | - | |||
470 | - | /* Let diff find renames */ | ||
471 | 250 | 16 | findopts.flags = GIT_DIFF_FIND_RENAMES; | |
472 | 250 | 16,17 | if (0 != git_diff_find_similar(difflist, &findopts)) | |
473 | ##### | 18 | goto cleanup; | |
474 | - | |||
475 | - | /* Find one that matches */ | ||
476 | 6882 | 19,26-29 | for (i=0; i<(int)git_diff_num_deltas(difflist); i++) { | |
477 | 6632 | 20 | const git_diff_delta *delta = git_diff_get_delta(difflist, i); | |
478 | - | |||
479 | 6632 | 21,22 | if (!git_vector_bsearch(NULL, &blame->paths, delta->new_file.path)) | |
480 | - | { | ||
481 | 250 | 23,24 | git_vector_insert_sorted(&blame->paths, (void*)git__strdup(delta->old_file.path), | |
482 | - | paths_on_dup); | ||
483 | 250 | 25 | make_origin(&porigin, parent, delta->old_file.path); | |
484 | - | } | ||
485 | - | } | ||
486 | - | } | ||
487 | - | |||
488 | - | cleanup: | ||
489 | 3371 | 31 | git_diff_free(difflist); | |
490 | 3371 | 32 | git_tree_free(otree); | |
491 | 3371 | 33 | git_tree_free(ptree); | |
492 | 3371 | 34 | return porigin; | |
493 | - | } | ||
494 | - | |||
495 | - | /* | ||
496 | - | * The blobs of origin and porigin exactly match, so everything origin is | ||
497 | - | * suspected for can be blamed on the parent. | ||
498 | - | */ | ||
499 | 3123 | 2 | static int pass_whole_blame(git_blame *blame, | |
500 | - | git_blame__origin *origin, git_blame__origin *porigin) | ||
501 | - | { | ||
502 | - | git_blame__entry *e; | ||
503 | - | |||
504 | 3123 | 2,5 | if (!porigin->blob && | |
505 | ##### | 3,4 | git_object_lookup((git_object**)&porigin->blob, blame->repository, | |
506 | ##### | 3 | git_blob_id(origin->blob), GIT_OBJECT_BLOB) < 0) | |
507 | ##### | 6 | return -1; | |
508 | 86007 | 7,14,15 | for (e=blame->ent; e; e=e->next) { | |
509 | 82884 | 8,9 | if (!same_suspect(e->suspect, origin)) | |
510 | 55005 | 10 | continue; | |
511 | 27879 | 11 | origin_incref(porigin); | |
512 | 27879 | 12 | origin_decref(e->suspect); | |
513 | 27879 | 13 | e->suspect = porigin; | |
514 | - | } | ||
515 | - | |||
516 | 3123 | 16 | return 0; | |
517 | - | } | ||
518 | - | |||
519 | 3316 | 2 | static int pass_blame(git_blame *blame, git_blame__origin *origin, uint32_t opt) | |
520 | - | { | ||
521 | 3316 | 2 | git_commit *commit = origin->commit; | |
522 | - | int i, num_parents; | ||
523 | - | git_blame__origin *sg_buf[16]; | ||
524 | 3316 | 2 | git_blame__origin *porigin, **sg_origin = sg_buf; | |
525 | 3316 | 2 | int ret, error = 0; | |
526 | - | |||
527 | 3316 | 2 | num_parents = git_commit_parentcount(commit); | |
528 | 3316 | 3-5 | if (!git_oid_cmp(git_commit_id(commit), &blame->options.oldest_commit)) | |
529 | - | /* Stop at oldest specified commit */ | ||
530 | 1 | 6 | num_parents = 0; | |
531 | 3315 | 7,8 | else if (opt & GIT_BLAME_FIRST_PARENT && num_parents > 1) | |
532 | - | /* Limit search to the first parent */ | ||
533 | 1 | 9 | num_parents = 1; | |
534 | - | |||
535 | 3316 | 10 | if (!num_parents) { | |
536 | 15 | 11,12 | git_oid_cpy(&blame->options.oldest_commit, git_commit_id(commit)); | |
537 | 15 | 13 | goto finish; | |
538 | 3301 | 14 | } else if (num_parents < (int)ARRAY_SIZE(sg_buf)) | |
539 | 3301 | 15 | memset(sg_buf, 0, sizeof(sg_buf)); | |
540 | - | else { | ||
541 | ##### | 16 | sg_origin = git__calloc(num_parents, sizeof(*sg_origin)); | |
542 | ##### | 17,18 | GIT_ERROR_CHECK_ALLOC(sg_origin); | |
543 | - | } | ||
544 | - | |||
545 | 3549 | 19,53,54 | for (i=0; i<num_parents; i++) { | |
546 | - | git_commit *p; | ||
547 | - | int j, same; | ||
548 | - | |||
549 | 3371 | 20 | if (sg_origin[i]) | |
550 | 7 | 21,50 | continue; | |
551 | - | |||
552 | 3371 | 22,23 | if ((error = git_commit_parent(&p, origin->commit, i)) < 0) | |
553 | 3123 | 24,52 | goto finish; | |
554 | 3371 | 25 | porigin = find_origin(blame, p, origin); | |
555 | - | |||
556 | 3371 | 26 | if (!porigin) { | |
557 | - | /* | ||
558 | - | * We only have to decrement the parent's | ||
559 | - | * reference count when no porigin has | ||
560 | - | * been created, as otherwise the commit | ||
561 | - | * is assigned to the created object. | ||
562 | - | */ | ||
563 | 7 | 27 | git_commit_free(p); | |
564 | 7 | 28 | continue; | |
565 | - | } | ||
566 | 3364 | 29,30,34 | if (porigin->blob && origin->blob && | |
567 | 3364 | 31-33 | !git_oid_cmp(git_blob_id(porigin->blob), git_blob_id(origin->blob))) { | |
568 | 3123 | 35 | error = pass_whole_blame(blame, origin, porigin); | |
569 | 3123 | 36 | origin_decref(porigin); | |
570 | 3123 | 51 | goto finish; | |
571 | - | } | ||
572 | 268 | 37,44,45 | for (j = same = 0; j<i; j++) | |
573 | 27 | 38,42 | if (sg_origin[j] && | |
574 | 27 | 39-41 | !git_oid_cmp(git_blob_id(sg_origin[j]->blob), git_blob_id(porigin->blob))) { | |
575 | ##### | 43 | same = 1; | |
576 | ##### | 43 | break; | |
577 | - | } | ||
578 | 241 | 46 | if (!same) | |
579 | 241 | 47 | sg_origin[i] = porigin; | |
580 | - | else | ||
581 | 241 | 48,49 | origin_decref(porigin); | |
582 | - | } | ||
583 | - | |||
584 | - | /* Standard blame */ | ||
585 | 375 | 55,66,67 | for (i=0; i<num_parents; i++) { | |
586 | 205 | 56 | git_blame__origin *porigin = sg_origin[i]; | |
587 | 205 | 56 | if (!porigin) | |
588 | 6 | 57 | continue; | |
589 | 199 | 58 | if (!origin->previous) { | |
590 | 172 | 59 | origin_incref(porigin); | |
591 | 172 | 60 | origin->previous = porigin; | |
592 | - | } | ||
593 | - | |||
594 | 199 | 61,62 | if ((ret = pass_blame_to_parent(blame, origin, porigin)) != 0) { | |
595 | 8 | 63 | if (ret < 0) | |
596 | ##### | 64 | error = -1; | |
597 | - | |||
598 | 8 | 65 | goto finish; | |
599 | - | } | ||
600 | - | } | ||
601 | - | |||
602 | - | /* TODO: optionally find moves in parents' files */ | ||
603 | - | |||
604 | - | /* TODO: optionally find copies in parents' files */ | ||
605 | - | |||
606 | - | finish: | ||
607 | 7623 | 68,71,72 | for (i=0; i<num_parents; i++) | |
608 | 4307 | 69 | if (sg_origin[i]) | |
609 | 241 | 70 | origin_decref(sg_origin[i]); | |
610 | 3316 | 73 | if (sg_origin != sg_buf) | |
611 | ##### | 74 | git__free(sg_origin); | |
612 | 3316 | 75 | return error; | |
613 | - | } | ||
614 | - | |||
615 | - | /* | ||
616 | - | * If two blame entries that are next to each other came from | ||
617 | - | * contiguous lines in the same origin (i.e. <commit, path> pair), | ||
618 | - | * merge them together. | ||
619 | - | */ | ||
620 | 22 | 2 | static void coalesce(git_blame *blame) | |
621 | - | { | ||
622 | - | git_blame__entry *ent, *next; | ||
623 | - | |||
624 | 173 | 2,12-14 | for (ent=blame->ent; ent && (next = ent->next); ent = next) { | |
625 | 151 | 3-5 | if (same_suspect(ent->suspect, next->suspect) && | |
626 | ##### | 5,6 | ent->guilty == next->guilty && | |
627 | ##### | 6 | ent->s_lno + ent->num_lines == next->s_lno) | |
628 | - | { | ||
629 | ##### | 7 | ent->num_lines += next->num_lines; | |
630 | ##### | 7 | ent->next = next->next; | |
631 | ##### | 7 | if (ent->next) | |
632 | ##### | 8 | ent->next->prev = ent; | |
633 | ##### | 9 | origin_decref(next->suspect); | |
634 | ##### | 10 | git__free(next); | |
635 | ##### | 11 | ent->score = 0; | |
636 | ##### | 11 | next = ent; /* again */ | |
637 | - | } | ||
638 | - | } | ||
639 | 22 | 15 | } | |
640 | - | |||
641 | 22 | 2 | int git_blame__like_git(git_blame *blame, uint32_t opt) | |
642 | - | { | ||
643 | 22 | 2 | int error = 0; | |
644 | - | |||
645 | - | while (true) { | ||
646 | - | git_blame__entry *ent; | ||
647 | 3338 | 3 | git_blame__origin *suspect = NULL; | |
648 | - | |||
649 | - | /* Find a suspect to break down */ | ||
650 | 8789 | 3,6-8 | for (ent = blame->ent; !suspect && ent; ent = ent->next) | |
651 | 5451 | 4 | if (!ent->guilty) | |
652 | 3316 | 5 | suspect = ent->suspect; | |
653 | 3338 | 9 | if (!suspect) | |
654 | 22 | 10 | break; | |
655 | - | |||
656 | - | /* We'll use this suspect later in the loop, so hold on to it for now. */ | ||
657 | 3316 | 11 | origin_incref(suspect); | |
658 | - | |||
659 | 3316 | 12,13 | if ((error = pass_blame(blame, suspect, opt)) < 0) | |
660 | ##### | 14 | break; | |
661 | - | |||
662 | - | /* Take responsibility for the remaining entries */ | ||
663 | 89945 | 15,21,22 | for (ent = blame->ent; ent; ent = ent->next) { | |
664 | 86629 | 16,17 | if (same_suspect(ent->suspect, suspect)) { | |
665 | 173 | 18 | ent->guilty = true; | |
666 | 173 | 18-20 | ent->is_boundary = !git_oid_cmp( | |
667 | 173 | 18 | git_commit_id(suspect->commit), | |
668 | 173 | 18 | &blame->options.oldest_commit); | |
669 | - | } | ||
670 | - | } | ||
671 | 3316 | 23 | origin_decref(suspect); | |
672 | 3316 | 24 | } | |
673 | - | |||
674 | 22 | 25 | if (!error) | |
675 | 22 | 26 | coalesce(blame); | |
676 | - | |||
677 | 22 | 27 | return error; | |
678 | - | } | ||
679 | - | |||
680 | 173 | 2 | void git_blame__free_entry(git_blame__entry *ent) | |
681 | - | { | ||
682 | 173 | 2,3,6 | if (!ent) return; | |
683 | 173 | 4 | origin_decref(ent->suspect); | |
684 | 173 | 5 | git__free(ent); | |
685 | - | } |