Skip to content

Commit

Permalink
Add '...' operator for revisions
Browse files Browse the repository at this point in the history
'A...B' is a shortcut for 'A B --not $(git-merge-base --all A B)'.
This XOR-like operation is called symmetric difference in set
theory.

The symbol '...' has been chosen because it's rather similar to the
existing '..' operator and the somewhat more natural caret ('^') is
already taken.

Signed-off-by: Rene Scharfe <rene.scharfe@lsrfire.ath.cx>
Signed-off-by: Junio C Hamano <junkio@cox.net>
  • Loading branch information
Rene Scharfe authored and Junio C Hamano committed Jul 2, 2006
1 parent 31609c1 commit 0d2c9d6
Show file tree
Hide file tree
Showing 2 changed files with 53 additions and 9 deletions.
14 changes: 14 additions & 0 deletions Documentation/git-rev-list.txt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ SYNOPSIS
[ \--sparse ]
[ \--no-merges ]
[ \--remove-empty ]
[ \--not ]
[ \--all ]
[ \--topo-order ]
[ \--parents ]
Expand All @@ -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
-------
Expand Down Expand Up @@ -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>.
Expand Down
48 changes: 39 additions & 9 deletions revision.c
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,18 @@ void init_revisions(struct rev_info *revs)
diff_setup(&revs->diffopt);
}

static void add_pending_commit_list(struct rev_info *revs,
struct commit_list *commit_list,
unsigned int flags)
{
while (commit_list) {
struct object *object = &commit_list->item->object;
object->flags |= flags;
add_pending_object(revs, object, sha1_to_hex(object->sha1));
commit_list = commit_list->next;
}
}

/*
* Parse revision information, filling in the "rev_info" structure,
* and removing the used arguments from the argument list.
Expand Down Expand Up @@ -771,27 +783,45 @@ int setup_revisions(int argc, const char **argv, struct rev_info *revs, const ch
unsigned char from_sha1[20];
const char *next = dotdot + 2;
const char *this = arg;
int symmetric = *next == '.';
unsigned int flags_exclude = flags ^ UNINTERESTING;

*dotdot = 0;
next += symmetric;

if (!*next)
next = "HEAD";
if (dotdot == arg)
this = "HEAD";
if (!get_sha1(this, from_sha1) &&
!get_sha1(next, sha1)) {
struct object *exclude;
struct object *include;

exclude = get_reference(revs, this, from_sha1, flags ^ UNINTERESTING);
include = get_reference(revs, next, sha1, flags);
if (!exclude || !include)
die("Invalid revision range %s..%s", arg, next);
struct commit *a, *b;
struct commit_list *exclude;

a = lookup_commit_reference(from_sha1);
b = lookup_commit_reference(sha1);
if (!a || !b) {
die(symmetric ?
"Invalid symmetric difference expression %s...%s" :
"Invalid revision range %s..%s",
arg, next);
}

if (!seen_dashdash) {
*dotdot = '.';
verify_non_filename(revs->prefix, arg);
}
add_pending_object(revs, exclude, this);
add_pending_object(revs, include, next);

if (symmetric) {
exclude = get_merge_bases_clean(a, b);
add_pending_commit_list(revs, exclude,
flags_exclude);
a->object.flags |= flags;
} else
a->object.flags |= flags_exclude;
b->object.flags |= flags;
add_pending_object(revs, &a->object, this);
add_pending_object(revs, &b->object, next);
continue;
}
*dotdot = '.';
Expand Down

0 comments on commit 0d2c9d6

Please sign in to comment.