diff options
Diffstat (limited to 'diff-lib.c')
| -rw-r--r-- | diff-lib.c | 228 |
1 files changed, 181 insertions, 47 deletions
diff --git a/diff-lib.c b/diff-lib.c index a9f38eb5a3..70b3578b90 100644 --- a/diff-lib.c +++ b/diff-lib.c @@ -6,6 +6,7 @@ #include "commit.h" #include "diff.h" #include "diffcore.h" +#include "hex.h" #include "revision.h" #include "cache-tree.h" #include "unpack-trees.h" @@ -13,6 +14,7 @@ #include "submodule.h" #include "dir.h" #include "fsmonitor.h" +#include "commit-reach.h" /* * diff-files @@ -27,9 +29,10 @@ * exists for ce that is a submodule -- it is a submodule that is not * checked out). Return negative for an error. */ -static int check_removed(const struct cache_entry *ce, struct stat *st) +static int check_removed(const struct index_state *istate, const struct cache_entry *ce, struct stat *st) { - if (lstat(ce->name, st) < 0) { + assert(is_fsmonitor_refreshed(istate)); + if (!(ce->ce_flags & CE_FSMONITOR_VALID) && lstat(ce->name, st) < 0) { if (!is_missing_file_error(errno)) return -1; return 1; @@ -70,7 +73,7 @@ static int match_stat_with_submodule(struct diff_options *diffopt, struct stat *st, unsigned ce_option, unsigned *dirty_submodule) { - int changed = ce_match_stat(ce, st, ce_option); + int changed = ie_match_stat(diffopt->repo->index, ce, st, ce_option); if (S_ISGITLINK(ce->ce_mode)) { struct diff_flags orig_flags = diffopt->flags; if (!diffopt->flags.override_submodule_config) @@ -93,15 +96,18 @@ int run_diff_files(struct rev_info *revs, unsigned int option) unsigned ce_option = ((option & DIFF_RACY_IS_MODIFIED) ? CE_MATCH_RACY_IS_DIRTY : 0); uint64_t start = getnanotime(); + struct index_state *istate = revs->diffopt.repo->index; diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/"); + refresh_fsmonitor(istate); + if (diff_unmerged_stage < 0) diff_unmerged_stage = 2; - entries = active_nr; + entries = istate->cache_nr; for (i = 0; i < entries; i++) { unsigned int oldmode, newmode; - struct cache_entry *ce = active_cache[i]; + struct cache_entry *ce = istate->cache[i]; int changed; unsigned dirty_submodule = 0; const struct object_id *old_oid, *new_oid; @@ -109,7 +115,11 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (diff_can_quit_early(&revs->diffopt)) break; - if (!ce_path_match(ce, &revs->prune_data, NULL)) + if (!ce_path_match(istate, ce, &revs->prune_data, NULL)) + continue; + + if (revs->diffopt.prefix && + strncmp(ce->name, revs->diffopt.prefix, revs->diffopt.prefix_length)) continue; if (ce_stage(ce)) { @@ -132,7 +142,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) memset(&(dpath->parent[0]), 0, sizeof(struct combine_diff_parent)*5); - changed = check_removed(ce, &st); + changed = check_removed(istate, ce, &st); if (!changed) wt_mode = ce_mode_from_stat(ce, st.st_mode); else { @@ -145,7 +155,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) dpath->mode = wt_mode; while (i < entries) { - struct cache_entry *nce = active_cache[i]; + struct cache_entry *nce = istate->cache[i]; int stage; if (strcmp(ce->name, nce->name)) @@ -176,9 +186,7 @@ int run_diff_files(struct rev_info *revs, unsigned int option) i--; if (revs->combine_merges && num_compare_stages == 2) { - show_combined_diff(dpath, 2, - revs->dense_combined_merges, - revs); + show_combined_diff(dpath, 2, revs); free(dpath); continue; } @@ -198,14 +206,23 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (ce_uptodate(ce) || ce_skip_worktree(ce)) continue; - /* If CE_VALID is set, don't look at workdir for file removal */ - if (ce->ce_flags & CE_VALID) { + /* + * When CE_VALID is set (via "update-index --assume-unchanged" + * or via adding paths while core.ignorestat is set to true), + * the user has promised that the working tree file for that + * path will not be modified. When CE_FSMONITOR_VALID is true, + * the fsmonitor knows that the path hasn't been modified since + * we refreshed the cached stat information. In either case, + * we do not have to stat to see if the path has been removed + * or modified. + */ + if (ce->ce_flags & (CE_VALID | CE_FSMONITOR_VALID)) { changed = 0; newmode = ce->ce_mode; } else { struct stat st; - changed = check_removed(ce, &st); + changed = check_removed(istate, ce, &st); if (changed) { if (changed < 0) { perror(ce->name); @@ -218,9 +235,9 @@ int run_diff_files(struct rev_info *revs, unsigned int option) continue; } else if (revs->diffopt.ita_invisible_in_index && ce_intent_to_add(ce)) { - diff_addremove(&revs->diffopt, '+', ce->ce_mode, - the_hash_algo->empty_tree, 0, - ce->name, 0); + newmode = ce_mode_from_stat(ce, st.st_mode); + diff_addremove(&revs->diffopt, '+', newmode, + null_oid(), 0, ce->name, 0); continue; } @@ -231,13 +248,13 @@ int run_diff_files(struct rev_info *revs, unsigned int option) if (!changed && !dirty_submodule) { ce_mark_uptodate(ce); - mark_fsmonitor_valid(ce); + mark_fsmonitor_valid(istate, ce); if (!revs->diffopt.flags.find_copies_harder) continue; } oldmode = ce->ce_mode; old_oid = &ce->oid; - new_oid = changed ? &null_oid : &ce->oid; + new_oid = changed ? null_oid() : &ce->oid; diff_change(&revs->diffopt, oldmode, newmode, old_oid, new_oid, !is_null_oid(old_oid), @@ -267,7 +284,8 @@ static void diff_index_show_file(struct rev_info *revs, oid, oid_valid, ce->name, dirty_submodule); } -static int get_stat_data(const struct cache_entry *ce, +static int get_stat_data(const struct index_state *istate, + const struct cache_entry *ce, const struct object_id **oidp, unsigned int *modep, int cached, int match_missing, @@ -279,7 +297,7 @@ static int get_stat_data(const struct cache_entry *ce, if (!cached && !ce_uptodate(ce)) { int changed; struct stat st; - changed = check_removed(ce, &st); + changed = check_removed(istate, ce, &st); if (changed < 0) return -1; else if (changed) { @@ -294,7 +312,7 @@ static int get_stat_data(const struct cache_entry *ce, 0, dirty_submodule); if (changed) { mode = ce_mode_from_stat(ce, st.st_mode); - oid = &null_oid; + oid = null_oid(); } } @@ -310,12 +328,18 @@ static void show_new_file(struct rev_info *revs, const struct object_id *oid; unsigned int mode; unsigned dirty_submodule = 0; + struct index_state *istate = revs->diffopt.repo->index; + + if (new_file && S_ISSPARSEDIR(new_file->ce_mode)) { + diff_tree_oid(NULL, &new_file->oid, new_file->name, &revs->diffopt); + return; + } /* * New file in the index: it might actually be different in * the working tree. */ - if (get_stat_data(new_file, &oid, &mode, cached, match_missing, + if (get_stat_data(istate, new_file, &oid, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) return; @@ -331,8 +355,23 @@ static int show_modified(struct rev_info *revs, unsigned int mode, oldmode; const struct object_id *oid; unsigned dirty_submodule = 0; + struct index_state *istate = revs->diffopt.repo->index; + + assert(S_ISSPARSEDIR(old_entry->ce_mode) == + S_ISSPARSEDIR(new_entry->ce_mode)); - if (get_stat_data(new_entry, &oid, &mode, cached, match_missing, + /* + * If both are sparse directory entries, then expand the + * modifications to the file level. If only one was a sparse + * directory, then they appear as an add and delete instead of + * a modification. + */ + if (S_ISSPARSEDIR(new_entry->ce_mode)) { + diff_tree_oid(&old_entry->oid, &new_entry->oid, new_entry->name, &revs->diffopt); + return 0; + } + + if (get_stat_data(istate, new_entry, &oid, &mode, cached, match_missing, &dirty_submodule, &revs->diffopt) < 0) { if (report_missing) diff_index_show_file(revs, "-", old_entry, @@ -342,7 +381,7 @@ static int show_modified(struct rev_info *revs, } if (revs->combine_merges && !cached && - (oidcmp(oid, &old_entry->oid) || oidcmp(&old_entry->oid, &new_entry->oid))) { + (!oideq(oid, &old_entry->oid) || !oideq(&old_entry->oid, &new_entry->oid))) { struct combine_diff_path *p; int pathlen = ce_namelen(new_entry); @@ -360,13 +399,13 @@ static int show_modified(struct rev_info *revs, p->parent[1].status = DIFF_STATUS_MODIFIED; p->parent[1].mode = old_entry->ce_mode; oidcpy(&p->parent[1].oid, &old_entry->oid); - show_combined_diff(p, 2, revs->dense_combined_merges, revs); + show_combined_diff(p, 2, revs); free(p); return 0; } oldmode = old_entry->ce_mode; - if (mode == oldmode && !oidcmp(oid, &old_entry->oid) && !dirty_submodule && + if (mode == oldmode && oideq(oid, &old_entry->oid) && !dirty_submodule && !revs->diffopt.flags.find_copies_harder) return 0; @@ -404,14 +443,8 @@ static void do_oneway_diff(struct unpack_trees_options *o, /* if the entry is not checked out, don't examine work tree */ cached = o->index_only || (idx && ((idx->ce_flags & CE_VALID) || ce_skip_worktree(idx))); - /* - * Backward compatibility wart - "diff-index -m" does - * not mean "do not ignore merges", but "match_missing". - * - * But with the revision flag parsing, that's found in - * "!revs->ignore_merges". - */ - match_missing = !revs->ignore_merges; + + match_missing = revs->match_missing; if (cached && idx && ce_stage(idx)) { struct diff_filepair *pair; @@ -434,6 +467,11 @@ static void do_oneway_diff(struct unpack_trees_options *o, * Something removed from the tree? */ if (!idx) { + if (S_ISSPARSEDIR(tree->ce_mode)) { + diff_tree_oid(&tree->oid, NULL, tree->name, &revs->diffopt); + return; + } + diff_index_show_file(revs, "-", tree, &tree->oid, 1, tree->ce_mode, 0); return; @@ -474,7 +512,9 @@ static int oneway_diff(const struct cache_entry * const *src, if (tree == o->df_conflict_entry) tree = NULL; - if (ce_path_match(idx ? idx : tree, &revs->prune_data, NULL)) { + if (ce_path_match(revs->diffopt.repo->index, + idx ? idx : tree, + &revs->prune_data, NULL)) { do_oneway_diff(o, idx, tree); if (diff_can_quit_early(&revs->diffopt)) { o->exiting_early = 1; @@ -506,7 +546,7 @@ static int diff_cache(struct rev_info *revs, opts.merge = 1; opts.fn = oneway_diff; opts.unpack_data = revs; - opts.src_index = &the_index; + opts.src_index = revs->diffopt.repo->index; opts.dst_index = NULL; opts.pathspec = &revs->diffopt.pathspec; opts.pathspec->recursive = 1; @@ -515,20 +555,84 @@ static int diff_cache(struct rev_info *revs, return unpack_trees(1, &t, &opts); } -int run_diff_index(struct rev_info *revs, int cached) +void diff_get_merge_base(const struct rev_info *revs, struct object_id *mb) +{ + int i; + struct commit *mb_child[2] = {0}; + struct commit_list *merge_bases; + + for (i = 0; i < revs->pending.nr; i++) { + struct object *obj = revs->pending.objects[i].item; + if (obj->flags) + die(_("--merge-base does not work with ranges")); + if (obj->type != OBJ_COMMIT) + die(_("--merge-base only works with commits")); + } + + /* + * This check must go after the for loop above because A...B + * ranges produce three pending commits, resulting in a + * misleading error message. + */ + if (revs->pending.nr < 1 || revs->pending.nr > 2) + BUG("unexpected revs->pending.nr: %d", revs->pending.nr); + + for (i = 0; i < revs->pending.nr; i++) + mb_child[i] = lookup_commit_reference(the_repository, &revs->pending.objects[i].item->oid); + if (revs->pending.nr == 1) { + struct object_id oid; + + if (get_oid("HEAD", &oid)) + die(_("unable to get HEAD")); + + mb_child[1] = lookup_commit_reference(the_repository, &oid); + } + + merge_bases = repo_get_merge_bases(the_repository, mb_child[0], mb_child[1]); + if (!merge_bases) + die(_("no merge base found")); + if (merge_bases->next) + die(_("multiple merge bases found")); + + oidcpy(mb, &merge_bases->item->object.oid); + + free_commit_list(merge_bases); +} + +int run_diff_index(struct rev_info *revs, unsigned int option) { struct object_array_entry *ent; - uint64_t start = getnanotime(); + int cached = !!(option & DIFF_INDEX_CACHED); + int merge_base = !!(option & DIFF_INDEX_MERGE_BASE); + struct object_id oid; + const char *name; + char merge_base_hex[GIT_MAX_HEXSZ + 1]; + struct index_state *istate = revs->diffopt.repo->index; + + if (revs->pending.nr != 1) + BUG("run_diff_index must be passed exactly one tree"); + trace_performance_enter(); ent = revs->pending.objects; - if (diff_cache(revs, &ent->item->oid, ent->name, cached)) + + refresh_fsmonitor(istate); + + if (merge_base) { + diff_get_merge_base(revs, &oid); + name = oid_to_hex_r(merge_base_hex, &oid); + } else { + oidcpy(&oid, &ent->item->oid); + name = ent->name; + } + + if (diff_cache(revs, &oid, name, cached)) exit(128); diff_set_mnemonic_prefix(&revs->diffopt, "c/", cached ? "i/" : "w/"); - diffcore_fix_diff_index(&revs->diffopt); + diffcore_fix_diff_index(); diffcore_std(&revs->diffopt); diff_flush(&revs->diffopt); - trace_performance_since(start, "diff-index"); + trace_performance_leave("diff-index"); return 0; } @@ -536,22 +640,26 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt) { struct rev_info revs; - init_revisions(&revs, NULL); + repo_init_revisions(opt->repo, &revs, NULL); copy_pathspec(&revs.prune_data, &opt->pathspec); + diff_setup_done(&revs.diffopt); revs.diffopt = *opt; if (diff_cache(&revs, tree_oid, NULL, 1)) exit(128); + release_revisions(&revs); return 0; } -int index_differs_from(const char *def, const struct diff_flags *flags, +int index_differs_from(struct repository *r, + const char *def, const struct diff_flags *flags, int ita_invisible_in_index) { struct rev_info rev; struct setup_revision_opt opt; + unsigned has_changes; - init_revisions(&rev, NULL); + repo_init_revisions(r, &rev, NULL); memset(&opt, 0, sizeof(opt)); opt.def = def; setup_revisions(0, NULL, &rev, &opt); @@ -561,6 +669,32 @@ int index_differs_from(const char *def, const struct diff_flags *flags, diff_flags_or(&rev.diffopt.flags, flags); rev.diffopt.ita_invisible_in_index = ita_invisible_in_index; run_diff_index(&rev, 1); - object_array_clear(&rev.pending); - return (rev.diffopt.flags.has_changes != 0); + has_changes = rev.diffopt.flags.has_changes; + release_revisions(&rev); + return (has_changes != 0); +} + +static struct strbuf *idiff_prefix_cb(struct diff_options *opt UNUSED, void *data) +{ + return data; +} + +void show_interdiff(const struct object_id *oid1, const struct object_id *oid2, + int indent, struct diff_options *diffopt) +{ + struct diff_options opts; + struct strbuf prefix = STRBUF_INIT; + + memcpy(&opts, diffopt, sizeof(opts)); + opts.output_format = DIFF_FORMAT_PATCH; + opts.output_prefix = idiff_prefix_cb; + strbuf_addchars(&prefix, ' ', indent); + opts.output_prefix_data = &prefix; + diff_setup_done(&opts); + + diff_tree_oid(oid1, oid2, "", &opts); + diffcore_std(&opts); + diff_flush(&opts); + + strbuf_release(&prefix); } |
