threads / discuss / 2927

Add a "git-describe" command

Subject: Add a "git-describe" command

## tl;dr

14 messages between Dec 24, 2005 and Dec 28, 2005.

replies: 13people: 4as markdown or json

Linus Torvalds· Dec 24, 2005, 21:50 UTC · lore

It shows you the most recent tag that is reachable from a particular commit is.

Maybe this is something that "git-name-rev" should be taught to do, instead of having a separate command for it. Regardless, I find it useful.

What it does is to take any random commit, and "name" it by looking up the most recent commit that is tagged and reachable from that commit. If the match is exact, it will just print out that ref-name directly. Otherwise it will print out the ref-name, followed by the 8-character "short SHA".

IOW, with something like Junios current tree, I get:
	[torvalds@g5 git]$ git-describe parent
	refs/tags/v1.0.4-g2414721b

ie the current head of my "parent" branch (ie Junio) is based on v1.0.4, but since it has a few commits on top of that, it has added the git hash of the thing to the end: "-g" + 8-char shorthand for the commit 2414721b194453f058079d897d13c4e377f92dc6.

Doing a "git-describe" on a tag-name will just show the full tag path:
	[torvalds@g5 git]$ git-describe v1.0.4
	refs/tags/v1.0.4

unless there are _other_ tags pointing to that commit, in which case it will just choose one at random.

This is useful for two things:
 - automatic version naming in Makefiles, for example. We could use it in 
   git itself: when doing "git --version", we could use this to give a 
   much more useful description of exactly what version was installed.
 - for any random commit (say, you use "gitk <pathname>" or 
   "git-whatchanged" to look at what has changed in some file), you can 
   figure out what the last version of the repo was. Ie, say I find a bug 
   in commit 39ca371c45b04cd50d0974030ae051906fc516b6, I just do:
	[torvalds@g5 linux]$ git-describe 39ca371c45b04cd50d0974030ae051906fc516b6
	refs/tags/v2.6.14-rc4-g39ca371c
   and I now know that it was _not_ in v2.6.14-rc4, but was presumably in 
   v2.6.14-rc5.

The latter is useful when you want to see what "version timeframe" a commit happened in.

Signed-off-by: Linus Torvalds <torvalds@osdl.org>
---
Comments?
			Linus
diff --git a/Makefile b/Makefile
index 3395a9e..47e7898 100644
--- a/Makefile
+++ b/Makefile
@@ -135,7 +135,8 @@ PROGRAMS = \
 	git-unpack-objects$X git-update-index$X git-update-server-info$X \
 	git-upload-pack$X git-verify-pack$X git-write-tree$X \
 	git-update-ref$X git-symbolic-ref$X git-check-ref-format$X \
-	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X
+	git-name-rev$X git-pack-redundant$X git-repo-config$X git-var$X \
+	git-describe$X
 
 # what 'all' will build and 'install' will install.
 ALL_PROGRAMS = $(PROGRAMS) $(SIMPLE_PROGRAMS) $(SCRIPTS) git$X
diff --git a/describe.c b/describe.c
new file mode 100644
index 0000000..ebfa429
--- /dev/null
+++ b/describe.c
@@ -0,0 +1,118 @@
+#include "cache.h"
+#include "commit.h"
+#include "refs.h"
+
+#define SEEN (1u << 0)
+
+static const char describe_usage[] = "git-describe [--all] <committish>*";
+
+static int all = 0;	/* Default to tags only */
+
+static int names = 0, allocs = 0;
+static struct commit_name {
+	const struct commit *commit;
+	char path[];
+} **name_array = NULL;
+
+static struct commit_name *match(struct commit *cmit)
+{
+	int i = names;
+	struct commit_name **p = name_array;
+
+	while (i-- > 0) {
+		struct commit_name *n = *p++;
+		if (n->commit == cmit)
+			return n;
+	}
+	return NULL;
+}
+
+static void add_to_known_names(const char *path, const struct commit *commit)
+{
+	int idx;
+	int len = strlen(path)+1;
+	struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
+
+	name->commit = commit;
+	memcpy(name->path, path, len);
+	idx = names;
+	if (idx >= allocs) {
+		allocs = (idx + 50) * 3 / 2;
+		name_array = xrealloc(name_array, allocs*sizeof(*name_array));
+	}
+	name_array[idx] = name;
+	names = ++idx;
+}
+
+static int get_name(const char *path, const unsigned char *sha1)
+{
+	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
+	if (!commit)
+		return 0;
+	if (!all && strncmp(path, "refs/tags/", 10))
+		return 0;
+	add_to_known_names(path, commit);
+	return 0;
+}
+
+static int compare_names(const void *_a, const void *_b)
+{
+	struct commit_name *a = *(struct commit_name **)_a;
+	struct commit_name *b = *(struct commit_name **)_b;
+	unsigned long a_date = a->commit->date;
+	unsigned long b_date = b->commit->date;
+	return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
+}
+
+static void describe(struct commit *cmit)
+{
+	struct commit_list *list;
+	static int initialized = 0;
+	struct commit_name *n;
+
+	if (!initialized) {
+		initialized = 1;
+		for_each_ref(get_name);
+		qsort(name_array, names, sizeof(*name_array), compare_names);
+	}
+
+	n = match(cmit);
+	if (n) {
+		printf("%s\n", n->path);
+		return;
+	}
+
+	list = NULL;
+	commit_list_insert(cmit, &list);
+	while (list) {
+		struct commit *c = pop_most_recent_commit(&list, SEEN);
+		n = match(c);
+		if (n) {
+			printf("%s-g%.8s\n", n->path, sha1_to_hex(cmit->object.sha1));
+			return;
+		}
+	}
+}
+
+int main(int argc, char **argv)
+{
+	int i;
+
+	for (i = 1; i < argc; i++) {
+		const char *arg = argv[i];
+		unsigned char sha1[20];
+		struct commit *cmit;
+
+		if (!strcmp(arg, "--all")) {
+			all = 1;
+			continue;
+		}
+		if (get_sha1(arg, sha1) < 0)
+			usage(describe_usage);
+		cmit = lookup_commit_reference(sha1);
+		if (!cmit)
+			usage(describe_usage);
+		describe(cmit);
+	}
+	return 0;
+}
Linus Torvalds· Dec 24, 2005, 22:13 UTC · re: Linus Torvalds · lore

Re: Add a "git-describe" command

On Sat, 24 Dec 2005, Linus Torvalds wrote:
Show 6 quoted lines
> 
> This is useful for two things:
> 
>  - automatic version naming in Makefiles, for example. We could use it in 
>    git itself: when doing "git --version", we could use this to give a 
>    much more useful description of exactly what version was installed.

This trivial patch fails to do that correctly, but maybe somebody could fix it.

The problem is not that it generates GIT_VERSION wrong. The problem is 
two-fold:
 - it should notice when "git-describe" doesn't exist, and fall back on 
   the old less-than-descriptive behaviour
 - it doesn't do dependencies correctly (ie it should now make "git" 
   depend on the version number, but it doesn't, so it doesn't re-build 
   git after a commit/pull)
but at least it shows the _idea_ of using git-describe.
With this I get
	[torvalds@g5 git]$ git --version
	git version v1.0.4-g6e9961d6

which I think is better than "1.0.GIT" which doesn't say anything about what the _actual_ version was.

(Ignore the particular SHA1 hash - it has my local commit that you can't re-create that just created that git-describe thing. You'll get your own version number).

		Linus
---
diff --git a/Makefile b/Makefile
index 47e7898..2e5c569 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,7 @@ all:
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-GIT_VERSION = 1.0.GIT
+GIT_VERSION = $(shell git-describe HEAD | sed 's:refs/tags/::')
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
M_· Dec 25, 2005, 19:21 UTC · re: Linus Torvalds · lore

Re: Add a "git-describe" command

Alle 23:13, sabato 24 dicembre 2005, Linus Torvalds ha scritto:
>  - it should notice when "git-describe" doesn't exist, and fall back on
>    the old less-than-descriptive behaviour
And something like this?
GIT_VERSION = $(shell (git-describe HEAD || echo "1.0.GIT") | sed 's:refs/tags/::')
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

Re: Add a "git-describe" command

Linus Torvalds <torvalds@osdl.org> writes:
Show 17 quoted lines
> The problem is not that it generates GIT_VERSION wrong. The problem is 
> two-fold:
>  - it should notice when "git-describe" doesn't exist, and fall back on 
>    the old less-than-descriptive behaviour
>  - it doesn't do dependencies correctly (ie it should now make "git" 
>    depend on the version number, but it doesn't, so it doesn't re-build 
>    git after a commit/pull)
>
> but at least it shows the _idea_ of using git-describe.
>
> With this I get
>
> 	[torvalds@g5 git]$ git --version
> 	git version v1.0.4-g6e9961d6
>
> which I think is better than "1.0.GIT" which doesn't say anything about 
> what the _actual_ version was.

I do not think 1.0.GIT is a good name either, so obviously there is a room for improvement.

One problem with git-describe is that getting tags is a concious user action, and you need to do "git fetch --tags" from time to time in order to see the v1.0.4-g6e9961d6 name. It could well say v1.0.3-g6649c466 if the end user has built on top of otherwise fully up-to-date maint branch without fetching v1.0.4 nor v1.0.5 tags, so when we learn that the v1.0.3-g6649c466 does not work for an end-user, it would not help us to look for regression between v1.0.3 and v1.0.4. All it tells us is that it is not older than v1.0.3, and it may well be the v1.0.5 without any local modification.

Johannes Schindelin· Dec 28, 2005, 02:05 UTC · re: Junio C Hamano · lore

Re: Add a "git-describe" command

Hi,
On Tue, 27 Dec 2005, Junio C Hamano wrote:
> One problem with git-describe is that getting tags is a concious
> user action, and you need to do "git fetch --tags" from time to
> time in order to see the v1.0.4-g6e9961d6 name.

This is probably the biggest problem. I trust you to set the correct version in the Makefile just before tagging it.

Also, Linus hinted at similarities between git-describe and git-name-rev:

--- [PATCH] Teach name-rev to understand the "--inverse" flag

If "--inverse" is passed to name-rev, instead of naming the given revs by the available refs, it does the opposite. The output is sorted by distance, i.e. how many hops are between the rev and the ref. If the ref is not an ancestor of the rev, the distance is inifinite, and the name is undefined.

You can combine "--inverse" with "--tags", in effect getting the list of tags ordered such that the tag describing the rev best comes first.

Signed-off-by: Johannes Schindelin <Johannes.Schindelin@gmx.de>
---
	I guess this is what Linus has been hinting at when he said that
	git-name-rev could be adapted to achieve something similar to
	git-describe.
 name-rev.c |   85 +++++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 1 files changed, 78 insertions(+), 7 deletions(-)
b1831f3f66f659445d2628dda73a1d79fc8c3b6f
diff --git a/name-rev.c b/name-rev.c
index 65333d4..4f7c04e 100644
--- a/name-rev.c
+++ b/name-rev.c
@@ -5,19 +5,21 @@
 #include "refs.h"
 
 static const char name_rev_usage[] =
-	"git-name-rev [--tags] ( --all | --stdin | commitish [commitish...] )\n";
+	"git-name-rev [--tags] ( --all | --stdin | \n"
+	"                       [--inverse] commitish [commitish...] )\n";
 
 typedef struct rev_name {
 	const char *tip_name;
 	int merge_traversals;
 	int generation;
+	int distance;
 } rev_name;
 
 static long cutoff = LONG_MAX;
 
 static void name_rev(struct commit *commit,
 		const char *tip_name, int merge_traversals, int generation,
-		int deref)
+		int distance, int deref)
 {
 	struct rev_name *name = (struct rev_name *)commit->object.util;
 	struct commit_list *parents;
@@ -50,6 +52,7 @@ copy_data:
 		name->tip_name = tip_name;
 		name->merge_traversals = merge_traversals;
 		name->generation = generation;
+		name->distance = distance;
 	} else
 		return;
 
@@ -66,10 +69,10 @@ copy_data:
 				sprintf(new_name, "%s^%d", tip_name, parent_number);
 
 			name_rev(parents->item, new_name,
-				merge_traversals + 1 , 0, 0);
+				merge_traversals + 1 , 0, distance + 1, 0);
 		} else {
 			name_rev(parents->item, tip_name, merge_traversals,
-				generation + 1, 0);
+				generation + 1, distance + 1, 0);
 		}
 	}
 }
@@ -98,7 +101,7 @@ static int name_ref(const char *path, co
 		while ((p = strchr(path, '/')))
 			path = p+1;
 
-		name_rev(commit, strdup(path), 0, 0, deref);
+		name_rev(commit, strdup(path), 0, 0, 0, deref);
 	}
 	return 0;
 }
@@ -118,12 +121,56 @@ static const char* get_rev_name(struct o
 
 	return buffer;
 }
-	
+
+typedef struct {
+	const char* name;
+	struct object* o;
+} named_commit_t;
+static named_commit_t* ref_list = NULL;
+static int ref_count = 0;
+
+static int inverse_build_ref_list(const char *path, const unsigned char *sha1)
+{
+	struct object *o = deref_tag(parse_object(sha1), path, 0);
+
+	if ((!tags_only || !strncmp(path, "refs/tags/", 10)) &&
+			o->type == commit_type) {
+		struct commit* commit = (struct commit*)o;
+
+		if (cutoff > commit->date)
+			cutoff = commit->date;
+
+		if ((ref_count % 256) == 0)
+			ref_list = xrealloc(ref_list,
+				sizeof(named_commit_t*) * (ref_count + 256));
+		ref_list[ref_count].name = strdup(path);
+		ref_list[ref_count].o = (struct object*)commit;
+		ref_count++;
+	}
+	return 0;
+}
+
+static int name_comp(const void* a, const void* b)
+{
+	const rev_name* c = ((const named_commit_t*)a)->o->util;
+	const rev_name* d = ((const named_commit_t*)b)->o->util;
+
+	if (!c)
+		return 1;
+	if (!d)
+		return -1;
+	if (c->distance > d->distance)
+		return 1;
+	if (c->distance < d->distance)
+		return -1;
+	return 0;
+}
+
 int main(int argc, char **argv)
 {
 	struct object_list *revs = NULL;
 	struct object_list **walker = &revs;
-	int as_is = 0, all = 0, transform_stdin = 0;
+	int as_is = 0, all = 0, inverse = 0, transform_stdin = 0;
 
 	setup_git_directory();
 
@@ -142,6 +189,9 @@ int main(int argc, char **argv)
 			} else if (!strcmp(*argv, "--tags")) {
 				tags_only = 1;
 				continue;
+			} else if (!strcmp(*argv, "--inverse")) {
+				inverse = 1;
+				continue;
 			} else if (!strcmp(*argv, "--all")) {
 				if (argc > 1)
 					die("Specify either a list, or --all, not both!");
@@ -181,6 +231,27 @@ int main(int argc, char **argv)
 		walker = &((*walker)->next);
 	}
 
+	if (inverse) {
+		int i;
+
+		if (transform_stdin || all || !revs)
+			usage(name_rev_usage);
+
+		for_each_ref(inverse_build_ref_list);
+		
+		tags_only = 0;
+		for (; revs; revs = revs->next)
+			name_ref(revs->name, revs->item->sha1);
+
+		qsort(ref_list, ref_count, sizeof(named_commit_t), name_comp);
+
+		for (i = 0; i < ref_count; i++)
+			printf("%s %s\n", ref_list[i].name,
+				get_rev_name(ref_list[i].o));
+
+		return 0;
+	}
+
 	for_each_ref(name_ref);
 
 	if (transform_stdin) {
-- 
1.0.GIT
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 1/6] git-describe: really prefer tags only.

Often there are references other than annotated tags under refs/tags hierarchy that are used to "keep things just in case". default to use annotated tags only, still leaving the option to use any ref with --all flag.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Linus Torvalds <torvalds@osdl.org> writes:
 > On Sat, 24 Dec 2005, Linus Torvalds wrote:
 >> 
 >> This is useful for two things:
 >> 
 >>  - automatic version naming in Makefiles, for example. We could use it in 
 >>    git itself: when doing "git --version", we could use this to give a 
 >>    much more useful description of exactly what version was installed.
 >
 > This trivial patch fails to do that correctly, but maybe somebody could 
 > fix it. 
 Here is a 6-patch series towards that.  Will be in the proposed
 updates later tonight.
 describe.c |   15 +++++++++++----
 1 files changed, 11 insertions(+), 4 deletions(-)
170c7e67e7792e0d5ce2328c1d7b8d4139a7c7e7
diff --git a/describe.c b/describe.c
index ebfa429..e1b6588 100644
--- a/describe.c
+++ b/describe.c
@@ -1,12 +1,13 @@
 #include "cache.h"
 #include "commit.h"
+#include "tag.h"
 #include "refs.h"
 
 #define SEEN (1u << 0)
 
 static const char describe_usage[] = "git-describe [--all] <committish>*";
 
-static int all = 0;	/* Default to tags only */
+static int all = 0;	/* Default to annotated tags only */
 
 static int names = 0, allocs = 0;
 static struct commit_name {
@@ -49,9 +50,15 @@ static int get_name(const char *path, co
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	if (!commit)
 		return 0;
-	if (!all && strncmp(path, "refs/tags/", 10))
-		return 0;
-	add_to_known_names(path, commit);
+	if (!all) {
+		struct object *object;
+		if (strncmp(path, "refs/tags/", 10))
+			return 0;
+		object = parse_object(sha1);
+		if (object->type != tag_type)
+			return 0;
+	}
+	add_to_known_names(all ? path : path + 10, commit);
 	return 0;
 }
 
-- 
1.0.5-geb9d
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 2/6] git-describe: use find_unique_abbrev()

Just in case 8 hexadecimal digits are not enough. We could use shorter default if we wanted to.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 describe.c |    3 ++-
 1 files changed, 2 insertions(+), 1 deletions(-)
298f9203cf85d5de28a8380bd26f85206530b7d0
diff --git a/describe.c b/describe.c
index e1b6588..ba49d00 100644
--- a/describe.c
+++ b/describe.c
@@ -95,7 +95,8 @@ static void describe(struct commit *cmit
 		struct commit *c = pop_most_recent_commit(&list, SEEN);
 		n = match(c);
 		if (n) {
-			printf("%s-g%.8s\n", n->path, sha1_to_hex(cmit->object.sha1));
+			printf("%s-g%s\n", n->path,
+			       find_unique_abbrev(cmit->object.sha1, 8));
 			return;
 		}
 	}
-- 
1.0.5-geb9d
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 3/6] git-describe: --tags and --abbrev

With --tags, not just annontated tags, but also any ref under refs/tags/ are used to name the revision.

The number of digits is configurable with the --abbrev=<n> option.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 describe.c |   33 +++++++++++++++++++++++++++------
 1 files changed, 27 insertions(+), 6 deletions(-)
970d400993d27a3228bccd72c0ddba767ebbbfff
diff --git a/describe.c b/describe.c
index ba49d00..ee38f5f 100644
--- a/describe.c
+++ b/describe.c
@@ -5,9 +5,14 @@
 
 #define SEEN (1u << 0)
 
-static const char describe_usage[] = "git-describe [--all] <committish>*";
+static const char describe_usage[] =
+"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";
 
 static int all = 0;	/* Default to annotated tags only */
+static int tags = 0;	/* But allow any tags if --tags is specified */
+
+#define DEFAULT_ABBREV 8 /* maybe too many */
+static int abbrev = DEFAULT_ABBREV;
 
 static int names = 0, allocs = 0;
 static struct commit_name {
@@ -50,13 +55,19 @@ static int get_name(const char *path, co
 	struct commit *commit = lookup_commit_reference_gently(sha1, 1);
 	if (!commit)
 		return 0;
+	/* If --all, then any refs are used.
+	 * If --tags, then any tags are used.
+	 * Otherwise only annotated tags are used.
+	 */
 	if (!all) {
-		struct object *object;
 		if (strncmp(path, "refs/tags/", 10))
 			return 0;
-		object = parse_object(sha1);
-		if (object->type != tag_type)
-			return 0;
+		if (!tags) {
+			struct object *object;
+			object = parse_object(sha1);
+			if (object->type != tag_type)
+				return 0;
+		}
 	}
 	add_to_known_names(all ? path : path + 10, commit);
 	return 0;
@@ -96,7 +107,7 @@ static void describe(struct commit *cmit
 		n = match(c);
 		if (n) {
 			printf("%s-g%s\n", n->path,
-			       find_unique_abbrev(cmit->object.sha1, 8));
+			       find_unique_abbrev(cmit->object.sha1, abbrev));
 			return;
 		}
 	}
@@ -115,6 +126,16 @@ int main(int argc, char **argv)
 			all = 1;
 			continue;
 		}
+		if (!strcmp(arg, "--tags")) {
+			tags = 1;
+			continue;
+		}
+		if (!strncmp(arg, "--abbrev=", 9)) {
+			abbrev = strtoul(arg + 9, NULL, 10);
+			if (abbrev < 4 || 40 <= abbrev)
+				abbrev = DEFAULT_ABBREV;
+			continue;
+		}
 		if (get_sha1(arg, sha1) < 0)
 			usage(describe_usage);
 		cmit = lookup_commit_reference(sha1);
-- 
1.0.5-geb9d
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 4/6] git-describe: still prefer annotated tag under --all and --tags

Even though --all and --tags can be used to include non annotated tags in the reference point candidates, prefer to use annotated tags if there are more than one refs that name the same commit.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 describe.c |   34 +++++++++++++++++++++++++---------
 1 files changed, 25 insertions(+), 9 deletions(-)
43214fab3416d54dcf41a8f89c59484e9c930b23
diff --git a/describe.c b/describe.c
index ee38f5f..84d96b5 100644
--- a/describe.c
+++ b/describe.c
@@ -17,6 +17,7 @@ static int abbrev = DEFAULT_ABBREV;
 static int names = 0, allocs = 0;
 static struct commit_name {
 	const struct commit *commit;
+	int prio; /* annotated tag = 2, tag = 1, head = 0 */
 	char path[];
 } **name_array = NULL;
 
@@ -33,13 +34,16 @@ static struct commit_name *match(struct 
 	return NULL;
 }
 
-static void add_to_known_names(const char *path, const struct commit *commit)
+static void add_to_known_names(const char *path,
+			       const struct commit *commit,
+			       int prio)
 {
 	int idx;
 	int len = strlen(path)+1;
 	struct commit_name *name = xmalloc(sizeof(struct commit_name) + len);
 
 	name->commit = commit;
+	name->prio = prio; 
 	memcpy(name->path, path, len);
 	idx = names;
 	if (idx >= allocs) {
@@ -53,23 +57,32 @@ static void add_to_known_names(const cha
 static int get_name(const char *path, const unsigned char *sha1)
 {
 	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 (!strncmp(path, "refs/tags/", 10)) {
+		if (object->type == tag_type)
+			prio = 2;
+		else
+			prio = 1;
+	}
+	else
+		prio = 0;
+
 	if (!all) {
-		if (strncmp(path, "refs/tags/", 10))
+		if (!prio)
+			return 0;
+		if (!tags && prio < 2)
 			return 0;
-		if (!tags) {
-			struct object *object;
-			object = parse_object(sha1);
-			if (object->type != tag_type)
-				return 0;
-		}
 	}
-	add_to_known_names(all ? path : path + 10, commit);
+	add_to_known_names(all ? path + 5 : path + 10, commit, prio);
 	return 0;
 }
 
@@ -79,6 +92,9 @@ static int compare_names(const void *_a,
 	struct commit_name *b = *(struct commit_name **)_b;
 	unsigned long a_date = a->commit->date;
 	unsigned long b_date = b->commit->date;
+
+	if (a->prio != b->prio)
+		return b->prio - a->prio;
 	return (a_date > b_date) ? -1 : (a_date == b_date) ? 0 : 1;
 }
 
-- 
1.0.5-geb9d
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 5/6] git-describe: documentation.

Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 Documentation/git-describe.txt |   79 ++++++++++++++++++++++++++++++++++++++++
 Documentation/git.txt          |    3 ++
 2 files changed, 82 insertions(+), 0 deletions(-)
 create mode 100644 Documentation/git-describe.txt
82c11e802b05278d902138abc627982273b777c7
diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
new file mode 100644
index 0000000..0efe82a
--- /dev/null
+++ b/Documentation/git-describe.txt
@@ -0,0 +1,79 @@
+git-describe(1)
+===============
+
+NAME
+----
+git-describe - Show the most recent tag that is reachable from a commit.
+
+
+SYNOPSIS
+--------
+'git-describe' [--all] [--tags] [--abbrev=<n>] <committish>...
+
+DESCRIPTION
+-----------
+The command finds the most recent tag that is reachable from a
+commit, and if the commit itself is pointed at by the tag, shows
+the tag.  Otherwise, it suffixes the tag name with abbreviated
+object name of the commit.
+
+
+OPTIONS
+-------
+<committish>::
+	The object name of the comittish. 
+
+--all::
+	Instead of using only the annotated tags, use any ref
+	found in `.git/refs/`.
+
+--tags::
+	Instead of using only the annotated tags, use any tag
+	found in `.git/refs/tags`.
+
+--abbrev=<n>::
+	Instead of using the default 8 hexadecimal digits as the
+	abbreviated object name, use <n> digits.
+
+
+EXAMPLES
+--------
+
+With something like git.git current tree, I get:
+
+	[torvalds@g5 git]$ git-describe parent
+	v1.0.4-g2414721b
+
+i.e. the current head of my "parent" branch is based on v1.0.4,
+but since it has a few commits on top of that, it has added the
+git hash of the thing to the end: "-g" + 8-char shorthand for
+the commit `2414721b194453f058079d897d13c4e377f92dc6`.
+
+Doing a "git-describe" on a tag-name will just show the tag name:
+
+	[torvalds@g5 git]$ git-describe v1.0.4
+	v1.0.4
+
+With --all, the command can use branch heads as references, so
+the output shows the reference path as well:
+
+	[torvalds@g5 git]$ git describe --all --abbrev=4 v1.0.5^2
+	tags/v1.0.0-g975b
+
+	[torvalds@g5 git]$ git describe --all HEAD^
+	heads/lt/describe-g975b
+
+
+Author
+------
+Written by Linus Torvalds <torvalds@osdl.org>, but somewhat
+butchered by Junio C Hamano <junkio@cox.net>
+
+Documentation
+--------------
+Documentation by David Greaves, Junio C Hamano and the git-list <git@vger.kernel.org>.
+
+GIT
+---
+Part of the gitlink:git[7] suite
+
diff --git a/Documentation/git.txt b/Documentation/git.txt
index 5f068c2..90c5bfa 100644
--- a/Documentation/git.txt
+++ b/Documentation/git.txt
@@ -139,6 +139,9 @@ Interrogation commands
 gitlink:git-cat-file[1]::
 	Provide content or type/size information for repository objects.
 
+gitlink:git-describe[1]::
+	Show the most recent tag that is reachable from a commit.
+
 gitlink:git-diff-index[1]::
 	Compares content and mode of blobs between the index and repository.
 
-- 
1.0.5-geb9d
Junio C Hamano· Dec 28, 2005, 00:42 UTC · re: Linus Torvalds · lore

[PATCH 6/6] Makefile: use git-describe to mark the git version.

Note: with this commit, the GIT maintainer workflow must change.
GIT-VERSION-GEN is now the file to munge when the default
version needs to be changed, not Makefile.  The tag needs to be
pushed into the repository to build the official tarball and
binary package beforehand.
Signed-off-by: Junio C Hamano <junkio@cox.net>
---
 * Only lightly tested.  Especially bootstrapping might be
   fishy, but I'll be heading off to Japan for fishful new year
   meals in a few days ;-), so...
 .gitignore      |    1 +
 GIT-VERSION-GEN |   18 ++++++++++++++++++
 Makefile        |   10 ++++++++--
 3 files changed, 27 insertions(+), 2 deletions(-)
 create mode 100755 GIT-VERSION-GEN
8e517cdeb5644b9857c8a9d8ce204ec9b7405297
diff --git a/.gitignore b/.gitignore
index 6bd508e..47d76f4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
+GIT-VERSION-FILE
 git
 git-add
 git-am
diff --git a/GIT-VERSION-GEN b/GIT-VERSION-GEN
new file mode 100755
index 0000000..196402c
--- /dev/null
+++ b/GIT-VERSION-GEN
@@ -0,0 +1,18 @@
+#!/bin/sh
+
+GVF=GIT-VERSION-FILE
+
+VN=$(git describe --abbrev=4 HEAD 2>/dev/null) || VN=v1.0.GIT
+VN=$(expr "$VN" : v'\(.*\)')
+if test -r $GVF
+then
+	VC=$(sed -e 's/^GIT_VERSION = //' <$GVF)
+else
+	VC=unset
+fi
+test "$VN" = "$VC" || {
+	echo >&2 "GIT_VERSION = $VN"
+	echo "GIT_VERSION = $VN" >$GVF
+}
+
+
diff --git a/Makefile b/Makefile
index 47e7898..6f1d123 100644
--- a/Makefile
+++ b/Makefile
@@ -55,7 +55,9 @@ all:
 # Define USE_STDEV below if you want git to care about the underlying device
 # change being considered an inode change from the update-cache perspective.
 
-GIT_VERSION = 1.0.GIT
+GIT-VERSION-FILE: .FORCE-GIT-VERSION-FILE
+	@sh ./GIT-VERSION-GEN
+-include GIT-VERSION-FILE
 
 # CFLAGS and LDFLAGS are for the users to override from the command line.
 
@@ -369,7 +371,7 @@ all: $(ALL_PROGRAMS)
 all:
 	$(MAKE) -C templates
 
-git$X: git.c $(LIB_FILE) Makefile
+git$X: git.c $(LIB_FILE) Makefile GIT-VERSION-FILE
 	$(CC) -DGIT_EXEC_PATH='"$(bindir)"' -DGIT_VERSION='"$(GIT_VERSION)"' \
 		$(CFLAGS) $(COMPAT_CFLAGS) -o $@ $(filter %.c,$^) $(LIB_FILE)
 
@@ -499,6 +501,7 @@ deb: dist
 ### Cleaning rules
 
 clean:
+	rm -f GIT-VERSION-FILE
 	rm -f *.o mozilla-sha1/*.o arm/*.o ppc/*.o compat/*.o $(LIB_FILE)
 	rm -f $(PROGRAMS) $(SIMPLE_PROGRAMS) git$X
 	rm -f $(filter-out gitk,$(SCRIPTS))
@@ -511,3 +514,6 @@ clean:
 	$(MAKE) -C templates clean
 	$(MAKE) -C t/ clean
 
+.PHONY: all install clean
+.PHONY: .FORCE-GIT-VERSION-FILE
+
-- 
1.0.5-geb9d
Junio C Hamano· Dec 25, 2005, 01:42 UTC · re: Linus Torvalds · lore

Re: Add a "git-describe" command

Linus Torvalds <torvalds@osdl.org> writes:
> What it does is to take any random commit, and "name" it by looking up the 
> most recent commit that is tagged and reachable from that commit.
Sounds useful.
> Comments?
> +static struct commit_name {
> +	const struct commit *commit;
> +	char path[];
> +} **name_array = NULL;

I wonder if some tags are more important than others (we may want to pick a signed tag that is a bit further down than an autogenerated snapshot -git47 tag), but that depends on the usage. For bug hunting -git47 is more useful than sparsely done signed tags e.g. -rc4.

Show 13 quoted lines
> +static void describe(struct commit *cmit)
> +...
> +	list = NULL;
> +	commit_list_insert(cmit, &list);
> +	while (list) {
> +		struct commit *c = pop_most_recent_commit(&list, SEEN);
> +		n = match(c);
> +		if (n) {
> +			printf("%s-g%.8s\n", n->path, sha1_to_hex(cmit->object.sha1));
> +			return;
> +		}
> +	}
> +}

I think this leaks list elements but until/unless this is libified we would not care too much about it. We could use find_unique_abbrev(cmit->object.sha1, 8) but probably it is an overkill.

Linus Torvalds· Dec 25, 2005, 03:46 UTC · re: Junio C Hamano · lore

Re: Add a "git-describe" command

On Sat, 24 Dec 2005, Junio C Hamano wrote:
Show 6 quoted lines
>
> I wonder if some tags are more important than others (we may
> want to pick a signed tag that is a bit further down than an
> autogenerated snapshot -git47 tag), but that depends on the
> usage.  For bug hunting -git47 is more useful than sparsely done
> signed tags e.g. -rc4.
I considered just printing every tag I could reach, and then you could do 
	git-describe xyz | head -1
to get the closest one.

But then I decided that almost always, you just want the most recent one. But we could add a "--history" cmd line option to show all the reachable ones (or limit it to <n> tags shown or something)..

But there's already some logic to cull out uninteresting references (ie the "--all" flag enables all kinds of references, normally we only look at tags), and yes, we could also just add some other culling logic. For example, maybe we only care about annotated tags (ie real tag objects with a message, rather than just direct references to commits).

My real goal was to see a real meaningful version when I do "git --version". I just hate how it normally just says it's version "1.0.GIT", and I have no idea how new/old it really is.

			Linus
Junio C Hamano· Dec 25, 2005, 09:44 UTC · re: Linus Torvalds · lore

Re: Add a "git-describe" command

Linus Torvalds <torvalds@osdl.org> writes:
> My real goal was to see a real meaningful version when I do "git 
> --version". I just hate how it normally just says it's version "1.0.GIT", 
> and I have no idea how new/old it really is.

Hmph. I am not so interested in "git --version", but I find git-describe to be pretty attractive.

Last night I proposed to tighten pack naming which would promote so-far just a convention to a rule, which would introduce an backward incompatibility and wanted to know which released versions are affected. With git-whatchanged I can identify the exact commit that changed the packname SHA1 computation with ease:

        $ git whatchanged --abbrev --pretty=oneline \
          -S'	sorted_by_sha = create_sorted_list(sha1_sort);
                SHA1_Init(&ctx);
                list = sorted_by_sha;
                for (i = 0; i < nr_objects; i++) {
        ' HEAD -- pack-objects.c
        diff-tree 84c8d8a... (from 9cf6d33...)
        Fix packname hash generation.
        :100644 100644 3d62278... ef55cab... M	pack-objects.c

and from that I would have been able to find that the change was between 0.99.8 and 0.99.9:

        $ git describe 84c8d8a
        refs/tags/v0.99.8-g84c8d8ae

I used "git-show-branch $that_commit tags/v0.99.?" to get that information instead, but git describe would have been very useful.

← back to recent threads