| |
@@ -6,6 +6,7 @@
|
| |
#define USE_THE_INDEX_COMPATIBILITY_MACROS
|
| |
#include "cache.h"
|
| |
#include "config.h"
|
| |
+ #include "ewah/ewok.h"
|
| |
#include "lockfile.h"
|
| |
#include "color.h"
|
| |
#include "commit.h"
|
| |
@@ -23,7 +24,13 @@
|
| |
#define DIFF_NO_INDEX_IMPLICIT 2
|
| |
|
| |
static const char builtin_diff_usage[] =
|
| |
- "git diff [<options>] [<commit> [<commit>]] [--] [<path>...]";
|
| |
+ "git diff [<options>] [<commit>] [--] [<path>...]\n"
|
| |
+ " or: git diff [<options>] --cached [<commit>] [--] [<path>...]\n"
|
| |
+ " or: git diff [<options>] <commit> [<commit>...] <commit> [--] [<path>...]\n"
|
| |
+ " or: git diff [<options>] <commit>...<commit>] [--] [<path>...]\n"
|
| |
+ " or: git diff [<options>] <blob> <blob>]\n"
|
| |
+ " or: git diff [<options>] --no-index [--] <path> <path>]\n"
|
| |
+ COMMON_DIFF_OPTIONS_HELP;
|
| |
|
| |
static const char *blob_path(struct object_array_entry *entry)
|
| |
{
|
| |
@@ -254,6 +261,108 @@
|
| |
return run_diff_files(revs, options);
|
| |
}
|
| |
|
| |
+ struct symdiff {
|
| |
+ struct bitmap *skip;
|
| |
+ int warn;
|
| |
+ const char *base, *left, *right;
|
| |
+ };
|
| |
+
|
| |
+ /*
|
| |
+ * Check for symmetric-difference arguments, and if present, arrange
|
| |
+ * everything we need to know to handle them correctly. As a bonus,
|
| |
+ * weed out all bogus range-based revision specifications, e.g.,
|
| |
+ * "git diff A..B C..D" or "git diff A..B C" get rejected.
|
| |
+ *
|
| |
+ * For an actual symmetric diff, *symdiff is set this way:
|
| |
+ *
|
| |
+ * - its skip is non-NULL and marks *all* rev->pending.objects[i]
|
| |
+ * indices that the caller should ignore (extra merge bases, of
|
| |
+ * which there might be many, and A in A...B). Note that the
|
| |
+ * chosen merge base and right side are NOT marked.
|
| |
+ * - warn is set if there are multiple merge bases.
|
| |
+ * - base, left, and right point to the names to use in a
|
| |
+ * warning about multiple merge bases.
|
| |
+ *
|
| |
+ * If there is no symmetric diff argument, sym->skip is NULL and
|
| |
+ * sym->warn is cleared. The remaining fields are not set.
|
| |
+ */
|
| |
+ static void symdiff_prepare(struct rev_info *rev, struct symdiff *sym)
|
| |
+ {
|
| |
+ int i, is_symdiff = 0, basecount = 0, othercount = 0;
|
| |
+ int lpos = -1, rpos = -1, basepos = -1;
|
| |
+ struct bitmap *map = NULL;
|
| |
+
|
| |
+ /*
|
| |
+ * Use the whence fields to find merge bases and left and
|
| |
+ * right parts of symmetric difference, so that we do not
|
| |
+ * depend on the order that revisions are parsed. If there
|
| |
+ * are any revs that aren't from these sources, we have a
|
| |
+ * "git diff C A...B" or "git diff A...B C" case. Or we
|
| |
+ * could even get "git diff A...B C...E", for instance.
|
| |
+ *
|
| |
+ * If we don't have just one merge base, we pick one
|
| |
+ * at random.
|
| |
+ *
|
| |
+ * NB: REV_CMD_LEFT, REV_CMD_RIGHT are also used for A..B,
|
| |
+ * so we must check for SYMMETRIC_LEFT too. The two arrays
|
| |
+ * rev->pending.objects and rev->cmdline.rev are parallel.
|
| |
+ */
|
| |
+ for (i = 0; i < rev->cmdline.nr; i++) {
|
| |
+ struct object *obj = rev->pending.objects[i].item;
|
| |
+ switch (rev->cmdline.rev[i].whence) {
|
| |
+ case REV_CMD_MERGE_BASE:
|
| |
+ if (basepos < 0)
|
| |
+ basepos = i;
|
| |
+ basecount++;
|
| |
+ break; /* do mark all bases */
|
| |
+ case REV_CMD_LEFT:
|
| |
+ if (lpos >= 0)
|
| |
+ usage(builtin_diff_usage);
|
| |
+ lpos = i;
|
| |
+ if (obj->flags & SYMMETRIC_LEFT) {
|
| |
+ is_symdiff = 1;
|
| |
+ break; /* do mark A */
|
| |
+ }
|
| |
+ continue;
|
| |
+ case REV_CMD_RIGHT:
|
| |
+ if (rpos >= 0)
|
| |
+ usage(builtin_diff_usage);
|
| |
+ rpos = i;
|
| |
+ continue; /* don't mark B */
|
| |
+ case REV_CMD_PARENTS_ONLY:
|
| |
+ case REV_CMD_REF:
|
| |
+ case REV_CMD_REV:
|
| |
+ othercount++;
|
| |
+ continue;
|
| |
+ }
|
| |
+ if (map == NULL)
|
| |
+ map = bitmap_new();
|
| |
+ bitmap_set(map, i);
|
| |
+ }
|
| |
+
|
| |
+ /*
|
| |
+ * Forbid any additional revs for both A...B and A..B.
|
| |
+ */
|
| |
+ if (lpos >= 0 && othercount > 0)
|
| |
+ usage(builtin_diff_usage);
|
| |
+
|
| |
+ if (!is_symdiff) {
|
| |
+ bitmap_free(map);
|
| |
+ sym->warn = 0;
|
| |
+ sym->skip = NULL;
|
| |
+ return;
|
| |
+ }
|
| |
+
|
| |
+ sym->left = rev->pending.objects[lpos].name;
|
| |
+ sym->right = rev->pending.objects[rpos].name;
|
| |
+ sym->base = rev->pending.objects[basepos].name;
|
| |
+ if (basecount == 0)
|
| |
+ die(_("%s...%s: no merge base"), sym->left, sym->right);
|
| |
+ bitmap_unset(map, basepos); /* unmark the base we want */
|
| |
+ sym->warn = basecount > 1;
|
| |
+ sym->skip = map;
|
| |
+ }
|
| |
+
|
| |
int cmd_diff(int argc, const char **argv, const char *prefix)
|
| |
{
|
| |
int i;
|
| |
@@ -263,6 +372,7 @@
|
| |
struct object_array_entry *blob[2];
|
| |
int nongit = 0, no_index = 0;
|
| |
int result = 0;
|
| |
+ struct symdiff sdiff;
|
| |
|
| |
/*
|
| |
* We could get N tree-ish in the rev.pending_objects list.
|
| |
@@ -382,6 +492,7 @@
|
| |
}
|
| |
}
|
| |
|
| |
+ symdiff_prepare(&rev, &sdiff);
|
| |
for (i = 0; i < rev.pending.nr; i++) {
|
| |
struct object_array_entry *entry = &rev.pending.objects[i];
|
| |
struct object *obj = entry->item;
|
| |
@@ -396,6 +507,8 @@
|
| |
obj = &get_commit_tree(((struct commit *)obj))->object;
|
| |
|
| |
if (obj->type == OBJ_TREE) {
|
| |
+ if (sdiff.skip && bitmap_get(sdiff.skip, i))
|
| |
+ continue;
|
| |
obj->flags |= flags;
|
| |
add_object_array(obj, name, &ent);
|
| |
} else if (obj->type == OBJ_BLOB) {
|
| |
@@ -437,21 +550,12 @@
|
| |
usage(builtin_diff_usage);
|
| |
else if (ent.nr == 1)
|
| |
result = builtin_diff_index(&rev, argc, argv);
|
| |
- else if (ent.nr == 2)
|
| |
+ else if (ent.nr == 2) {
|
| |
+ if (sdiff.warn)
|
| |
+ warning(_("%s...%s: multiple merge bases, using %s"),
|
| |
+ sdiff.left, sdiff.right, sdiff.base);
|
| |
result = builtin_diff_tree(&rev, argc, argv,
|
| |
&ent.objects[0], &ent.objects[1]);
|
| |
- else if (ent.objects[0].item->flags & UNINTERESTING) {
|
| |
- /*
|
| |
- * diff A...B where there is at least one merge base
|
| |
- * between A and B. We have ent.objects[0] ==
|
| |
- * merge-base, ent.objects[ents-2] == A, and
|
| |
- * ent.objects[ents-1] == B. Show diff between the
|
| |
- * base and B. Note that we pick one merge base at
|
| |
- * random if there are more than one.
|
| |
- */
|
| |
- result = builtin_diff_tree(&rev, argc, argv,
|
| |
- &ent.objects[0],
|
| |
- &ent.objects[ent.nr-1]);
|
| |
} else
|
| |
result = builtin_diff_combined(&rev, argc, argv,
|
| |
ent.objects, ent.nr);
|
| |
"Chris Torek via GitGitGadget" gitgitgadget@gmail.com writes:
s/undocumented feature/undefined behaviour/;
The show.sh scripts wants to compute the diff against first parent,
and it uses a range notation HEAD^! which happens to mean
HEAD^..HEAD for a single parent commit, but it forgets that the
commit it may get fed could be a merge. What the code happens to do
when given "git diff ^HEAD^2 HEAD^..HEAD" is undefined behaviour and
does not even ...
...deserve to be explained in a paragraph like this, I would think.
Yes, the resulting code expresses the intent much better.