describe: Break annotated tag ties by tagger date
If more than one annotated tag points at the same commit, use the tag whose tagger field has a more recent date stamp. This resolves non-deterministic cases where the maintainer has done: $ git tag -a -m "2.1-rc1" v2.1-rc1 deadbeef $ git tag -a -m "2.1" v2.1 deadbeef If the tag is an older-style annotated tag with no tagger date, we assume a date stamp at the UNIX epoch. This will cause us to prefer an annotated tag that has a valid date. We could also try to consider the tag object chain, favoring a tag that "includes" another one: $ git tag -a -m "2.1-rc0" v2.1-rc1 deadbeef $ git tag -a -m "2.1" v2.1 v2.1-rc1 However traversing the tag's object chain looking for inclusion is much more complicated. Its already very likely that even in these cases the v2.1 tag will have a more recent tagger date than v2.1-rc1, so with this change describe should still resolve this by selecting the more recent v2.1. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
e451d06bf3
commit
03e8b541b3
@ -35,7 +35,8 @@ static const char *diff_index_args[] = {
|
|||||||
|
|
||||||
struct commit_name {
|
struct commit_name {
|
||||||
struct tag *tag;
|
struct tag *tag;
|
||||||
int prio; /* annotated tag = 2, tag = 1, head = 0 */
|
unsigned prio:2; /* annotated tag = 2, tag = 1, head = 0 */
|
||||||
|
unsigned name_checked:1;
|
||||||
unsigned char sha1[20];
|
unsigned char sha1[20];
|
||||||
char path[FLEX_ARRAY]; /* more */
|
char path[FLEX_ARRAY]; /* more */
|
||||||
};
|
};
|
||||||
@ -43,18 +44,53 @@ static const char *prio_names[] = {
|
|||||||
"head", "lightweight", "annotated",
|
"head", "lightweight", "annotated",
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static int replace_name(struct commit_name *e,
|
||||||
|
int prio,
|
||||||
|
const unsigned char *sha1,
|
||||||
|
struct tag **tag)
|
||||||
|
{
|
||||||
|
if (!e || e->prio < prio)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (e->prio == 2 && prio == 2) {
|
||||||
|
/* Multiple annotated tags point to the same commit.
|
||||||
|
* Select one to keep based upon their tagger date.
|
||||||
|
*/
|
||||||
|
struct tag *t;
|
||||||
|
|
||||||
|
if (!e->tag) {
|
||||||
|
t = lookup_tag(e->sha1);
|
||||||
|
if (!t || parse_tag(t))
|
||||||
|
return 1;
|
||||||
|
e->tag = t;
|
||||||
|
}
|
||||||
|
|
||||||
|
t = lookup_tag(sha1);
|
||||||
|
if (!t || parse_tag(t))
|
||||||
|
return 0;
|
||||||
|
*tag = t;
|
||||||
|
|
||||||
|
if (e->tag->date < t->date)
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
static void add_to_known_names(const char *path,
|
static void add_to_known_names(const char *path,
|
||||||
struct commit *commit,
|
struct commit *commit,
|
||||||
int prio,
|
int prio,
|
||||||
const unsigned char *sha1)
|
const unsigned char *sha1)
|
||||||
{
|
{
|
||||||
struct commit_name *e = commit->util;
|
struct commit_name *e = commit->util;
|
||||||
if (!e || e->prio < prio) {
|
struct tag *tag = NULL;
|
||||||
|
if (replace_name(e, prio, sha1, &tag)) {
|
||||||
size_t len = strlen(path)+1;
|
size_t len = strlen(path)+1;
|
||||||
free(e);
|
free(e);
|
||||||
e = xmalloc(sizeof(struct commit_name) + len);
|
e = xmalloc(sizeof(struct commit_name) + len);
|
||||||
e->tag = NULL;
|
e->tag = tag;
|
||||||
e->prio = prio;
|
e->prio = prio;
|
||||||
|
e->name_checked = 0;
|
||||||
hashcpy(e->sha1, sha1);
|
hashcpy(e->sha1, sha1);
|
||||||
memcpy(e->path, path, len);
|
memcpy(e->path, path, len);
|
||||||
commit->util = e;
|
commit->util = e;
|
||||||
@ -165,10 +201,15 @@ static void display_name(struct commit_name *n)
|
|||||||
{
|
{
|
||||||
if (n->prio == 2 && !n->tag) {
|
if (n->prio == 2 && !n->tag) {
|
||||||
n->tag = lookup_tag(n->sha1);
|
n->tag = lookup_tag(n->sha1);
|
||||||
if (!n->tag || parse_tag(n->tag) || !n->tag->tag)
|
if (!n->tag || parse_tag(n->tag))
|
||||||
die("annotated tag %s not available", n->path);
|
die("annotated tag %s not available", n->path);
|
||||||
|
}
|
||||||
|
if (n->tag && !n->name_checked) {
|
||||||
|
if (!n->tag->tag)
|
||||||
|
die("annotated tag %s has no embedded name", n->path);
|
||||||
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
|
if (strcmp(n->tag->tag, all ? n->path + 5 : n->path))
|
||||||
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
|
warning("tag '%s' is really '%s' here", n->tag->tag, n->path);
|
||||||
|
n->name_checked = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (n->tag)
|
if (n->tag)
|
||||||
|
@ -8,7 +8,7 @@ test_description='test describe
|
|||||||
o----o----o----o----o----. /
|
o----o----o----o----o----. /
|
||||||
\ A c /
|
\ A c /
|
||||||
.------------o---o---o
|
.------------o---o---o
|
||||||
D e
|
D,R e
|
||||||
'
|
'
|
||||||
. ./test-lib.sh
|
. ./test-lib.sh
|
||||||
|
|
||||||
@ -68,6 +68,8 @@ test_expect_success setup '
|
|||||||
echo D >another && git add another && git commit -m D &&
|
echo D >another && git add another && git commit -m D &&
|
||||||
test_tick &&
|
test_tick &&
|
||||||
git tag -a -m D D &&
|
git tag -a -m D D &&
|
||||||
|
test_tick &&
|
||||||
|
git tag -a -m R R &&
|
||||||
|
|
||||||
test_tick &&
|
test_tick &&
|
||||||
echo DD >another && git commit -a -m another &&
|
echo DD >another && git commit -a -m another &&
|
||||||
@ -89,10 +91,10 @@ test_expect_success setup '
|
|||||||
|
|
||||||
check_describe A-* HEAD
|
check_describe A-* HEAD
|
||||||
check_describe A-* HEAD^
|
check_describe A-* HEAD^
|
||||||
check_describe D-* HEAD^^
|
check_describe R-* HEAD^^
|
||||||
check_describe A-* HEAD^^2
|
check_describe A-* HEAD^^2
|
||||||
check_describe B HEAD^^2^
|
check_describe B HEAD^^2^
|
||||||
check_describe D-* HEAD^^^
|
check_describe R-* HEAD^^^
|
||||||
|
|
||||||
check_describe c-* --tags HEAD
|
check_describe c-* --tags HEAD
|
||||||
check_describe c-* --tags HEAD^
|
check_describe c-* --tags HEAD^
|
||||||
|
Loading…
x
Reference in New Issue
Block a user