| 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 | } |