On Fri, Jun 30, 2006 at 08:54:33PM -0700, Linus Torvalds wrote:
Now, the expression
A...B == B...A == A B --not $(git-merge-base --all A B)
is meaningful (and the one I want for merges), but it's largely useless
for anything else. It just means "the set of all commits that aren't
trivially in both" (it's not strictly a valid set operation, but it
approaches being an "xor" instead of a union or an intersection or a
difference).
You mean something like the following patch on top of the 'next' branch?
It also documents the --not switch because I needed it for the example.
TODO: There are still a few undocumented options left and setup_revisions()
is fat and ugly. Any volunteers? I'd clean it up if I only could
write comprehensible documentation and wasn't that lazy..
Signed-off-by: Rene Scharfe <redacted>
diff --git a/Documentation/git-rev-list.txt b/Documentation/git-rev-list.txt
index ad6d14c..ffbf0c9 100644
--- a/Documentation/git-rev-list.txt
+++ b/Documentation/git-rev-list.txt
@@ -15,6 +15,7 @@ SYNOPSIS
[ \--sparse ]
[ \--no-merges ]
[ \--remove-empty ]
+ [ \--not ]
[ \--all ]
[ \--topo-order ]
[ \--parents ]
@@ -37,6 +38,14 @@ not in 'baz'".
A special notation <commit1>..<commit2> can be used as a
short-hand for {caret}<commit1> <commit2>.
+Another special notation is <commit1>...<commit2> which is useful for
+merges. The resulting set of commits is the symmetric difference
+between the two operands. The following two commands are equivalent:
+
+------------
+$ git-rev-list A B --not $(git-merge-base --all A B)
+$ git-rev-list A...B
+------------
OPTIONS
-------@@ -93,6 +102,11 @@ OPTIONS
--remove-empty::
Stop when a given path disappears from the tree.
+--not::
+ Reverses the meaning of the '{caret}' prefix (or lack
+ thereof) for all following revision specifiers, up to
+ the next `--not`.
+
--all::
Pretend as if all the refs in `$GIT_DIR/refs/` are
listed on the command line as <commit>.diff --git a/revision.c b/revision.c
index ae4ca82..d4224a1 100644
--- a/revision.c
+++ b/revision.c
@@ -766,6 +766,47 @@ int setup_revisions(int argc, const char
left++;
continue;
}
+ dotdot = strstr(arg, "...");
+ if (dotdot) {
+ unsigned char other_sha1[20];
+ const char *one = arg;
+ const char *two = dotdot + 3;
+ *dotdot = '\0';
+ if (dotdot == arg)
+ one = "HEAD";
+ if (!*two)
+ two = "HEAD";
+ if (!get_sha1(one, sha1) &&
+ !get_sha1(two, other_sha1)) {
+ struct commit *a, *b;
+ struct commit_list *exclude;
+
+ a = lookup_commit_reference(sha1);
+ b = lookup_commit_reference(other_sha1);
+ if (!a || !b)
+ die("Invalid symmetric difference expression %s...%s", arg, two);
+
+ if (!seen_dashdash) {
+ *dotdot = '.';
+ verify_non_filename(revs->prefix, arg);
+
+ }
+ exclude = get_merge_bases(a, b);
+ while (exclude) {
+ struct object *object =
+ &exclude->item->object;
+ object->flags |= flags ^ UNINTERESTING;
+ add_pending_object(revs, object, sha1_to_hex(object->sha1));
+ exclude = exclude->next;
+ }
+ a->object.flags |= flags;
+ add_pending_object(revs, &a->object, one);
+ b->object.flags |= flags;
+ add_pending_object(revs, &b->object, two);
+ continue;
+ }
+ *dotdot = '.';
+ }
dotdot = strstr(arg, "..");
if (dotdot) {
unsigned char from_sha1[20];