revision: do not peel tags used in range notation

A range notation "A..B" means exactly the same thing as what "^A B"
means, i.e. the set of commits that are reachable from B but not
from A.  But the internal representation after the revision parser
parsed these two notations are subtly different.

 - "rev-list ^A B" leaves A and B in the revs->pending.objects[]
   array, with the former marked as UNINTERESTING and the revision
   traversal machinery propagates the mark to underlying commit
   objects A^0 and B^0.

 - "rev-list A..B" peels tags and leaves A^0 (marked as
   UNINTERESTING) and B^0 in revs->pending.objects[] array before
   the traversal machinery kicks in.

This difference usually does not matter, but starts to matter when
the --objects option is used.  For example, we see this:

    $ git rev-list --objects v1.8.4^1..v1.8.4 | grep $(git rev-parse v1.8.4)
    $ git rev-list --objects v1.8.4 ^v1.8.4^1 | grep $(git rev-parse v1.8.4)
    04f013dc38 v1.8.4

With the former invocation, the revision traversal machinery never
hears about the tag v1.8.4 (it only sees the result of peeling it,
i.e. the commit v1.8.4^0), and the tag itself does not appear in the
output.  The latter does send the tag object itself to the output.

Make the range notation keep the unpeeled objects and feed them to
the traversal machinery to fix this inconsistency.

Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
Junio C Hamano 2013-09-19 14:20:34 -07:00
Родитель 15999998fb
Коммит 895c5ba3c1
2 изменённых файлов: 45 добавлений и 22 удалений

Просмотреть файл

@ -1157,41 +1157,56 @@ int handle_revision_arg(const char *arg_, struct rev_info *revs, int flags, unsi
}
if (!get_sha1_committish(this, from_sha1) &&
!get_sha1_committish(next, sha1)) {
struct commit *a, *b;
struct commit_list *exclude;
a = lookup_commit_reference(from_sha1);
b = lookup_commit_reference(sha1);
if (!a || !b) {
if (revs->ignore_missing)
return 0;
die(symmetric ?
"Invalid symmetric difference expression %s...%s" :
"Invalid revision range %s..%s",
arg, next);
}
struct object *a_obj, *b_obj;
if (!cant_be_filename) {
*dotdot = '.';
verify_non_filename(revs->prefix, arg);
}
if (symmetric) {
a_obj = parse_object(from_sha1);
b_obj = parse_object(sha1);
if (!a_obj || !b_obj) {
missing:
if (revs->ignore_missing)
return 0;
die(symmetric
? "Invalid symmetric difference expression %s"
: "Invalid revision range %s", arg);
}
if (!symmetric) {
/* just A..B */
a_flags = flags_exclude;
} else {
/* A...B -- find merge bases between the two */
struct commit *a, *b;
struct commit_list *exclude;
a = (a_obj->type == OBJ_COMMIT
? (struct commit *)a_obj
: lookup_commit_reference(a_obj->sha1));
b = (b_obj->type == OBJ_COMMIT
? (struct commit *)b_obj
: lookup_commit_reference(b_obj->sha1));
if (!a || !b)
goto missing;
exclude = get_merge_bases(a, b, 1);
add_pending_commit_list(revs, exclude,
flags_exclude);
free_commit_list(exclude);
a_flags = flags | SYMMETRIC_LEFT;
} else
a_flags = flags_exclude;
a->object.flags |= a_flags;
b->object.flags |= flags;
add_rev_cmdline(revs, &a->object, this,
}
a_obj->flags |= a_flags;
b_obj->flags |= flags;
add_rev_cmdline(revs, a_obj, this,
REV_CMD_LEFT, a_flags);
add_rev_cmdline(revs, &b->object, next,
add_rev_cmdline(revs, b_obj, next,
REV_CMD_RIGHT, flags);
add_pending_object(revs, &a->object, this);
add_pending_object(revs, &b->object, next);
add_pending_object(revs, a_obj, this);
add_pending_object(revs, b_obj, next);
return 0;
}
*dotdot = '.';

Просмотреть файл

@ -48,4 +48,12 @@ test_expect_success 'rev-list --objects with pathspecs and copied files' '
! grep one output
'
test_expect_success 'rev-list A..B and rev-list ^A B are the same' '
git commit --allow-empty -m another &&
git tag -a -m "annotated" v1.0 &&
git rev-list --objects ^v1.0^ v1.0 >expect &&
git rev-list --objects v1.0^..v1.0 >actual &&
test_cmp expect actual
'
test_done