File: | line-log.c |
Location: | line 46, column 2 |
Description: | Null pointer argument in call to memory copy function |
1 | #include "git-compat-util.h" | |||||
2 | #include "line-range.h" | |||||
3 | #include "cache.h" | |||||
4 | #include "tag.h" | |||||
5 | #include "blob.h" | |||||
6 | #include "tree.h" | |||||
7 | #include "diff.h" | |||||
8 | #include "commit.h" | |||||
9 | #include "decorate.h" | |||||
10 | #include "revision.h" | |||||
11 | #include "xdiff-interface.h" | |||||
12 | #include "strbuf.h" | |||||
13 | #include "log-tree.h" | |||||
14 | #include "graph.h" | |||||
15 | #include "userdiff.h" | |||||
16 | #include "line-log.h" | |||||
17 | #include "argv-array.h" | |||||
18 | ||||||
19 | static void range_set_grow(struct range_set *rs, size_t extra) | |||||
20 | { | |||||
21 | ALLOC_GROW(rs->ranges, rs->nr + extra, rs->alloc)do { if ((rs->nr + extra) > rs->alloc) { if ((((rs-> alloc)+16)*3/2) < (rs->nr + extra)) rs->alloc = (rs-> nr + extra); else rs->alloc = (((rs->alloc)+16)*3/2); ( rs->ranges) = xrealloc((rs->ranges), st_mult(sizeof(*(rs ->ranges)), (rs->alloc))); } } while (0); | |||||
22 | } | |||||
23 | ||||||
24 | /* Either initialization would be fine */ | |||||
25 | #define RANGE_SET_INIT{0} {0} | |||||
26 | ||||||
27 | void range_set_init(struct range_set *rs, size_t prealloc) | |||||
28 | { | |||||
29 | rs->alloc = rs->nr = 0; | |||||
30 | rs->ranges = NULL((void*)0); | |||||
31 | if (prealloc) | |||||
32 | range_set_grow(rs, prealloc); | |||||
33 | } | |||||
34 | ||||||
35 | void range_set_release(struct range_set *rs) | |||||
36 | { | |||||
37 | free(rs->ranges); | |||||
38 | rs->alloc = rs->nr = 0; | |||||
39 | rs->ranges = NULL((void*)0); | |||||
40 | } | |||||
41 | ||||||
42 | /* dst must be uninitialized! */ | |||||
43 | static void range_set_copy(struct range_set *dst, struct range_set *src) | |||||
44 | { | |||||
45 | range_set_init(dst, src->nr); | |||||
46 | memcpy(dst->ranges, src->ranges, src->nr*sizeof(struct range_set))__builtin___memcpy_chk (dst->ranges, src->ranges, src-> nr*sizeof(struct range_set), __builtin_object_size (dst->ranges , 0)); | |||||
| ||||||
47 | dst->nr = src->nr; | |||||
48 | } | |||||
49 | static void range_set_move(struct range_set *dst, struct range_set *src) | |||||
50 | { | |||||
51 | range_set_release(dst); | |||||
52 | dst->ranges = src->ranges; | |||||
53 | dst->nr = src->nr; | |||||
54 | dst->alloc = src->alloc; | |||||
55 | src->ranges = NULL((void*)0); | |||||
56 | src->alloc = src->nr = 0; | |||||
57 | } | |||||
58 | ||||||
59 | /* tack on a _new_ range _at the end_ */ | |||||
60 | void range_set_append_unsafe(struct range_set *rs, long a, long b) | |||||
61 | { | |||||
62 | assert(a <= b)(__builtin_expect(!(a <= b), 0) ? __assert_rtn(__func__, "line-log.c" , 62, "a <= b") : (void)0); | |||||
63 | range_set_grow(rs, 1); | |||||
64 | rs->ranges[rs->nr].start = a; | |||||
65 | rs->ranges[rs->nr].end = b; | |||||
66 | rs->nr++; | |||||
67 | } | |||||
68 | ||||||
69 | void range_set_append(struct range_set *rs, long a, long b) | |||||
70 | { | |||||
71 | assert(rs->nr == 0 || rs->ranges[rs->nr-1].end <= a)(__builtin_expect(!(rs->nr == 0 || rs->ranges[rs->nr -1].end <= a), 0) ? __assert_rtn(__func__, "line-log.c", 71 , "rs->nr == 0 || rs->ranges[rs->nr-1].end <= a") : (void)0); | |||||
72 | range_set_append_unsafe(rs, a, b); | |||||
73 | } | |||||
74 | ||||||
75 | static int range_cmp(const void *_r, const void *_s) | |||||
76 | { | |||||
77 | const struct range *r = _r; | |||||
78 | const struct range *s = _s; | |||||
79 | ||||||
80 | /* this could be simply 'return r.start-s.start', but for the types */ | |||||
81 | if (r->start == s->start) | |||||
82 | return 0; | |||||
83 | if (r->start < s->start) | |||||
84 | return -1; | |||||
85 | return 1; | |||||
86 | } | |||||
87 | ||||||
88 | /* | |||||
89 | * Check that the ranges are non-empty, sorted and non-overlapping | |||||
90 | */ | |||||
91 | static void range_set_check_invariants(struct range_set *rs) | |||||
92 | { | |||||
93 | int i; | |||||
94 | ||||||
95 | if (!rs) | |||||
96 | return; | |||||
97 | ||||||
98 | if (rs->nr) | |||||
99 | assert(rs->ranges[0].start < rs->ranges[0].end)(__builtin_expect(!(rs->ranges[0].start < rs->ranges [0].end), 0) ? __assert_rtn(__func__, "line-log.c", 99, "rs->ranges[0].start < rs->ranges[0].end" ) : (void)0); | |||||
100 | ||||||
101 | for (i = 1; i < rs->nr; i++) { | |||||
102 | assert(rs->ranges[i-1].end < rs->ranges[i].start)(__builtin_expect(!(rs->ranges[i-1].end < rs->ranges [i].start), 0) ? __assert_rtn(__func__, "line-log.c", 102, "rs->ranges[i-1].end < rs->ranges[i].start" ) : (void)0); | |||||
103 | assert(rs->ranges[i].start < rs->ranges[i].end)(__builtin_expect(!(rs->ranges[i].start < rs->ranges [i].end), 0) ? __assert_rtn(__func__, "line-log.c", 103, "rs->ranges[i].start < rs->ranges[i].end" ) : (void)0); | |||||
104 | } | |||||
105 | } | |||||
106 | ||||||
107 | /* | |||||
108 | * In-place pass of sorting and merging the ranges in the range set, | |||||
109 | * to establish the invariants when we get the ranges from the user | |||||
110 | */ | |||||
111 | void sort_and_merge_range_set(struct range_set *rs) | |||||
112 | { | |||||
113 | int i; | |||||
114 | int o = 0; /* output cursor */ | |||||
115 | ||||||
116 | QSORT(rs->ranges, rs->nr, range_cmp)sane_qsort((rs->ranges), (rs->nr), sizeof(*(rs->ranges )), range_cmp); | |||||
117 | ||||||
118 | for (i = 0; i < rs->nr; i++) { | |||||
119 | if (rs->ranges[i].start == rs->ranges[i].end) | |||||
120 | continue; | |||||
121 | if (o > 0 && rs->ranges[i].start <= rs->ranges[o-1].end) { | |||||
122 | if (rs->ranges[o-1].end < rs->ranges[i].end) | |||||
123 | rs->ranges[o-1].end = rs->ranges[i].end; | |||||
124 | } else { | |||||
125 | rs->ranges[o].start = rs->ranges[i].start; | |||||
126 | rs->ranges[o].end = rs->ranges[i].end; | |||||
127 | o++; | |||||
128 | } | |||||
129 | } | |||||
130 | assert(o <= rs->nr)(__builtin_expect(!(o <= rs->nr), 0) ? __assert_rtn(__func__ , "line-log.c", 130, "o <= rs->nr") : (void)0); | |||||
131 | rs->nr = o; | |||||
132 | ||||||
133 | range_set_check_invariants(rs); | |||||
134 | } | |||||
135 | ||||||
136 | /* | |||||
137 | * Union of range sets (i.e., sets of line numbers). Used to merge | |||||
138 | * them when searches meet at a common ancestor. | |||||
139 | * | |||||
140 | * This is also where the ranges are consolidated into canonical form: | |||||
141 | * overlapping and adjacent ranges are merged, and empty ranges are | |||||
142 | * removed. | |||||
143 | */ | |||||
144 | static void range_set_union(struct range_set *out, | |||||
145 | struct range_set *a, struct range_set *b) | |||||
146 | { | |||||
147 | int i = 0, j = 0, o = 0; | |||||
148 | struct range *ra = a->ranges; | |||||
149 | struct range *rb = b->ranges; | |||||
150 | /* cannot make an alias of out->ranges: it may change during grow */ | |||||
151 | ||||||
152 | assert(out->nr == 0)(__builtin_expect(!(out->nr == 0), 0) ? __assert_rtn(__func__ , "line-log.c", 152, "out->nr == 0") : (void)0); | |||||
153 | while (i < a->nr || j < b->nr) { | |||||
154 | struct range *new; | |||||
155 | if (i < a->nr && j < b->nr) { | |||||
156 | if (ra[i].start < rb[j].start) | |||||
157 | new = &ra[i++]; | |||||
158 | else if (ra[i].start > rb[j].start) | |||||
159 | new = &rb[j++]; | |||||
160 | else if (ra[i].end < rb[j].end) | |||||
161 | new = &ra[i++]; | |||||
162 | else | |||||
163 | new = &rb[j++]; | |||||
164 | } else if (i < a->nr) /* b exhausted */ | |||||
165 | new = &ra[i++]; | |||||
166 | else /* a exhausted */ | |||||
167 | new = &rb[j++]; | |||||
168 | if (new->start == new->end) | |||||
169 | ; /* empty range */ | |||||
170 | else if (!o || out->ranges[o-1].end < new->start) { | |||||
171 | range_set_grow(out, 1); | |||||
172 | out->ranges[o].start = new->start; | |||||
173 | out->ranges[o].end = new->end; | |||||
174 | o++; | |||||
175 | } else if (out->ranges[o-1].end < new->end) { | |||||
176 | out->ranges[o-1].end = new->end; | |||||
177 | } | |||||
178 | } | |||||
179 | out->nr = o; | |||||
180 | } | |||||
181 | ||||||
182 | /* | |||||
183 | * Difference of range sets (out = a \ b). Pass the "interesting" | |||||
184 | * ranges as 'a' and the target side of the diff as 'b': it removes | |||||
185 | * the ranges for which the commit is responsible. | |||||
186 | */ | |||||
187 | static void range_set_difference(struct range_set *out, | |||||
188 | struct range_set *a, struct range_set *b) | |||||
189 | { | |||||
190 | int i, j = 0; | |||||
191 | for (i = 0; i < a->nr; i++) { | |||||
192 | long start = a->ranges[i].start; | |||||
193 | long end = a->ranges[i].end; | |||||
194 | while (start < end) { | |||||
195 | while (j < b->nr && start >= b->ranges[j].end) | |||||
196 | /* | |||||
197 | * a: |------- | |||||
198 | * b: ------| | |||||
199 | */ | |||||
200 | j++; | |||||
201 | if (j >= b->nr || end < b->ranges[j].start) { | |||||
202 | /* | |||||
203 | * b exhausted, or | |||||
204 | * a: ----| | |||||
205 | * b: |---- | |||||
206 | */ | |||||
207 | range_set_append(out, start, end); | |||||
208 | break; | |||||
209 | } | |||||
210 | if (start >= b->ranges[j].start) { | |||||
211 | /* | |||||
212 | * a: |--???? | |||||
213 | * b: |------| | |||||
214 | */ | |||||
215 | start = b->ranges[j].end; | |||||
216 | } else if (end > b->ranges[j].start) { | |||||
217 | /* | |||||
218 | * a: |-----| | |||||
219 | * b: |--????? | |||||
220 | */ | |||||
221 | if (start < b->ranges[j].start) | |||||
222 | range_set_append(out, start, b->ranges[j].start); | |||||
223 | start = b->ranges[j].end; | |||||
224 | } | |||||
225 | } | |||||
226 | } | |||||
227 | } | |||||
228 | ||||||
229 | static void diff_ranges_init(struct diff_ranges *diff) | |||||
230 | { | |||||
231 | range_set_init(&diff->parent, 0); | |||||
232 | range_set_init(&diff->target, 0); | |||||
233 | } | |||||
234 | ||||||
235 | static void diff_ranges_release(struct diff_ranges *diff) | |||||
236 | { | |||||
237 | range_set_release(&diff->parent); | |||||
238 | range_set_release(&diff->target); | |||||
239 | } | |||||
240 | ||||||
241 | static void line_log_data_init(struct line_log_data *r) | |||||
242 | { | |||||
243 | memset(r, 0, sizeof(struct line_log_data))__builtin___memset_chk (r, 0, sizeof(struct line_log_data), __builtin_object_size (r, 0)); | |||||
244 | range_set_init(&r->ranges, 0); | |||||
245 | } | |||||
246 | ||||||
247 | static void line_log_data_clear(struct line_log_data *r) | |||||
248 | { | |||||
249 | range_set_release(&r->ranges); | |||||
250 | if (r->pair) | |||||
251 | diff_free_filepair(r->pair); | |||||
252 | } | |||||
253 | ||||||
254 | static void free_line_log_data(struct line_log_data *r) | |||||
255 | { | |||||
256 | while (r) { | |||||
257 | struct line_log_data *next = r->next; | |||||
258 | line_log_data_clear(r); | |||||
259 | free(r); | |||||
260 | r = next; | |||||
261 | } | |||||
262 | } | |||||
263 | ||||||
264 | static struct line_log_data * | |||||
265 | search_line_log_data(struct line_log_data *list, const char *path, | |||||
266 | struct line_log_data **insertion_point) | |||||
267 | { | |||||
268 | struct line_log_data *p = list; | |||||
269 | if (insertion_point) | |||||
270 | *insertion_point = NULL((void*)0); | |||||
271 | while (p) { | |||||
272 | int cmp = strcmp(p->path, path); | |||||
273 | if (!cmp) | |||||
274 | return p; | |||||
275 | if (insertion_point && cmp < 0) | |||||
276 | *insertion_point = p; | |||||
277 | p = p->next; | |||||
278 | } | |||||
279 | return NULL((void*)0); | |||||
280 | } | |||||
281 | ||||||
282 | /* | |||||
283 | * Note: takes ownership of 'path', which happens to be what the only | |||||
284 | * caller needs. | |||||
285 | */ | |||||
286 | static void line_log_data_insert(struct line_log_data **list, | |||||
287 | char *path, | |||||
288 | long begin, long end) | |||||
289 | { | |||||
290 | struct line_log_data *ip; | |||||
291 | struct line_log_data *p = search_line_log_data(*list, path, &ip); | |||||
292 | ||||||
293 | if (p) { | |||||
294 | range_set_append_unsafe(&p->ranges, begin, end); | |||||
295 | free(path); | |||||
296 | return; | |||||
297 | } | |||||
298 | ||||||
299 | p = xcalloc(1, sizeof(struct line_log_data)); | |||||
300 | p->path = path; | |||||
301 | range_set_append(&p->ranges, begin, end); | |||||
302 | if (ip) { | |||||
303 | p->next = ip->next; | |||||
304 | ip->next = p; | |||||
305 | } else { | |||||
306 | p->next = *list; | |||||
307 | *list = p; | |||||
308 | } | |||||
309 | } | |||||
310 | ||||||
311 | struct collect_diff_cbdata { | |||||
312 | struct diff_ranges *diff; | |||||
313 | }; | |||||
314 | ||||||
315 | static int collect_diff_cb(long start_a, long count_a, | |||||
316 | long start_b, long count_b, | |||||
317 | void *data) | |||||
318 | { | |||||
319 | struct collect_diff_cbdata *d = data; | |||||
320 | ||||||
321 | if (count_a >= 0) | |||||
322 | range_set_append(&d->diff->parent, start_a, start_a + count_a); | |||||
323 | if (count_b >= 0) | |||||
324 | range_set_append(&d->diff->target, start_b, start_b + count_b); | |||||
325 | ||||||
326 | return 0; | |||||
327 | } | |||||
328 | ||||||
329 | static int collect_diff(mmfile_t *parent, mmfile_t *target, struct diff_ranges *out) | |||||
330 | { | |||||
331 | struct collect_diff_cbdata cbdata = {NULL((void*)0)}; | |||||
332 | xpparam_t xpp; | |||||
333 | xdemitconf_t xecfg; | |||||
334 | xdemitcb_t ecb; | |||||
335 | ||||||
336 | memset(&xpp, 0, sizeof(xpp))__builtin___memset_chk (&xpp, 0, sizeof(xpp), __builtin_object_size (&xpp, 0)); | |||||
337 | memset(&xecfg, 0, sizeof(xecfg))__builtin___memset_chk (&xecfg, 0, sizeof(xecfg), __builtin_object_size (&xecfg, 0)); | |||||
338 | xecfg.ctxlen = xecfg.interhunkctxlen = 0; | |||||
339 | ||||||
340 | cbdata.diff = out; | |||||
341 | xecfg.hunk_func = collect_diff_cb; | |||||
342 | memset(&ecb, 0, sizeof(ecb))__builtin___memset_chk (&ecb, 0, sizeof(ecb), __builtin_object_size (&ecb, 0)); | |||||
343 | ecb.priv = &cbdata; | |||||
344 | return xdi_diff(parent, target, &xpp, &xecfg, &ecb); | |||||
345 | } | |||||
346 | ||||||
347 | /* | |||||
348 | * These are handy for debugging. Removing them with #if 0 silences | |||||
349 | * the "unused function" warning. | |||||
350 | */ | |||||
351 | #if 0 | |||||
352 | static void dump_range_set(struct range_set *rs, const char *desc) | |||||
353 | { | |||||
354 | int i; | |||||
355 | printf("range set %s (%d items):\n", desc, rs->nr); | |||||
356 | for (i = 0; i < rs->nr; i++) | |||||
357 | printf("\t[%ld,%ld]\n", rs->ranges[i].start, rs->ranges[i].end); | |||||
358 | } | |||||
359 | ||||||
360 | static void dump_line_log_data(struct line_log_data *r) | |||||
361 | { | |||||
362 | char buf[4096]; | |||||
363 | while (r) { | |||||
364 | snprintf(buf, 4096, "file %s\n", r->path)__builtin___snprintf_chk (buf, 4096, 0, __builtin_object_size (buf, 2 > 1 ? 1 : 0), "file %s\n", r->path); | |||||
365 | dump_range_set(&r->ranges, buf); | |||||
366 | r = r->next; | |||||
367 | } | |||||
368 | } | |||||
369 | ||||||
370 | static void dump_diff_ranges(struct diff_ranges *diff, const char *desc) | |||||
371 | { | |||||
372 | int i; | |||||
373 | assert(diff->parent.nr == diff->target.nr)(__builtin_expect(!(diff->parent.nr == diff->target.nr) , 0) ? __assert_rtn(__func__, "line-log.c", 373, "diff->parent.nr == diff->target.nr" ) : (void)0); | |||||
374 | printf("diff ranges %s (%d items):\n", desc, diff->parent.nr); | |||||
375 | printf("\tparent\ttarget\n"); | |||||
376 | for (i = 0; i < diff->parent.nr; i++) { | |||||
377 | printf("\t[%ld,%ld]\t[%ld,%ld]\n", | |||||
378 | diff->parent.ranges[i].start, | |||||
379 | diff->parent.ranges[i].end, | |||||
380 | diff->target.ranges[i].start, | |||||
381 | diff->target.ranges[i].end); | |||||
382 | } | |||||
383 | } | |||||
384 | #endif | |||||
385 | ||||||
386 | ||||||
387 | static int ranges_overlap(struct range *a, struct range *b) | |||||
388 | { | |||||
389 | return !(a->end <= b->start || b->end <= a->start); | |||||
390 | } | |||||
391 | ||||||
392 | /* | |||||
393 | * Given a diff and the set of interesting ranges, determine all hunks | |||||
394 | * of the diff which touch (overlap) at least one of the interesting | |||||
395 | * ranges in the target. | |||||
396 | */ | |||||
397 | static void diff_ranges_filter_touched(struct diff_ranges *out, | |||||
398 | struct diff_ranges *diff, | |||||
399 | struct range_set *rs) | |||||
400 | { | |||||
401 | int i, j = 0; | |||||
402 | ||||||
403 | assert(out->target.nr == 0)(__builtin_expect(!(out->target.nr == 0), 0) ? __assert_rtn (__func__, "line-log.c", 403, "out->target.nr == 0") : (void )0); | |||||
404 | ||||||
405 | for (i = 0; i < diff->target.nr; i++) { | |||||
406 | while (diff->target.ranges[i].start > rs->ranges[j].end) { | |||||
407 | j++; | |||||
408 | if (j == rs->nr) | |||||
409 | return; | |||||
410 | } | |||||
411 | if (ranges_overlap(&diff->target.ranges[i], &rs->ranges[j])) { | |||||
412 | range_set_append(&out->parent, | |||||
413 | diff->parent.ranges[i].start, | |||||
414 | diff->parent.ranges[i].end); | |||||
415 | range_set_append(&out->target, | |||||
416 | diff->target.ranges[i].start, | |||||
417 | diff->target.ranges[i].end); | |||||
418 | } | |||||
419 | } | |||||
420 | } | |||||
421 | ||||||
422 | /* | |||||
423 | * Adjust the line counts in 'rs' to account for the lines | |||||
424 | * added/removed in the diff. | |||||
425 | */ | |||||
426 | static void range_set_shift_diff(struct range_set *out, | |||||
427 | struct range_set *rs, | |||||
428 | struct diff_ranges *diff) | |||||
429 | { | |||||
430 | int i, j = 0; | |||||
431 | long offset = 0; | |||||
432 | struct range *src = rs->ranges; | |||||
433 | struct range *target = diff->target.ranges; | |||||
434 | struct range *parent = diff->parent.ranges; | |||||
435 | ||||||
436 | for (i = 0; i < rs->nr; i++) { | |||||
437 | while (j < diff->target.nr && src[i].start >= target[j].start) { | |||||
438 | offset += (parent[j].end-parent[j].start) | |||||
439 | - (target[j].end-target[j].start); | |||||
440 | j++; | |||||
441 | } | |||||
442 | range_set_append(out, src[i].start+offset, src[i].end+offset); | |||||
443 | } | |||||
444 | } | |||||
445 | ||||||
446 | /* | |||||
447 | * Given a diff and the set of interesting ranges, map the ranges | |||||
448 | * across the diff. That is: observe that the target commit takes | |||||
449 | * blame for all the + (target-side) ranges. So for every pair of | |||||
450 | * ranges in the diff that was touched, we remove the latter and add | |||||
451 | * its parent side. | |||||
452 | */ | |||||
453 | static void range_set_map_across_diff(struct range_set *out, | |||||
454 | struct range_set *rs, | |||||
455 | struct diff_ranges *diff, | |||||
456 | struct diff_ranges **touched_out) | |||||
457 | { | |||||
458 | struct diff_ranges *touched = xmalloc(sizeof(*touched)); | |||||
459 | struct range_set tmp1 = RANGE_SET_INIT{0}; | |||||
460 | struct range_set tmp2 = RANGE_SET_INIT{0}; | |||||
461 | ||||||
462 | diff_ranges_init(touched); | |||||
463 | diff_ranges_filter_touched(touched, diff, rs); | |||||
464 | range_set_difference(&tmp1, rs, &touched->target); | |||||
465 | range_set_shift_diff(&tmp2, &tmp1, diff); | |||||
466 | range_set_union(out, &tmp2, &touched->parent); | |||||
467 | range_set_release(&tmp1); | |||||
468 | range_set_release(&tmp2); | |||||
469 | ||||||
470 | *touched_out = touched; | |||||
471 | } | |||||
472 | ||||||
473 | static struct commit *check_single_commit(struct rev_info *revs) | |||||
474 | { | |||||
475 | struct object *commit = NULL((void*)0); | |||||
476 | int found = -1; | |||||
477 | int i; | |||||
478 | ||||||
479 | for (i = 0; i < revs->pending.nr; i++) { | |||||
480 | struct object *obj = revs->pending.objects[i].item; | |||||
481 | if (obj->flags & UNINTERESTING(1u<<1)) | |||||
482 | continue; | |||||
483 | obj = deref_tag(obj, NULL((void*)0), 0); | |||||
484 | if (obj->type != OBJ_COMMIT) | |||||
485 | die("Non commit %s?", revs->pending.objects[i].name); | |||||
486 | if (commit) | |||||
487 | die("More than one commit to dig from: %s and %s?", | |||||
488 | revs->pending.objects[i].name, | |||||
489 | revs->pending.objects[found].name); | |||||
490 | commit = obj; | |||||
491 | found = i; | |||||
492 | } | |||||
493 | ||||||
494 | if (!commit) | |||||
495 | die("No commit specified?"); | |||||
496 | ||||||
497 | return (struct commit *) commit; | |||||
498 | } | |||||
499 | ||||||
500 | static void fill_blob_sha1(struct commit *commit, struct diff_filespec *spec) | |||||
501 | { | |||||
502 | unsigned mode; | |||||
503 | unsigned char sha1[20]; | |||||
504 | ||||||
505 | if (get_tree_entry(commit->object.oid.hash, spec->path, | |||||
506 | sha1, &mode)) | |||||
507 | die("There is no path %s in the commit", spec->path); | |||||
508 | fill_filespec(spec, sha1, 1, mode); | |||||
509 | ||||||
510 | return; | |||||
511 | } | |||||
512 | ||||||
513 | static void fill_line_ends(struct diff_filespec *spec, long *lines, | |||||
514 | unsigned long **line_ends) | |||||
515 | { | |||||
516 | int num = 0, size = 50; | |||||
517 | long cur = 0; | |||||
518 | unsigned long *ends = NULL((void*)0); | |||||
519 | char *data = NULL((void*)0); | |||||
520 | ||||||
521 | if (diff_populate_filespec(spec, 0)) | |||||
522 | die("Cannot read blob %s", oid_to_hex(&spec->oid)); | |||||
523 | ||||||
524 | ALLOC_ARRAY(ends, size)(ends) = xmalloc(st_mult(sizeof(*(ends)), (size))); | |||||
525 | ends[cur++] = 0; | |||||
526 | data = spec->data; | |||||
527 | while (num < spec->size) { | |||||
528 | if (data[num] == '\n' || num == spec->size - 1) { | |||||
529 | ALLOC_GROW(ends, (cur + 1), size)do { if (((cur + 1)) > size) { if ((((size)+16)*3/2) < ( (cur + 1))) size = ((cur + 1)); else size = (((size)+16)*3/2) ; (ends) = xrealloc((ends), st_mult(sizeof(*(ends)), (size))) ; } } while (0); | |||||
530 | ends[cur++] = num; | |||||
531 | } | |||||
532 | num++; | |||||
533 | } | |||||
534 | ||||||
535 | /* shrink the array to fit the elements */ | |||||
536 | REALLOC_ARRAY(ends, cur)(ends) = xrealloc((ends), st_mult(sizeof(*(ends)), (cur))); | |||||
537 | *lines = cur-1; | |||||
538 | *line_ends = ends; | |||||
539 | } | |||||
540 | ||||||
541 | struct nth_line_cb { | |||||
542 | struct diff_filespec *spec; | |||||
543 | long lines; | |||||
544 | unsigned long *line_ends; | |||||
545 | }; | |||||
546 | ||||||
547 | static const char *nth_line(void *data, long line) | |||||
548 | { | |||||
549 | struct nth_line_cb *d = data; | |||||
550 | assert(d && line <= d->lines)(__builtin_expect(!(d && line <= d->lines), 0) ? __assert_rtn(__func__, "line-log.c", 550, "d && line <= d->lines" ) : (void)0); | |||||
551 | assert(d->spec && d->spec->data)(__builtin_expect(!(d->spec && d->spec->data ), 0) ? __assert_rtn(__func__, "line-log.c", 551, "d->spec && d->spec->data" ) : (void)0); | |||||
552 | ||||||
553 | if (line == 0) | |||||
554 | return (char *)d->spec->data; | |||||
555 | else | |||||
556 | return (char *)d->spec->data + d->line_ends[line] + 1; | |||||
557 | } | |||||
558 | ||||||
559 | static struct line_log_data * | |||||
560 | parse_lines(struct commit *commit, const char *prefix, struct string_list *args) | |||||
561 | { | |||||
562 | long lines = 0; | |||||
563 | unsigned long *ends = NULL((void*)0); | |||||
564 | struct nth_line_cb cb_data; | |||||
565 | struct string_list_item *item; | |||||
566 | struct line_log_data *ranges = NULL((void*)0); | |||||
567 | struct line_log_data *p; | |||||
568 | ||||||
569 | for_each_string_list_item(item, args)for (item = (args)->items; item < (args)->items + (args )->nr; ++item) { | |||||
570 | const char *name_part, *range_part; | |||||
571 | char *full_name; | |||||
572 | struct diff_filespec *spec; | |||||
573 | long begin = 0, end = 0; | |||||
574 | long anchor; | |||||
575 | ||||||
576 | name_part = skip_range_arg(item->string); | |||||
577 | if (!name_part || *name_part != ':' || !name_part[1]) | |||||
578 | die("-L argument not 'start,end:file' or ':funcname:file': %s", | |||||
579 | item->string); | |||||
580 | range_part = xstrndup(item->string, name_part - item->string); | |||||
581 | name_part++; | |||||
582 | ||||||
583 | full_name = prefix_path(prefix, prefix ? strlen(prefix) : 0, | |||||
584 | name_part); | |||||
585 | ||||||
586 | spec = alloc_filespec(full_name); | |||||
587 | fill_blob_sha1(commit, spec); | |||||
588 | fill_line_ends(spec, &lines, &ends); | |||||
589 | cb_data.spec = spec; | |||||
590 | cb_data.lines = lines; | |||||
591 | cb_data.line_ends = ends; | |||||
592 | ||||||
593 | p = search_line_log_data(ranges, full_name, NULL((void*)0)); | |||||
594 | if (p && p->ranges.nr) | |||||
595 | anchor = p->ranges.ranges[p->ranges.nr - 1].end + 1; | |||||
596 | else | |||||
597 | anchor = 1; | |||||
598 | ||||||
599 | if (parse_range_arg(range_part, nth_line, &cb_data, | |||||
600 | lines, anchor, &begin, &end, | |||||
601 | full_name)) | |||||
602 | die("malformed -L argument '%s'", range_part); | |||||
603 | if (lines < end || ((lines || begin) && lines < begin)) | |||||
604 | die("file %s has only %lu lines", name_part, lines); | |||||
605 | if (begin < 1) | |||||
606 | begin = 1; | |||||
607 | if (end < 1) | |||||
608 | end = lines; | |||||
609 | begin--; | |||||
610 | line_log_data_insert(&ranges, full_name, begin, end); | |||||
611 | ||||||
612 | free_filespec(spec); | |||||
613 | free(ends); | |||||
614 | ends = NULL((void*)0); | |||||
615 | } | |||||
616 | ||||||
617 | for (p = ranges; p; p = p->next) | |||||
618 | sort_and_merge_range_set(&p->ranges); | |||||
619 | ||||||
620 | return ranges; | |||||
621 | } | |||||
622 | ||||||
623 | static struct line_log_data *line_log_data_copy_one(struct line_log_data *r) | |||||
624 | { | |||||
625 | struct line_log_data *ret = xmalloc(sizeof(*ret)); | |||||
626 | ||||||
627 | assert(r)(__builtin_expect(!(r), 0) ? __assert_rtn(__func__, "line-log.c" , 627, "r") : (void)0); | |||||
628 | line_log_data_init(ret); | |||||
629 | range_set_copy(&ret->ranges, &r->ranges); | |||||
| ||||||
630 | ||||||
631 | ret->path = xstrdup(r->path); | |||||
632 | ||||||
633 | return ret; | |||||
634 | } | |||||
635 | ||||||
636 | static struct line_log_data * | |||||
637 | line_log_data_copy(struct line_log_data *r) | |||||
638 | { | |||||
639 | struct line_log_data *ret = NULL((void*)0); | |||||
640 | struct line_log_data *tmp = NULL((void*)0), *prev = NULL((void*)0); | |||||
641 | ||||||
642 | assert(r)(__builtin_expect(!(r), 0) ? __assert_rtn(__func__, "line-log.c" , 642, "r") : (void)0); | |||||
643 | ret = tmp = prev = line_log_data_copy_one(r); | |||||
644 | r = r->next; | |||||
645 | while (r) { | |||||
646 | tmp = line_log_data_copy_one(r); | |||||
647 | prev->next = tmp; | |||||
648 | prev = tmp; | |||||
649 | r = r->next; | |||||
650 | } | |||||
651 | ||||||
652 | return ret; | |||||
653 | } | |||||
654 | ||||||
655 | /* merge two range sets across files */ | |||||
656 | static struct line_log_data *line_log_data_merge(struct line_log_data *a, | |||||
657 | struct line_log_data *b) | |||||
658 | { | |||||
659 | struct line_log_data *head = NULL((void*)0), **pp = &head; | |||||
660 | ||||||
661 | while (a || b) { | |||||
662 | struct line_log_data *src; | |||||
663 | struct line_log_data *src2 = NULL((void*)0); | |||||
664 | struct line_log_data *d; | |||||
665 | int cmp; | |||||
666 | if (!a) | |||||
667 | cmp = 1; | |||||
668 | else if (!b) | |||||
669 | cmp = -1; | |||||
670 | else | |||||
671 | cmp = strcmp(a->path, b->path); | |||||
672 | if (cmp < 0) { | |||||
673 | src = a; | |||||
674 | a = a->next; | |||||
675 | } else if (cmp == 0) { | |||||
676 | src = a; | |||||
677 | a = a->next; | |||||
678 | src2 = b; | |||||
679 | b = b->next; | |||||
680 | } else { | |||||
681 | src = b; | |||||
682 | b = b->next; | |||||
683 | } | |||||
684 | d = xmalloc(sizeof(struct line_log_data)); | |||||
685 | line_log_data_init(d); | |||||
686 | d->path = xstrdup(src->path); | |||||
687 | *pp = d; | |||||
688 | pp = &d->next; | |||||
689 | if (src2) | |||||
690 | range_set_union(&d->ranges, &src->ranges, &src2->ranges); | |||||
691 | else | |||||
692 | range_set_copy(&d->ranges, &src->ranges); | |||||
693 | } | |||||
694 | ||||||
695 | return head; | |||||
696 | } | |||||
697 | ||||||
698 | static void add_line_range(struct rev_info *revs, struct commit *commit, | |||||
699 | struct line_log_data *range) | |||||
700 | { | |||||
701 | struct line_log_data *old = NULL((void*)0); | |||||
702 | struct line_log_data *new = NULL((void*)0); | |||||
703 | ||||||
704 | old = lookup_decoration(&revs->line_log_data, &commit->object); | |||||
705 | if (old && range) { | |||||
706 | new = line_log_data_merge(old, range); | |||||
707 | free_line_log_data(old); | |||||
708 | } else if (range) | |||||
709 | new = line_log_data_copy(range); | |||||
710 | ||||||
711 | if (new) | |||||
712 | add_decoration(&revs->line_log_data, &commit->object, new); | |||||
713 | } | |||||
714 | ||||||
715 | static void clear_commit_line_range(struct rev_info *revs, struct commit *commit) | |||||
716 | { | |||||
717 | struct line_log_data *r; | |||||
718 | r = lookup_decoration(&revs->line_log_data, &commit->object); | |||||
719 | if (!r) | |||||
720 | return; | |||||
721 | free_line_log_data(r); | |||||
722 | add_decoration(&revs->line_log_data, &commit->object, NULL((void*)0)); | |||||
723 | } | |||||
724 | ||||||
725 | static struct line_log_data *lookup_line_range(struct rev_info *revs, | |||||
726 | struct commit *commit) | |||||
727 | { | |||||
728 | struct line_log_data *ret = NULL((void*)0); | |||||
729 | struct line_log_data *d; | |||||
730 | ||||||
731 | ret = lookup_decoration(&revs->line_log_data, &commit->object); | |||||
732 | ||||||
733 | for (d = ret; d; d = d->next) | |||||
734 | range_set_check_invariants(&d->ranges); | |||||
735 | ||||||
736 | return ret; | |||||
737 | } | |||||
738 | ||||||
739 | void line_log_init(struct rev_info *rev, const char *prefix, struct string_list *args) | |||||
740 | { | |||||
741 | struct commit *commit = NULL((void*)0); | |||||
742 | struct line_log_data *range; | |||||
743 | ||||||
744 | commit = check_single_commit(rev); | |||||
745 | range = parse_lines(commit, prefix, args); | |||||
746 | add_line_range(rev, commit, range); | |||||
747 | ||||||
748 | if (!rev->diffopt.detect_rename) { | |||||
749 | struct line_log_data *r; | |||||
750 | struct argv_array array = ARGV_ARRAY_INIT{ empty_argv, 0, 0 }; | |||||
751 | const char **paths; | |||||
752 | ||||||
753 | for (r = range; r; r = r->next) | |||||
754 | argv_array_push(&array, r->path); | |||||
755 | paths = argv_array_detach(&array); | |||||
756 | ||||||
757 | parse_pathspec(&rev->diffopt.pathspec, 0, | |||||
758 | PATHSPEC_PREFER_FULL(1<<1), "", paths); | |||||
759 | /* strings are now owned by pathspec */ | |||||
760 | free(paths); | |||||
761 | } | |||||
762 | } | |||||
763 | ||||||
764 | static void move_diff_queue(struct diff_queue_struct *dst, | |||||
765 | struct diff_queue_struct *src) | |||||
766 | { | |||||
767 | assert(src != dst)(__builtin_expect(!(src != dst), 0) ? __assert_rtn(__func__, "line-log.c" , 767, "src != dst") : (void)0); | |||||
768 | memcpy(dst, src, sizeof(struct diff_queue_struct))__builtin___memcpy_chk (dst, src, sizeof(struct diff_queue_struct ), __builtin_object_size (dst, 0)); | |||||
769 | DIFF_QUEUE_CLEAR(src)do { (src)->queue = ((void*)0); (src)->nr = (src)->alloc = 0; } while (0); | |||||
770 | } | |||||
771 | ||||||
772 | static void filter_diffs_for_paths(struct line_log_data *range, int keep_deletions) | |||||
773 | { | |||||
774 | int i; | |||||
775 | struct diff_queue_struct outq; | |||||
776 | DIFF_QUEUE_CLEAR(&outq)do { (&outq)->queue = ((void*)0); (&outq)->nr = (&outq)->alloc = 0; } while (0); | |||||
777 | ||||||
778 | for (i = 0; i < diff_queued_diff.nr; i++) { | |||||
779 | struct diff_filepair *p = diff_queued_diff.queue[i]; | |||||
780 | struct line_log_data *rg = NULL((void*)0); | |||||
781 | ||||||
782 | if (!DIFF_FILE_VALID(p->two)(((p->two)->mode) != 0)) { | |||||
783 | if (keep_deletions) | |||||
784 | diff_q(&outq, p); | |||||
785 | else | |||||
786 | diff_free_filepair(p); | |||||
787 | continue; | |||||
788 | } | |||||
789 | for (rg = range; rg; rg = rg->next) { | |||||
790 | if (!strcmp(rg->path, p->two->path)) | |||||
791 | break; | |||||
792 | } | |||||
793 | if (rg) | |||||
794 | diff_q(&outq, p); | |||||
795 | else | |||||
796 | diff_free_filepair(p); | |||||
797 | } | |||||
798 | free(diff_queued_diff.queue); | |||||
799 | diff_queued_diff = outq; | |||||
800 | } | |||||
801 | ||||||
802 | static inline int diff_might_be_rename(void) | |||||
803 | { | |||||
804 | int i; | |||||
805 | for (i = 0; i < diff_queued_diff.nr; i++) | |||||
806 | if (!DIFF_FILE_VALID(diff_queued_diff.queue[i]->one)(((diff_queued_diff.queue[i]->one)->mode) != 0)) { | |||||
807 | /* fprintf(stderr, "diff_might_be_rename found creation of: %s\n", */ | |||||
808 | /* diff_queued_diff.queue[i]->two->path); */ | |||||
809 | return 1; | |||||
810 | } | |||||
811 | return 0; | |||||
812 | } | |||||
813 | ||||||
814 | static void queue_diffs(struct line_log_data *range, | |||||
815 | struct diff_options *opt, | |||||
816 | struct diff_queue_struct *queue, | |||||
817 | struct commit *commit, struct commit *parent) | |||||
818 | { | |||||
819 | assert(commit)(__builtin_expect(!(commit), 0) ? __assert_rtn(__func__, "line-log.c" , 819, "commit") : (void)0); | |||||
820 | ||||||
821 | DIFF_QUEUE_CLEAR(&diff_queued_diff)do { (&diff_queued_diff)->queue = ((void*)0); (&diff_queued_diff )->nr = (&diff_queued_diff)->alloc = 0; } while (0); | |||||
822 | diff_tree_sha1(parent ? parent->tree->object.oid.hash : NULL((void*)0), | |||||
823 | commit->tree->object.oid.hash, "", opt); | |||||
824 | if (opt->detect_rename) { | |||||
825 | filter_diffs_for_paths(range, 1); | |||||
826 | if (diff_might_be_rename()) | |||||
827 | diffcore_std(opt); | |||||
828 | filter_diffs_for_paths(range, 0); | |||||
829 | } | |||||
830 | move_diff_queue(queue, &diff_queued_diff); | |||||
831 | } | |||||
832 | ||||||
833 | static char *get_nth_line(long line, unsigned long *ends, void *data) | |||||
834 | { | |||||
835 | if (line == 0) | |||||
836 | return (char *)data; | |||||
837 | else | |||||
838 | return (char *)data + ends[line] + 1; | |||||
839 | } | |||||
840 | ||||||
841 | static void print_line(const char *prefix, char first, | |||||
842 | long line, unsigned long *ends, void *data, | |||||
843 | const char *color, const char *reset, FILE *file) | |||||
844 | { | |||||
845 | char *begin = get_nth_line(line, ends, data); | |||||
846 | char *end = get_nth_line(line+1, ends, data); | |||||
847 | int had_nl = 0; | |||||
848 | ||||||
849 | if (end > begin && end[-1] == '\n') { | |||||
850 | end--; | |||||
851 | had_nl = 1; | |||||
852 | } | |||||
853 | ||||||
854 | fputs(prefix, file); | |||||
855 | fputs(color, file); | |||||
856 | putc(first, file); | |||||
857 | fwrite(begin, 1, end-begin, file); | |||||
858 | fputs(reset, file); | |||||
859 | putc('\n', file); | |||||
860 | if (!had_nl) | |||||
861 | fputs("\\ No newline at end of file\n", file); | |||||
862 | } | |||||
863 | ||||||
864 | static char *output_prefix(struct diff_options *opt) | |||||
865 | { | |||||
866 | char *prefix = ""; | |||||
867 | ||||||
868 | if (opt->output_prefix) { | |||||
869 | struct strbuf *sb = opt->output_prefix(opt, opt->output_prefix_data); | |||||
870 | prefix = sb->buf; | |||||
871 | } | |||||
872 | ||||||
873 | return prefix; | |||||
874 | } | |||||
875 | ||||||
876 | static void dump_diff_hacky_one(struct rev_info *rev, struct line_log_data *range) | |||||
877 | { | |||||
878 | int i, j = 0; | |||||
879 | long p_lines, t_lines; | |||||
880 | unsigned long *p_ends = NULL((void*)0), *t_ends = NULL((void*)0); | |||||
881 | struct diff_filepair *pair = range->pair; | |||||
882 | struct diff_ranges *diff = &range->diff; | |||||
883 | ||||||
884 | struct diff_options *opt = &rev->diffopt; | |||||
885 | char *prefix = output_prefix(opt); | |||||
886 | const char *c_reset = diff_get_color(opt->use_color, DIFF_RESET); | |||||
887 | const char *c_frag = diff_get_color(opt->use_color, DIFF_FRAGINFO); | |||||
888 | const char *c_meta = diff_get_color(opt->use_color, DIFF_METAINFO); | |||||
889 | const char *c_old = diff_get_color(opt->use_color, DIFF_FILE_OLD); | |||||
890 | const char *c_new = diff_get_color(opt->use_color, DIFF_FILE_NEW); | |||||
891 | const char *c_context = diff_get_color(opt->use_color, DIFF_CONTEXT); | |||||
892 | ||||||
893 | if (!pair || !diff) | |||||
894 | return; | |||||
895 | ||||||
896 | if (pair->one->oid_valid) | |||||
897 | fill_line_ends(pair->one, &p_lines, &p_ends); | |||||
898 | fill_line_ends(pair->two, &t_lines, &t_ends); | |||||
899 | ||||||
900 | fprintf(opt->file, "%s%sdiff --git a/%s b/%s%s\n", prefix, c_meta, pair->one->path, pair->two->path, c_reset); | |||||
901 | fprintf(opt->file, "%s%s--- %s%s%s\n", prefix, c_meta, | |||||
902 | pair->one->oid_valid ? "a/" : "", | |||||
903 | pair->one->oid_valid ? pair->one->path : "/dev/null", | |||||
904 | c_reset); | |||||
905 | fprintf(opt->file, "%s%s+++ b/%s%s\n", prefix, c_meta, pair->two->path, c_reset); | |||||
906 | for (i = 0; i < range->ranges.nr; i++) { | |||||
907 | long p_start, p_end; | |||||
908 | long t_start = range->ranges.ranges[i].start; | |||||
909 | long t_end = range->ranges.ranges[i].end; | |||||
910 | long t_cur = t_start; | |||||
911 | int j_last; | |||||
912 | ||||||
913 | while (j < diff->target.nr && diff->target.ranges[j].end < t_start) | |||||
914 | j++; | |||||
915 | if (j == diff->target.nr || diff->target.ranges[j].start > t_end) | |||||
916 | continue; | |||||
917 | ||||||
918 | /* Scan ahead to determine the last diff that falls in this range */ | |||||
919 | j_last = j; | |||||
920 | while (j_last < diff->target.nr && diff->target.ranges[j_last].start < t_end) | |||||
921 | j_last++; | |||||
922 | if (j_last > j) | |||||
923 | j_last--; | |||||
924 | ||||||
925 | /* | |||||
926 | * Compute parent hunk headers: we know that the diff | |||||
927 | * has the correct line numbers (but not all hunks). | |||||
928 | * So it suffices to shift the start/end according to | |||||
929 | * the line numbers of the first/last hunk(s) that | |||||
930 | * fall in this range. | |||||
931 | */ | |||||
932 | if (t_start < diff->target.ranges[j].start) | |||||
933 | p_start = diff->parent.ranges[j].start - (diff->target.ranges[j].start-t_start); | |||||
934 | else | |||||
935 | p_start = diff->parent.ranges[j].start; | |||||
936 | if (t_end > diff->target.ranges[j_last].end) | |||||
937 | p_end = diff->parent.ranges[j_last].end + (t_end-diff->target.ranges[j_last].end); | |||||
938 | else | |||||
939 | p_end = diff->parent.ranges[j_last].end; | |||||
940 | ||||||
941 | if (!p_start && !p_end) { | |||||
942 | p_start = -1; | |||||
943 | p_end = -1; | |||||
944 | } | |||||
945 | ||||||
946 | /* Now output a diff hunk for this range */ | |||||
947 | fprintf(opt->file, "%s%s@@ -%ld,%ld +%ld,%ld @@%s\n", | |||||
948 | prefix, c_frag, | |||||
949 | p_start+1, p_end-p_start, t_start+1, t_end-t_start, | |||||
950 | c_reset); | |||||
951 | while (j < diff->target.nr && diff->target.ranges[j].start < t_end) { | |||||
952 | int k; | |||||
953 | for (; t_cur < diff->target.ranges[j].start; t_cur++) | |||||
954 | print_line(prefix, ' ', t_cur, t_ends, pair->two->data, | |||||
955 | c_context, c_reset, opt->file); | |||||
956 | for (k = diff->parent.ranges[j].start; k < diff->parent.ranges[j].end; k++) | |||||
957 | print_line(prefix, '-', k, p_ends, pair->one->data, | |||||
958 | c_old, c_reset, opt->file); | |||||
959 | for (; t_cur < diff->target.ranges[j].end && t_cur < t_end; t_cur++) | |||||
960 | print_line(prefix, '+', t_cur, t_ends, pair->two->data, | |||||
961 | c_new, c_reset, opt->file); | |||||
962 | j++; | |||||
963 | } | |||||
964 | for (; t_cur < t_end; t_cur++) | |||||
965 | print_line(prefix, ' ', t_cur, t_ends, pair->two->data, | |||||
966 | c_context, c_reset, opt->file); | |||||
967 | } | |||||
968 | ||||||
969 | free(p_ends); | |||||
970 | free(t_ends); | |||||
971 | } | |||||
972 | ||||||
973 | /* | |||||
974 | * NEEDSWORK: manually building a diff here is not the Right | |||||
975 | * Thing(tm). log -L should be built into the diff pipeline. | |||||
976 | */ | |||||
977 | static void dump_diff_hacky(struct rev_info *rev, struct line_log_data *range) | |||||
978 | { | |||||
979 | fprintf(rev->diffopt.file, "%s\n", output_prefix(&rev->diffopt)); | |||||
980 | while (range) { | |||||
981 | dump_diff_hacky_one(rev, range); | |||||
982 | range = range->next; | |||||
983 | } | |||||
984 | } | |||||
985 | ||||||
986 | /* | |||||
987 | * Unlike most other functions, this destructively operates on | |||||
988 | * 'range'. | |||||
989 | */ | |||||
990 | static int process_diff_filepair(struct rev_info *rev, | |||||
991 | struct diff_filepair *pair, | |||||
992 | struct line_log_data *range, | |||||
993 | struct diff_ranges **diff_out) | |||||
994 | { | |||||
995 | struct line_log_data *rg = range; | |||||
996 | struct range_set tmp; | |||||
997 | struct diff_ranges diff; | |||||
998 | mmfile_t file_parent, file_target; | |||||
999 | ||||||
1000 | assert(pair->two->path)(__builtin_expect(!(pair->two->path), 0) ? __assert_rtn (__func__, "line-log.c", 1000, "pair->two->path") : (void )0); | |||||
1001 | while (rg) { | |||||
1002 | assert(rg->path)(__builtin_expect(!(rg->path), 0) ? __assert_rtn(__func__, "line-log.c", 1002, "rg->path") : (void)0); | |||||
1003 | if (!strcmp(rg->path, pair->two->path)) | |||||
1004 | break; | |||||
1005 | rg = rg->next; | |||||
1006 | } | |||||
1007 | ||||||
1008 | if (!rg) | |||||
1009 | return 0; | |||||
1010 | if (rg->ranges.nr == 0) | |||||
1011 | return 0; | |||||
1012 | ||||||
1013 | assert(pair->two->oid_valid)(__builtin_expect(!(pair->two->oid_valid), 0) ? __assert_rtn (__func__, "line-log.c", 1013, "pair->two->oid_valid") : (void)0); | |||||
1014 | diff_populate_filespec(pair->two, 0); | |||||
1015 | file_target.ptr = pair->two->data; | |||||
1016 | file_target.size = pair->two->size; | |||||
1017 | ||||||
1018 | if (pair->one->oid_valid) { | |||||
1019 | diff_populate_filespec(pair->one, 0); | |||||
1020 | file_parent.ptr = pair->one->data; | |||||
1021 | file_parent.size = pair->one->size; | |||||
1022 | } else { | |||||
1023 | file_parent.ptr = ""; | |||||
1024 | file_parent.size = 0; | |||||
1025 | } | |||||
1026 | ||||||
1027 | diff_ranges_init(&diff); | |||||
1028 | if (collect_diff(&file_parent, &file_target, &diff)) | |||||
1029 | die("unable to generate diff for %s", pair->one->path); | |||||
1030 | ||||||
1031 | /* NEEDSWORK should apply some heuristics to prevent mismatches */ | |||||
1032 | free(rg->path); | |||||
1033 | rg->path = xstrdup(pair->one->path); | |||||
1034 | ||||||
1035 | range_set_init(&tmp, 0); | |||||
1036 | range_set_map_across_diff(&tmp, &rg->ranges, &diff, diff_out); | |||||
1037 | range_set_release(&rg->ranges); | |||||
1038 | range_set_move(&rg->ranges, &tmp); | |||||
1039 | ||||||
1040 | diff_ranges_release(&diff); | |||||
1041 | ||||||
1042 | return ((*diff_out)->parent.nr > 0); | |||||
1043 | } | |||||
1044 | ||||||
1045 | static struct diff_filepair *diff_filepair_dup(struct diff_filepair *pair) | |||||
1046 | { | |||||
1047 | struct diff_filepair *new = xmalloc(sizeof(struct diff_filepair)); | |||||
1048 | new->one = pair->one; | |||||
1049 | new->two = pair->two; | |||||
1050 | new->one->count++; | |||||
1051 | new->two->count++; | |||||
1052 | return new; | |||||
1053 | } | |||||
1054 | ||||||
1055 | static void free_diffqueues(int n, struct diff_queue_struct *dq) | |||||
1056 | { | |||||
1057 | int i, j; | |||||
1058 | for (i = 0; i < n; i++) | |||||
1059 | for (j = 0; j < dq[i].nr; j++) | |||||
1060 | diff_free_filepair(dq[i].queue[j]); | |||||
1061 | free(dq); | |||||
1062 | } | |||||
1063 | ||||||
1064 | static int process_all_files(struct line_log_data **range_out, | |||||
1065 | struct rev_info *rev, | |||||
1066 | struct diff_queue_struct *queue, | |||||
1067 | struct line_log_data *range) | |||||
1068 | { | |||||
1069 | int i, changed = 0; | |||||
1070 | ||||||
1071 | *range_out = line_log_data_copy(range); | |||||
1072 | ||||||
1073 | for (i = 0; i < queue->nr; i++) { | |||||
1074 | struct diff_ranges *pairdiff = NULL((void*)0); | |||||
1075 | struct diff_filepair *pair = queue->queue[i]; | |||||
1076 | if (process_diff_filepair(rev, pair, *range_out, &pairdiff)) { | |||||
1077 | /* | |||||
1078 | * Store away the diff for later output. We | |||||
1079 | * tuck it in the ranges we got as _input_, | |||||
1080 | * since that's the commit that caused the | |||||
1081 | * diff. | |||||
1082 | * | |||||
1083 | * NEEDSWORK not enough when we get around to | |||||
1084 | * doing something interesting with merges; | |||||
1085 | * currently each invocation on a merge parent | |||||
1086 | * trashes the previous one's diff. | |||||
1087 | * | |||||
1088 | * NEEDSWORK tramples over data structures not owned here | |||||
1089 | */ | |||||
1090 | struct line_log_data *rg = range; | |||||
1091 | changed++; | |||||
1092 | while (rg && strcmp(rg->path, pair->two->path)) | |||||
1093 | rg = rg->next; | |||||
1094 | assert(rg)(__builtin_expect(!(rg), 0) ? __assert_rtn(__func__, "line-log.c" , 1094, "rg") : (void)0); | |||||
1095 | rg->pair = diff_filepair_dup(queue->queue[i]); | |||||
1096 | memcpy(&rg->diff, pairdiff, sizeof(struct diff_ranges))__builtin___memcpy_chk (&rg->diff, pairdiff, sizeof(struct diff_ranges), __builtin_object_size (&rg->diff, 0)); | |||||
1097 | } | |||||
1098 | free(pairdiff); | |||||
1099 | } | |||||
1100 | ||||||
1101 | return changed; | |||||
1102 | } | |||||
1103 | ||||||
1104 | int line_log_print(struct rev_info *rev, struct commit *commit) | |||||
1105 | { | |||||
1106 | struct line_log_data *range = lookup_line_range(rev, commit); | |||||
1107 | ||||||
1108 | show_log(rev); | |||||
1109 | dump_diff_hacky(rev, range); | |||||
1110 | return 1; | |||||
1111 | } | |||||
1112 | ||||||
1113 | static int process_ranges_ordinary_commit(struct rev_info *rev, struct commit *commit, | |||||
1114 | struct line_log_data *range) | |||||
1115 | { | |||||
1116 | struct commit *parent = NULL((void*)0); | |||||
1117 | struct diff_queue_struct queue; | |||||
1118 | struct line_log_data *parent_range; | |||||
1119 | int changed; | |||||
1120 | ||||||
1121 | if (commit->parents) | |||||
1122 | parent = commit->parents->item; | |||||
1123 | ||||||
1124 | queue_diffs(range, &rev->diffopt, &queue, commit, parent); | |||||
1125 | changed = process_all_files(&parent_range, rev, &queue, range); | |||||
1126 | if (parent) | |||||
1127 | add_line_range(rev, parent, parent_range); | |||||
1128 | return changed; | |||||
1129 | } | |||||
1130 | ||||||
1131 | static int process_ranges_merge_commit(struct rev_info *rev, struct commit *commit, | |||||
1132 | struct line_log_data *range) | |||||
1133 | { | |||||
1134 | struct diff_queue_struct *diffqueues; | |||||
1135 | struct line_log_data **cand; | |||||
1136 | struct commit **parents; | |||||
1137 | struct commit_list *p; | |||||
1138 | int i; | |||||
1139 | int nparents = commit_list_count(commit->parents); | |||||
1140 | ||||||
1141 | if (nparents > 1 && rev->first_parent_only) | |||||
1142 | nparents = 1; | |||||
1143 | ||||||
1144 | ALLOC_ARRAY(diffqueues, nparents)(diffqueues) = xmalloc(st_mult(sizeof(*(diffqueues)), (nparents ))); | |||||
1145 | ALLOC_ARRAY(cand, nparents)(cand) = xmalloc(st_mult(sizeof(*(cand)), (nparents))); | |||||
1146 | ALLOC_ARRAY(parents, nparents)(parents) = xmalloc(st_mult(sizeof(*(parents)), (nparents))); | |||||
1147 | ||||||
1148 | p = commit->parents; | |||||
1149 | for (i = 0; i < nparents; i++) { | |||||
1150 | parents[i] = p->item; | |||||
1151 | p = p->next; | |||||
1152 | queue_diffs(range, &rev->diffopt, &diffqueues[i], commit, parents[i]); | |||||
1153 | } | |||||
1154 | ||||||
1155 | for (i = 0; i < nparents; i++) { | |||||
1156 | int changed; | |||||
1157 | cand[i] = NULL((void*)0); | |||||
1158 | changed = process_all_files(&cand[i], rev, &diffqueues[i], range); | |||||
1159 | if (!changed) { | |||||
1160 | /* | |||||
1161 | * This parent can take all the blame, so we | |||||
1162 | * don't follow any other path in history | |||||
1163 | */ | |||||
1164 | add_line_range(rev, parents[i], cand[i]); | |||||
1165 | clear_commit_line_range(rev, commit); | |||||
1166 | commit_list_append(parents[i], &commit->parents); | |||||
1167 | free(parents); | |||||
1168 | free(cand); | |||||
1169 | free_diffqueues(nparents, diffqueues); | |||||
1170 | /* NEEDSWORK leaking like a sieve */ | |||||
1171 | return 0; | |||||
1172 | } | |||||
1173 | } | |||||
1174 | ||||||
1175 | /* | |||||
1176 | * No single parent took the blame. We add the candidates | |||||
1177 | * from the above loop to the parents. | |||||
1178 | */ | |||||
1179 | for (i = 0; i < nparents; i++) { | |||||
1180 | add_line_range(rev, parents[i], cand[i]); | |||||
1181 | } | |||||
1182 | ||||||
1183 | clear_commit_line_range(rev, commit); | |||||
1184 | free(parents); | |||||
1185 | free(cand); | |||||
1186 | free_diffqueues(nparents, diffqueues); | |||||
1187 | return 1; | |||||
1188 | ||||||
1189 | /* NEEDSWORK evil merge detection stuff */ | |||||
1190 | /* NEEDSWORK leaking like a sieve */ | |||||
1191 | } | |||||
1192 | ||||||
1193 | static int process_ranges_arbitrary_commit(struct rev_info *rev, struct commit *commit) | |||||
1194 | { | |||||
1195 | struct line_log_data *range = lookup_line_range(rev, commit); | |||||
1196 | int changed = 0; | |||||
1197 | ||||||
1198 | if (range) { | |||||
1199 | if (!commit->parents || !commit->parents->next) | |||||
1200 | changed = process_ranges_ordinary_commit(rev, commit, range); | |||||
1201 | else | |||||
1202 | changed = process_ranges_merge_commit(rev, commit, range); | |||||
1203 | } | |||||
1204 | ||||||
1205 | if (!changed) | |||||
1206 | commit->object.flags |= TREESAME(1u<<2); | |||||
1207 | ||||||
1208 | return changed; | |||||
1209 | } | |||||
1210 | ||||||
1211 | static enum rewrite_result line_log_rewrite_one(struct rev_info *rev, struct commit **pp) | |||||
1212 | { | |||||
1213 | for (;;) { | |||||
1214 | struct commit *p = *pp; | |||||
1215 | if (p->parents && p->parents->next) | |||||
1216 | return rewrite_one_ok; | |||||
1217 | if (p->object.flags & UNINTERESTING(1u<<1)) | |||||
1218 | return rewrite_one_ok; | |||||
1219 | if (!(p->object.flags & TREESAME(1u<<2))) | |||||
1220 | return rewrite_one_ok; | |||||
1221 | if (!p->parents) | |||||
1222 | return rewrite_one_noparents; | |||||
1223 | *pp = p->parents->item; | |||||
1224 | } | |||||
1225 | } | |||||
1226 | ||||||
1227 | int line_log_filter(struct rev_info *rev) | |||||
1228 | { | |||||
1229 | struct commit *commit; | |||||
1230 | struct commit_list *list = rev->commits; | |||||
1231 | struct commit_list *out = NULL((void*)0), **pp = &out; | |||||
1232 | ||||||
1233 | while (list) { | |||||
1234 | struct commit_list *to_free = NULL((void*)0); | |||||
1235 | commit = list->item; | |||||
1236 | if (process_ranges_arbitrary_commit(rev, commit)) { | |||||
1237 | *pp = list; | |||||
1238 | pp = &list->next; | |||||
1239 | } else | |||||
1240 | to_free = list; | |||||
1241 | list = list->next; | |||||
1242 | free(to_free); | |||||
1243 | } | |||||
1244 | *pp = NULL((void*)0); | |||||
1245 | ||||||
1246 | for (list = out; list; list = list->next) | |||||
1247 | rewrite_parents(rev, list->item, line_log_rewrite_one); | |||||
1248 | ||||||
1249 | rev->commits = out; | |||||
1250 | ||||||
1251 | return 0; | |||||
1252 | } |