Add a "git-describe" command

Subsystems: kernel build + files below scripts/ (unless maintained elsewhere), the rest

11 messages, 4 authors, 2016-06-15 · open the first message on its own page

Add a "git-describe" command

From: Linus Torvalds <torvalds@osdl.org>
Date: 2016-06-15 22:42:15

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;
+}

Re: Add a "git-describe" command

From: Linus Torvalds <torvalds@osdl.org>
Date: 2016-06-15 22:42:15


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. 

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.
 

Re: Add a "git-describe" command

From: M_ <hidden>
Date: 2016-06-15 22:42:15

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/::')

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

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 <redacted>

---

 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

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

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 <redacted>

---
 * 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

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

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 <redacted>

---

 Linus Torvalds [off-list ref] 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

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

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

Signed-off-by: Junio C Hamano <redacted>

---

 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

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

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 <redacted>

---

 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

Re: Add a "git-describe" command

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

Linus Torvalds [off-list ref] writes:
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.

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

From: Junio C Hamano <hidden>
Date: 2016-06-15 22:42:15

Signed-off-by: Junio C Hamano <redacted>

---

 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

Re: Add a "git-describe" command

From: Johannes Schindelin <hidden>
Date: 2016-06-15 22:42:15

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 <redacted>

---

	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
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help