static int pickaxe_match(struct diff_filepair *p, struct diff_options *o, regex_t *regexp, kwset_t kws, pickaxe_fn fn) { struct userdiff_driver *textconv_one = NULL; struct userdiff_driver *textconv_two = NULL; mmfile_t mf1, mf2; int ret; if (!o->pickaxe[0]) return 0; /* ignore unmerged */ if (!DIFF_FILE_VALID(p->one) && !DIFF_FILE_VALID(p->two)) return 0; if (DIFF_OPT_TST(o, ALLOW_TEXTCONV)) { textconv_one = get_textconv(p->one); textconv_two = get_textconv(p->two); } /* * If we have an unmodified pair, we know that the count will be the * same and don't even have to load the blobs. Unless textconv is in * play, _and_ we are using two different textconv filters (e.g., * because a pair is an exact rename with different textconv attributes * for each side, which might generate different content). */ if (textconv_one == textconv_two && diff_unmodified_pair(p)) return 0; mf1.size = fill_textconv(textconv_one, p->one, &mf1.ptr); mf2.size = fill_textconv(textconv_two, p->two, &mf2.ptr); ret = fn(DIFF_FILE_VALID(p->one) ? &mf1 : NULL, DIFF_FILE_VALID(p->two) ? &mf2 : NULL, o, regexp, kws); if (textconv_one) free(mf1.ptr); if (textconv_two) free(mf2.ptr); diff_free_filespec_data(p->one); diff_free_filespec_data(p->two); return ret; }
/* * Does it look like the resulting diff might be due to a rename? * - single entry * - not a valid previous file */ static inline int diff_might_be_rename(void) { return diff_queued_diff.nr == 1 && !DIFF_FILE_VALID(diff_queued_diff.queue[0]->one); }
void diffcore_pickaxe(const char *needle, int opts) { struct diff_queue_struct *q = &diff_queued_diff; unsigned long len = strlen(needle); int i, has_changes; regex_t regex, *regexp = NULL; struct diff_queue_struct outq; outq.queue = NULL; outq.nr = outq.alloc = 0; if (opts & DIFF_PICKAXE_REGEX) { int err; err = regcomp(®ex, needle, REG_EXTENDED | REG_NEWLINE); if (err) { /* The POSIX.2 people are surely sick */ char errbuf[1024]; regerror(err, ®ex, errbuf, 1024); regfree(®ex); die("invalid pickaxe regex: %s", errbuf); } regexp = ®ex; } if (opts & DIFF_PICKAXE_ALL) { /* Showing the whole changeset if needle exists */ for (i = has_changes = 0; !has_changes && i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; if (!DIFF_FILE_VALID(p->one)) { if (!DIFF_FILE_VALID(p->two)) continue; /* ignore unmerged */ /* created */ if (contains(p->two, needle, len, regexp)) has_changes++; } else if (!DIFF_FILE_VALID(p->two)) { if (contains(p->one, needle, len, regexp)) has_changes++; } else if (!diff_unmodified_pair(p) && contains(p->one, needle, len, regexp) != contains(p->two, needle, len, regexp)) has_changes++; } if (has_changes) return; /* not munge the queue */ /* otherwise we will clear the whole queue * by copying the empty outq at the end of this * function, but first clear the current entries * in the queue. */ for (i = 0; i < q->nr; i++) diff_free_filepair(q->queue[i]); } else /* Showing only the filepairs that has the needle */ for (i = 0; i < q->nr; i++) { struct diff_filepair *p = q->queue[i]; has_changes = 0; if (!DIFF_FILE_VALID(p->one)) { if (!DIFF_FILE_VALID(p->two)) ; /* ignore unmerged */ /* created */ else if (contains(p->two, needle, len, regexp)) has_changes = 1; } else if (!DIFF_FILE_VALID(p->two)) { if (contains(p->one, needle, len, regexp)) has_changes = 1; } else if (!diff_unmodified_pair(p) && contains(p->one, needle, len, regexp) != contains(p->two, needle, len, regexp)) has_changes = 1; if (has_changes) diff_q(&outq, p); else diff_free_filepair(p); } if (opts & DIFF_PICKAXE_REGEX) { regfree(®ex); } free(q->queue); *q = outq; return; }
/* * For lines target is suspected for, see if we can find code movement * across file boundary from the parent commit. porigin is the path * in the parent we already tried. */ static void find_copy_in_parent(struct blame_scoreboard *sb, struct blame_entry ***blamed, struct blame_entry **toosmall, struct blame_origin *target, struct commit *parent, struct blame_origin *porigin, int opt) { struct diff_options diff_opts; int i, j; struct blame_list *blame_list; int num_ents; struct blame_entry *unblamed = target->suspects; struct blame_entry *leftover = NULL; if (!unblamed) return; /* nothing remains for this target */ diff_setup(&diff_opts); diff_opts.flags.recursive = 1; diff_opts.output_format = DIFF_FORMAT_NO_OUTPUT; diff_setup_done(&diff_opts); /* Try "find copies harder" on new path if requested; * we do not want to use diffcore_rename() actually to * match things up; find_copies_harder is set only to * force diff_tree_oid() to feed all filepairs to diff_queue, * and this code needs to be after diff_setup_done(), which * usually makes find-copies-harder imply copy detection. */ if ((opt & PICKAXE_BLAME_COPY_HARDEST) || ((opt & PICKAXE_BLAME_COPY_HARDER) && (!porigin || strcmp(target->path, porigin->path)))) diff_opts.flags.find_copies_harder = 1; if (is_null_oid(&target->commit->object.oid)) do_diff_cache(get_commit_tree_oid(parent), &diff_opts); else diff_tree_oid(get_commit_tree_oid(parent), get_commit_tree_oid(target->commit), "", &diff_opts); if (!diff_opts.flags.find_copies_harder) diffcore_std(&diff_opts); do { struct blame_entry **unblamedtail = &unblamed; blame_list = setup_blame_list(unblamed, &num_ents); for (i = 0; i < diff_queued_diff.nr; i++) { struct diff_filepair *p = diff_queued_diff.queue[i]; struct blame_origin *norigin; mmfile_t file_p; struct blame_entry potential[3]; if (!DIFF_FILE_VALID(p->one)) continue; /* does not exist in parent */ if (S_ISGITLINK(p->one->mode)) continue; /* ignore git links */ if (porigin && !strcmp(p->one->path, porigin->path)) /* find_move already dealt with this path */ continue; norigin = get_origin(parent, p->one->path); oidcpy(&norigin->blob_oid, &p->one->oid); norigin->mode = p->one->mode; fill_origin_blob(&sb->revs->diffopt, norigin, &file_p, &sb->num_read_blob); if (!file_p.ptr) continue; for (j = 0; j < num_ents; j++) { find_copy_in_blob(sb, blame_list[j].ent, norigin, potential, &file_p); copy_split_if_better(sb, blame_list[j].split, potential); decref_split(potential); } blame_origin_decref(norigin); } for (j = 0; j < num_ents; j++) { struct blame_entry *split = blame_list[j].split; if (split[1].suspect && sb->copy_score < blame_entry_score(sb, &split[1])) { split_blame(blamed, &unblamedtail, split, blame_list[j].ent); } else { blame_list[j].ent->next = leftover; leftover = blame_list[j].ent; } decref_split(split); } free(blame_list); *unblamedtail = NULL; toosmall = filter_small(sb, toosmall, &unblamed, sb->copy_score); } while (unblamed); target->suspects = reverse_blame(leftover, NULL); diff_flush(&diff_opts); clear_pathspec(&diff_opts.pathspec); }