From: "René Scharfe" <rene.scharfe@lsrfire.ath.cx>
To: Junio C Hamano <gitster@pobox.com>
Cc: Git Mailing List <git@vger.kernel.org>,
Michael Gernoth <simigern@cip.informatik.uni-erlangen.de>,
Thomas Glanzmann <thomas@glanzmann.de>
Subject: Re: [PATCH 2/3] archive: specfile support (--pretty=format: in archive files)
Date: Wed, 05 Sep 2007 01:13:55 +0200 [thread overview]
Message-ID: <46DDE6B3.8010507@lsrfire.ath.cx> (raw)
In-Reply-To: <7vtzqb8fw2.fsf@gitster.siamese.dyndns.org>
Junio C Hamano schrieb:
>> Why did it take me that long to come up with such a simple patch?
>> There was a vacation and a feature freeze in between, but above all
>> I was only recently able to convince myself (using ugly code) that
>> format_commit_message() can indeed be made to expand placeholders
>> to git-describe strings..
>
> Thanks. Will take a look.
Here's that ugly code, by the way. It adds two placeholders, %d for
description and %D for description depth. Shortcomings of this code:
it adds three members to struct commit, it unconditionally computes
the description when format_commit_message() -- even if the format
string doesn't contain %d and %D, the patch is not nicely split up.
But it convinced me that describe *can* indeed be librarified. :-)
René
Makefile | 1 +
builtin-describe.c | 219 +++------------------------------------------------
cache.h | 5 +
commit.c | 31 ++++++++
commit.h | 5 +
describe.c | 170 ++++++++++++++++++++++++++++++++++++++++
6 files changed, 225 insertions(+), 206 deletions(-)
diff --git a/Makefile b/Makefile
index 51af531..7ec95f8 100644
--- a/Makefile
+++ b/Makefile
@@ -296,6 +296,7 @@ DIFF_OBJS = \
LIB_OBJS = \
blob.o commit.o connect.o csum-file.o cache-tree.o base85.o \
date.o diff-delta.o entry.o exec_cmd.o ident.o \
+ describe.o \
interpolate.o \
lockfile.o \
patch-ids.o \
diff --git a/builtin-describe.c b/builtin-describe.c
index 669110c..50ec08f 100644
--- a/builtin-describe.c
+++ b/builtin-describe.c
@@ -17,124 +17,13 @@ static int tags; /* But allow any tags if --tags is specified */
static int abbrev = DEFAULT_ABBREV;
static int max_candidates = 10;
-struct commit_name {
- int prio; /* annotated tag = 2, tag = 1, head = 0 */
- char path[FLEX_ARRAY]; /* more */
-};
-static const char *prio_names[] = {
- "head", "lightweight", "annotated",
-};
-
-static void add_to_known_names(const char *path,
- struct commit *commit,
- int prio)
-{
- struct commit_name *e = commit->util;
- if (!e || e->prio < prio) {
- size_t len = strlen(path)+1;
- free(e);
- e = xmalloc(sizeof(struct commit_name) + len);
- e->prio = prio;
- memcpy(e->path, path, len);
- commit->util = e;
- }
-}
-
-static int get_name(const char *path, const unsigned char *sha1, int flag, void *cb_data)
-{
- struct commit *commit = lookup_commit_reference_gently(sha1, 1);
- struct object *object;
- int prio;
-
- if (!commit)
- return 0;
- object = parse_object(sha1);
- /* If --all, then any refs are used.
- * If --tags, then any tags are used.
- * Otherwise only annotated tags are used.
- */
- if (!prefixcmp(path, "refs/tags/")) {
- if (object->type == OBJ_TAG)
- prio = 2;
- else
- prio = 1;
- }
- else
- prio = 0;
-
- if (!all) {
- if (!prio)
- return 0;
- if (!tags && prio < 2)
- return 0;
- }
- add_to_known_names(all ? path + 5 : path + 10, commit, prio);
- return 0;
-}
-
-struct possible_tag {
- struct commit_name *name;
- int depth;
- int found_order;
- unsigned flag_within;
-};
-
-static int compare_pt(const void *a_, const void *b_)
-{
- struct possible_tag *a = (struct possible_tag *)a_;
- struct possible_tag *b = (struct possible_tag *)b_;
- if (a->name->prio != b->name->prio)
- return b->name->prio - a->name->prio;
- if (a->depth != b->depth)
- return a->depth - b->depth;
- if (a->found_order != b->found_order)
- return a->found_order - b->found_order;
- return 0;
-}
-
-static unsigned long finish_depth_computation(
- struct commit_list **list,
- struct possible_tag *best)
-{
- unsigned long seen_commits = 0;
- while (*list) {
- struct commit *c = pop_commit(list);
- struct commit_list *parents = c->parents;
- seen_commits++;
- if (c->object.flags & best->flag_within) {
- struct commit_list *a = *list;
- while (a) {
- struct commit *i = a->item;
- if (!(i->object.flags & best->flag_within))
- break;
- a = a->next;
- }
- if (!a)
- break;
- } else
- best->depth++;
- while (parents) {
- struct commit *p = parents->item;
- parse_commit(p);
- if (!(p->object.flags & SEEN))
- insert_by_date(p, list);
- p->object.flags |= c->object.flags;
- parents = parents->next;
- }
- }
- return seen_commits;
-}
-
static void describe(const char *arg, int last_one)
{
unsigned char sha1[20];
- struct commit *cmit, *gave_up_on = NULL;
- struct commit_list *list;
+ struct commit *cmit;
static int initialized = 0;
- struct commit_name *n;
- struct possible_tag all_matches[MAX_TAGS];
- unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
- unsigned long seen_commits = 0;
+ char *name;
+ int depth = 0;
if (get_sha1(arg, sha1))
die("Not a valid object name %s", arg);
@@ -144,100 +33,23 @@ static void describe(const char *arg, int last_one)
if (!initialized) {
initialized = 1;
- for_each_ref(get_name, NULL);
- }
-
- n = cmit->util;
- if (n) {
- printf("%s\n", n->path);
- return;
+ load_commit_names(all ? 0 : (tags ? 1 : 2));
}
- if (debug)
- fprintf(stderr, "searching to describe %s\n", arg);
-
- list = NULL;
- cmit->object.flags = SEEN;
- commit_list_insert(cmit, &list);
- while (list) {
- struct commit *c = pop_commit(&list);
- struct commit_list *parents = c->parents;
- seen_commits++;
- n = c->util;
- if (n) {
- if (match_cnt < max_candidates) {
- struct possible_tag *t = &all_matches[match_cnt++];
- t->name = n;
- t->depth = seen_commits - 1;
- t->flag_within = 1u << match_cnt;
- t->found_order = match_cnt;
- c->object.flags |= t->flag_within;
- if (n->prio == 2)
- annotated_cnt++;
- }
- else {
- gave_up_on = c;
- break;
- }
- }
- for (cur_match = 0; cur_match < match_cnt; cur_match++) {
- struct possible_tag *t = &all_matches[cur_match];
- if (!(c->object.flags & t->flag_within))
- t->depth++;
- }
- if (annotated_cnt && !list) {
- if (debug)
- fprintf(stderr, "finished search at %s\n",
- sha1_to_hex(c->object.sha1));
- break;
- }
- while (parents) {
- struct commit *p = parents->item;
- parse_commit(p);
- if (!(p->object.flags & SEEN))
- insert_by_date(p, &list);
- p->object.flags |= c->object.flags;
- parents = parents->next;
- }
- }
-
- if (!match_cnt)
+ name = describe_commit(cmit, max_candidates, &depth);
+ if (!name)
die("cannot describe '%s'", sha1_to_hex(cmit->object.sha1));
+ if (!all)
+ name += 5;
- qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
-
- if (gave_up_on) {
- insert_by_date(gave_up_on, &list);
- seen_commits--;
- }
- seen_commits += finish_depth_computation(&list, &all_matches[0]);
- free_commit_list(list);
-
- if (debug) {
- for (cur_match = 0; cur_match < match_cnt; cur_match++) {
- struct possible_tag *t = &all_matches[cur_match];
- fprintf(stderr, " %-11s %8d %s\n",
- prio_names[t->name->prio],
- t->depth, t->name->path);
- }
- fprintf(stderr, "traversed %lu commits\n", seen_commits);
- if (gave_up_on) {
- fprintf(stderr,
- "more than %i tags found; listed %i most recent\n"
- "gave up search at %s\n",
- max_candidates, max_candidates,
- sha1_to_hex(gave_up_on->object.sha1));
- }
- }
- if (abbrev == 0)
- printf("%s\n", all_matches[0].name->path );
+ if (abbrev == 0 || depth == 0)
+ printf("%s\n", name);
else
- printf("%s-%d-g%s\n", all_matches[0].name->path,
- all_matches[0].depth,
+ printf("%s-%d-g%s\n", name, depth,
find_unique_abbrev(cmit->object.sha1, abbrev));
if (!last_one)
- clear_commit_marks(cmit, -1);
+ clear_commit_name_flags(cmit);
}
int cmd_describe(int argc, const char **argv, const char *prefix)
@@ -263,13 +75,8 @@ int cmd_describe(int argc, const char **argv, const char *prefix)
if (abbrev != 0 && (abbrev < MINIMUM_ABBREV || 40 < abbrev))
abbrev = DEFAULT_ABBREV;
}
- else if (!prefixcmp(arg, "--candidates=")) {
+ else if (!prefixcmp(arg, "--candidates="))
max_candidates = strtoul(arg + 13, NULL, 10);
- if (max_candidates < 1)
- max_candidates = 1;
- else if (max_candidates > MAX_TAGS)
- max_candidates = MAX_TAGS;
- }
else
usage(describe_usage);
}
diff --git a/cache.h b/cache.h
index 70abbd5..07ee149 100644
--- a/cache.h
+++ b/cache.h
@@ -600,4 +600,9 @@ extern int diff_auto_refresh_index;
/* match-trees.c */
void shift_tree(const unsigned char *, const unsigned char *, unsigned char *, int);
+/* describe.c */
+struct commit;
+extern void load_commit_names(int min_prio);
+extern char *describe_commit(struct commit *cmit, int max_candidates, int *depthp);
+
#endif /* CACHE_H */
diff --git a/commit.c b/commit.c
index dc5a064..d2343f2 100644
--- a/commit.c
+++ b/commit.c
@@ -455,6 +455,19 @@ void clear_commit_marks(struct commit *commit, unsigned int mark)
}
}
+void clear_commit_name_flags(struct commit *commit)
+{
+ struct commit_list *parents;
+
+ commit->name_flags = 0;
+
+ for (parents = commit->parents; parents; parents = parents->next) {
+ /* Have we already cleared this? */
+ if (parents->item->name_flags)
+ clear_commit_name_flags(parents->item);
+ }
+}
+
/*
* Generic support for pretty-printing the header
*/
@@ -819,6 +832,8 @@ static long format_commit_message(const struct commit *commit,
{ "%Cblue" }, /* blue */
{ "%Creset" }, /* reset color */
{ "%n" }, /* newline */
+ { "%d" }, /* description */
+ { "%D" }, /* description depth */
{ "%m" }, /* left/right/bottom */
};
enum interp_index {
@@ -837,6 +852,7 @@ static long format_commit_message(const struct commit *commit,
IBODY,
IRED, IGREEN, IBLUE, IRESET_COLOR,
INEWLINE,
+ IDESC, IDESC_DEPTH,
ILEFT_RIGHT,
};
struct commit_list *p;
@@ -920,6 +936,21 @@ static long format_commit_message(const struct commit *commit,
if (!table[i].value)
interp_set_entry(table, i, "<unknown>");
+ {
+ struct commit *cmit = (struct commit *)commit;
+ char *name;
+ char tmp[20];
+ int depth;
+ load_commit_names(2);
+ name = describe_commit(cmit, 10, &depth);
+ if (!name)
+ name = "";
+ sprintf(tmp, "%d", depth);
+ interp_set_entry(table, IDESC, name);
+ interp_set_entry(table, IDESC_DEPTH, tmp);
+ clear_commit_name_flags(cmit);
+ }
+
do {
char *buf = *buf_p;
unsigned long space = *space_p;
diff --git a/commit.h b/commit.h
index 467872e..69b6d67 100644
--- a/commit.h
+++ b/commit.h
@@ -17,6 +17,9 @@ struct commit {
struct commit_list *parents;
struct tree *tree;
char *buffer;
+ char *name;
+ unsigned int name_flags;
+ char name_prio;
};
extern int save_commit_buffer;
@@ -72,6 +75,8 @@ struct commit *pop_most_recent_commit(struct commit_list **list,
struct commit *pop_commit(struct commit_list **stack);
void clear_commit_marks(struct commit *commit, unsigned int mark);
+void clear_commit_name_flags(struct commit *commit);
+
/*
* Performs an in-place topological sort of list supplied.
diff --git a/describe.c b/describe.c
new file mode 100644
index 0000000..acd4517
--- /dev/null
+++ b/describe.c
@@ -0,0 +1,170 @@
+#include "cache.h"
+#include "commit.h"
+#include "tag.h"
+#include "refs.h"
+
+#define SEEN (1u<<0)
+#define MAX_TAGS (FLAG_BITS - 1)
+
+struct possible_tag {
+ char *name;
+ int name_prio;
+ int depth;
+ int found_order;
+ unsigned flag_within;
+};
+
+static int get_name(const char *path, const unsigned char *sha1, int flag,
+ void *cb_data)
+{
+ struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+ if (commit) {
+ struct object *object = parse_object(sha1);
+ int min_prio = *((int *)cb_data);
+ int prio = 0;
+
+ if (!prefixcmp(path, "refs/tags/")) {
+ if (object->type == OBJ_TAG)
+ prio = 2;
+ else
+ prio = 1;
+ }
+
+ if (prio >= min_prio &&
+ (!commit->name || commit->name_prio < prio)) {
+ free(commit->name);
+ commit->name = xstrdup(path + 5);
+ commit->name_prio = prio;
+ }
+ }
+ return 0;
+}
+
+void load_commit_names(int min_prio)
+{
+ for_each_ref(get_name, &min_prio);
+}
+
+static int compare_pt(const void *a_, const void *b_)
+{
+ struct possible_tag *a = (struct possible_tag *)a_;
+ struct possible_tag *b = (struct possible_tag *)b_;
+ if (a->name_prio != b->name_prio)
+ return b->name_prio - a->name_prio;
+ if (a->depth != b->depth)
+ return a->depth - b->depth;
+ if (a->found_order != b->found_order)
+ return a->found_order - b->found_order;
+ return 0;
+}
+
+static unsigned long finish_depth_computation(
+ struct commit_list **list,
+ struct possible_tag *best)
+{
+ unsigned long seen_commits = 0;
+ while (*list) {
+ struct commit *c = pop_commit(list);
+ struct commit_list *parents = c->parents;
+ seen_commits++;
+ if (c->name_flags & best->flag_within) {
+ struct commit_list *a = *list;
+ while (a) {
+ struct commit *i = a->item;
+ if (!(i->name_flags & best->flag_within))
+ break;
+ a = a->next;
+ }
+ if (!a)
+ break;
+ } else
+ best->depth++;
+ while (parents) {
+ struct commit *p = parents->item;
+ parse_commit(p);
+ if (!(p->name_flags & SEEN))
+ insert_by_date(p, list);
+ p->name_flags |= c->name_flags;
+ parents = parents->next;
+ }
+ }
+ return seen_commits;
+}
+
+char *describe_commit(struct commit *cmit, int max_candidates, int *depthp)
+{
+ struct commit *gave_up_on = NULL;
+ struct commit_list *list;
+ struct possible_tag all_matches[MAX_TAGS];
+ unsigned int match_cnt = 0, annotated_cnt = 0, cur_match;
+ unsigned long seen_commits = 0;
+
+ if (cmit->name) {
+ *depthp = 0;
+ return cmit->name;
+ }
+
+ if (max_candidates < 1)
+ max_candidates = 1;
+ else if (max_candidates > MAX_TAGS)
+ max_candidates = MAX_TAGS;
+
+ list = NULL;
+ cmit->name_flags = SEEN;
+ commit_list_insert(cmit, &list);
+ while (list) {
+ struct commit *c = pop_commit(&list);
+ struct commit_list *parents = c->parents;
+ seen_commits++;
+ if (c->name) {
+ if (match_cnt < max_candidates) {
+ struct possible_tag *t = &all_matches[match_cnt++];
+ t->name = c->name;
+ t->name_prio = c->name_prio;
+ t->depth = seen_commits - 1;
+ t->flag_within = 1u << match_cnt;
+ t->found_order = match_cnt;
+ c->name_flags |= t->flag_within;
+ if (c->name_prio == 2)
+ annotated_cnt++;
+ }
+ else {
+ gave_up_on = c;
+ break;
+ }
+ }
+ for (cur_match = 0; cur_match < match_cnt; cur_match++) {
+ struct possible_tag *t = &all_matches[cur_match];
+ if (!(c->name_flags & t->flag_within))
+ t->depth++;
+ }
+ if (annotated_cnt && !list)
+ break;
+ while (parents) {
+ struct commit *p = parents->item;
+ parse_commit(p);
+ if (!(p->name_flags & SEEN))
+ insert_by_date(p, &list);
+ p->name_flags |= c->name_flags;
+ parents = parents->next;
+ }
+ }
+
+ if (!match_cnt) {
+ free_commit_list(list);
+ *depthp = -1;
+ return NULL;
+ }
+
+ qsort(all_matches, match_cnt, sizeof(all_matches[0]), compare_pt);
+
+ if (gave_up_on) {
+ insert_by_date(gave_up_on, &list);
+ seen_commits--;
+ }
+ seen_commits += finish_depth_computation(&list, &all_matches[0]);
+ free_commit_list(list);
+
+ *depthp = all_matches[0].depth;
+ return all_matches[0].name;
+}
next prev parent reply other threads:[~2007-09-04 23:14 UTC|newest]
Thread overview: 25+ messages / expand[flat|nested] mbox.gz Atom feed top
2007-09-03 18:07 [PATCH 2/3] archive: specfile support (--pretty=format: in archive files) René Scharfe
2007-09-03 18:40 ` Johannes Schindelin
2007-09-03 20:19 ` David Kastrup
2007-09-04 23:13 ` René Scharfe
2007-09-03 23:53 ` Junio C Hamano
2007-09-04 5:45 ` Andreas Ericsson
2007-09-04 10:41 ` Johannes Schindelin
2007-09-04 23:13 ` René Scharfe
2007-09-05 0:12 ` Johannes Schindelin
2007-09-05 0:23 ` Junio C Hamano
2007-09-06 16:20 ` [PATCH 4/3] archive: specfile syntax change: "$Format:%PLCHLDR$" instead of just "%PLCHLDR" René Scharfe
2007-09-06 17:11 ` Johannes Schindelin
2007-09-06 20:35 ` René Scharfe
2007-09-06 20:53 ` René Scharfe
2007-09-06 23:17 ` Junio C Hamano
2007-09-07 10:44 ` Johannes Schindelin
2007-09-06 22:32 ` [PATCH 3.5/3] add memmem() René Scharfe
2007-09-06 22:34 ` [PATCH 4/3] archive: specfile syntax change: "$Format:%PLCHLDR$" instead of just "%PLCHLDR" (take 2) René Scharfe
2007-09-06 16:51 ` [PATCH 5/3] archive: rename attribute specfile to export-subst René Scharfe
2007-09-06 17:13 ` Johannes Schindelin
2007-09-06 20:38 ` René Scharfe
2007-09-06 21:03 ` Junio C Hamano
2007-09-07 10:45 ` Johannes Schindelin
2007-09-04 23:13 ` René Scharfe [this message]
2007-09-05 0:19 ` [PATCH 2/3] archive: specfile support (--pretty=format: in archive files) Junio C Hamano
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: http://vger.kernel.org/majordomo-info.html
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=46DDE6B3.8010507@lsrfire.ath.cx \
--to=rene.scharfe@lsrfire.ath.cx \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
--cc=simigern@cip.informatik.uni-erlangen.de \
--cc=thomas@glanzmann.de \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://80x24.org/mirrors/git.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).