summaryrefslogtreecommitdiff
path: root/diff-lib.c
diff options
context:
space:
mode:
Diffstat (limited to 'diff-lib.c')
-rw-r--r--diff-lib.c80
1 files changed, 76 insertions, 4 deletions
diff --git a/diff-lib.c b/diff-lib.c
index f95c6de75f..b73cc1859a 100644
--- a/diff-lib.c
+++ b/diff-lib.c
@@ -13,6 +13,7 @@
#include "submodule.h"
#include "dir.h"
#include "fsmonitor.h"
+#include "commit-reach.h"
/*
* diff-files
@@ -97,6 +98,8 @@ int run_diff_files(struct rev_info *revs, unsigned int option)
diff_set_mnemonic_prefix(&revs->diffopt, "i/", "w/");
+ refresh_fsmonitor(istate);
+
if (diff_unmerged_stage < 0)
diff_unmerged_stage = 2;
entries = istate->cache_nr;
@@ -197,8 +200,17 @@ 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 {
@@ -510,16 +522,74 @@ 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;
+ 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];
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))
+
+ 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/");
@@ -536,10 +606,12 @@ int do_diff_cache(const struct object_id *tree_oid, struct diff_options *opt)
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);
+ clear_pathspec(&revs.prune_data);
return 0;
}