Skip to content

Commit 80dbae0

Browse files
spearceJunio C Hamano
authored andcommitted
Chose better tag names in git-describe after merges.
Recently git.git itself encountered a situation on its master and next branches where git-describe stopped reporting 'v1.5.0-rc0-gN' and instead started reporting 'v1.4.4.4-gN'. This appeared to be a backward jump in version numbering. maint o-------------------4 \ \ master o-o-o-o-o-o-o-5-o-C-o-W The issue is that commit C in the diagram claims it is version 1.5.0, as the tag v1.5.0 is placed on commit 5. Yet commit W claims it is version 1.4.4.4 as the tag v1.5.0 has an older tag date than the v1.4.4.4 tag. As it turns out this situation is very common. A bug fix applied to maint and later merged into master occurs frequently enough that it should Just Work Right(tm). Rather than taking the first tag that gets found git-describe will now generate a list of all possible tags and select the one which has the most number of commits in common with HEAD (or whatever revision the user requested the description of). This rule is based on the principle shown in the diagram above. There are a large number of commits on the primary development branch 'master' which do not appear in the 'maint' branch, and many of these are already tagged as part of v1.5.0-rc0. Additionally these commits are not in v1.4.4.4, as they are part of the v1.5.0 release still being developed. The v1.5.0-rc0 tag is more descriptive of W than v1.4.4.4 is, and therefore should be used. Signed-off-by: Shawn O. Pearce <spearce@spearce.org> Signed-off-by: Junio C Hamano <junkio@cox.net>
1 parent e861ce1 commit 80dbae0

File tree

1 file changed

+67
-11
lines changed

1 file changed

+67
-11
lines changed

builtin-describe.c

Lines changed: 67 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@
22
#include "commit.h"
33
#include "tag.h"
44
#include "refs.h"
5+
#include "diff.h"
6+
#include "diffcore.h"
7+
#include "revision.h"
58
#include "builtin.h"
69

7-
#define SEEN (1u << 0)
8-
910
static const char describe_usage[] =
1011
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
1112

@@ -16,7 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
1617

1718
static int names, allocs;
1819
static struct commit_name {
19-
const struct commit *commit;
20+
struct commit *commit;
2021
int prio; /* annotated tag = 2, tag = 1, head = 0 */
2122
char path[FLEX_ARRAY]; /* more */
2223
} **name_array = NULL;
@@ -35,7 +36,7 @@ static struct commit_name *match(struct commit *cmit)
3536
}
3637

3738
static void add_to_known_names(const char *path,
38-
const struct commit *commit,
39+
struct commit *commit,
3940
int prio)
4041
{
4142
int idx;
@@ -98,13 +99,20 @@ static int compare_names(const void *_a, const void *_b)
9899
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
99100
}
100101

102+
struct possible_tag {
103+
struct possible_tag *next;
104+
struct commit_name *name;
105+
unsigned long depth;
106+
};
107+
101108
static void describe(const char *arg, int last_one)
102109
{
103110
unsigned char sha1[20];
104111
struct commit *cmit;
105112
struct commit_list *list;
106113
static int initialized = 0;
107114
struct commit_name *n;
115+
struct possible_tag *all_matches, *min_match, *cur_match;
108116

109117
if (get_sha1(arg, sha1))
110118
die("Not a valid object name %s", arg);
@@ -125,19 +133,67 @@ static void describe(const char *arg, int last_one)
125133
}
126134

127135
list = NULL;
136+
all_matches = NULL;
137+
cur_match = NULL;
128138
commit_list_insert(cmit, &list);
129139
while (list) {
130-
struct commit *c = pop_most_recent_commit(&list, SEEN);
140+
struct commit *c = pop_commit(&list);
131141
n = match(c);
132142
if (n) {
133-
printf("%s-g%s\n", n->path,
134-
find_unique_abbrev(cmit->object.sha1, abbrev));
135-
if (!last_one)
136-
clear_commit_marks(cmit, SEEN);
137-
return;
143+
struct possible_tag *p = xmalloc(sizeof(*p));
144+
p->name = n;
145+
p->next = NULL;
146+
if (cur_match)
147+
cur_match->next = p;
148+
else
149+
all_matches = p;
150+
cur_match = p;
151+
} else {
152+
struct commit_list *parents = c->parents;
153+
while (parents) {
154+
struct commit *p = parents->item;
155+
parse_commit(p);
156+
if (!(p->object.flags & SEEN)) {
157+
p->object.flags |= SEEN;
158+
insert_by_date(p, &list);
159+
}
160+
parents = parents->next;
161+
}
162+
}
163+
}
164+
165+
if (!all_matches)
166+
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
167+
168+
min_match = NULL;
169+
for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
170+
struct rev_info revs;
171+
struct commit *tagged = cur_match->name->commit;
172+
173+
clear_commit_marks(cmit, -1);
174+
init_revisions(&revs, NULL);
175+
tagged->object.flags |= UNINTERESTING;
176+
add_pending_object(&revs, &tagged->object, NULL);
177+
add_pending_object(&revs, &cmit->object, NULL);
178+
179+
prepare_revision_walk(&revs);
180+
cur_match->depth = 0;
181+
while ((!min_match || cur_match->depth < min_match->depth)
182+
&& get_revision(&revs))
183+
cur_match->depth++;
184+
if (!min_match || cur_match->depth < min_match->depth)
185+
min_match = cur_match;
186+
}
187+
printf("%s-g%s\n", min_match->name->path,
188+
find_unique_abbrev(cmit->object.sha1, abbrev));
189+
190+
if (!last_one) {
191+
for (cur_match = all_matches; cur_match; cur_match = min_match) {
192+
min_match = cur_match->next;
193+
free(cur_match);
138194
}
195+
clear_commit_marks(cmit, SEEN);
139196
}
140-
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
141197
}
142198

143199
int cmd_describe(int argc, const char **argv, const char *prefix)

0 commit comments

Comments
 (0)