summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--builtin-describe.c78
1 files changed, 67 insertions, 11 deletions
diff --git a/builtin-describe.c b/builtin-describe.c
index ad3b469f37..d65c7d286d 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -2,10 +2,11 @@
#include "commit.h"
#include "tag.h"
#include "refs.h"
+#include "diff.h"
+#include "diffcore.h"
+#include "revision.h"
#include "builtin.h"
-#define SEEN (1u << 0)
-
static const char describe_usage[] =
"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
@@ -16,7 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
static int names, allocs;
static struct commit_name {
- const struct commit *commit;
+ struct commit *commit;
int prio; /* annotated tag = 2, tag = 1, head = 0 */
char path[FLEX_ARRAY]; /* more */
} **name_array = NULL;
@@ -35,7 +36,7 @@ static struct commit_name *match(struct commit *cmit)
}
static void add_to_known_names(const char *path,
- const struct commit *commit,
+ struct commit *commit,
int prio)
{
int idx;
@@ -98,6 +99,12 @@ static int compare_names(const void *_a, const void *_b)
return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
}
+struct possible_tag {
+ struct possible_tag *next;
+ struct commit_name *name;
+ unsigned long depth;
+};
+
static void describe(const char *arg, int last_one)
{
unsigned char sha1[20];
@@ -105,6 +112,7 @@ static void describe(const char *arg, int last_one)
struct commit_list *list;
static int initialized = 0;
struct commit_name *n;
+ struct possible_tag *all_matches, *min_match, *cur_match;
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
@@ -125,19 +133,67 @@ static void describe(const char *arg, int last_one)
}
list = NULL;
+ all_matches = NULL;
+ cur_match = NULL;
commit_list_insert(cmit, &list);
while (list) {
- struct commit *c = pop_most_recent_commit(&list, SEEN);
+ struct commit *c = pop_commit(&list);
n = match(c);
if (n) {
- printf("%s-g%s\n", n->path,
- find_unique_abbrev(cmit->object.sha1, abbrev));
- if (!last_one)
- clear_commit_marks(cmit, SEEN);
- return;
+ struct possible_tag *p = xmalloc(sizeof(*p));
+ p->name = n;
+ p->next = NULL;
+ if (cur_match)
+ cur_match->next = p;
+ else
+ all_matches = p;
+ cur_match = p;
+ } else {
+ struct commit_list *parents = c->parents;
+ while (parents) {
+ struct commit *p = parents->item;
+ parse_commit(p);
+ if (!(p->object.flags & SEEN)) {
+ p->object.flags |= SEEN;
+ insert_by_date(p, &list);
+ }
+ parents = parents->next;
+ }
+ }
+ }
+
+ if (!all_matches)
+ die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+
+ min_match = NULL;
+ for (cur_match = all_matches; cur_match; cur_match = cur_match->next) {
+ struct rev_info revs;
+ struct commit *tagged = cur_match->name->commit;
+
+ clear_commit_marks(cmit, -1);
+ init_revisions(&revs, NULL);
+ tagged->object.flags |= UNINTERESTING;
+ add_pending_object(&revs, &tagged->object, NULL);
+ add_pending_object(&revs, &cmit->object, NULL);
+
+ prepare_revision_walk(&revs);
+ cur_match->depth = 0;
+ while ((!min_match || cur_match->depth < min_match->depth)
+ && get_revision(&revs))
+ cur_match->depth++;
+ if (!min_match || cur_match->depth < min_match->depth)
+ min_match = cur_match;
+ }
+ printf("%s-g%s\n", min_match->name->path,
+ find_unique_abbrev(cmit->object.sha1, abbrev));
+
+ if (!last_one) {
+ for (cur_match = all_matches; cur_match; cur_match = min_match) {
+ min_match = cur_match->next;
+ free(cur_match);
}
+ clear_commit_marks(cmit, SEEN);
}
- die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
}
int cmd_describe(int argc, const char **argv, const char *prefix)