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