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
@@ -135,7 +135,8 @@ PROGRAMS = \git-unpack-objects$Xgit-update-index$Xgit-update-server-info$X\git-upload-pack$Xgit-verify-pack$Xgit-write-tree$X\git-update-ref$Xgit-symbolic-ref$Xgit-check-ref-format$X\-git-name-rev$Xgit-pack-redundant$Xgit-repo-config$Xgit-var$X+git-name-rev$Xgit-pack-redundant$Xgit-repo-config$Xgit-var$X\+git-describe$X# what 'all' will build and 'install' will install.ALL_PROGRAMS=$(PROGRAMS)$(SIMPLE_PROGRAMS)$(SCRIPTS)git$X
@@ -0,0 +1,118 @@+#include"cache.h"+#include"commit.h"+#include"refs.h"++#define SEEN (1u << 0)++staticconstchardescribe_usage[]="git-describe [--all] <committish>*";++staticintall=0;/* Default to tags only */++staticintnames=0,allocs=0;+staticstructcommit_name{+conststructcommit*commit;+charpath[];+}**name_array=NULL;++staticstructcommit_name*match(structcommit*cmit)+{+inti=names;+structcommit_name**p=name_array;++while(i-->0){+structcommit_name*n=*p++;+if(n->commit==cmit)+returnn;+}+returnNULL;+}++staticvoidadd_to_known_names(constchar*path,conststructcommit*commit)+{+intidx;+intlen=strlen(path)+1;+structcommit_name*name=xmalloc(sizeof(structcommit_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;+}++staticintget_name(constchar*path,constunsignedchar*sha1)+{+structcommit*commit=lookup_commit_reference_gently(sha1,1);+if(!commit)+return0;+if(!all&&strncmp(path,"refs/tags/",10))+return0;+add_to_known_names(path,commit);+return0;+}++staticintcompare_names(constvoid*_a,constvoid*_b)+{+structcommit_name*a=*(structcommit_name**)_a;+structcommit_name*b=*(structcommit_name**)_b;+unsignedlonga_date=a->commit->date;+unsignedlongb_date=b->commit->date;+return(a_date>b_date)?-1:(a_date==b_date)?0:1;+}++staticvoiddescribe(structcommit*cmit)+{+structcommit_list*list;+staticintinitialized=0;+structcommit_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){+structcommit*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;+}+}+}++intmain(intargc,char**argv)+{+inti;++for(i=1;i<argc;i++){+constchar*arg=argv[i];+unsignedcharsha1[20];+structcommit*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);+}+return0;+}
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
---
@@ -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=$(shellgit-describeHEAD|sed's:refs/tags/::')# CFLAGS and LDFLAGS are for the users to override from the command line.
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
@@ -5,9 +5,14 @@#define SEEN (1u << 0)-staticconstchardescribe_usage[]="git-describe [--all] <committish>*";+staticconstchardescribe_usage[]=+"git-describe [--all] [--tags] [--abbrev=<n>] <committish>*";staticintall=0;/* Default to annotated tags only */+staticinttags=0;/* But allow any tags if --tags is specified */++#define DEFAULT_ABBREV 8 /* maybe too many */+staticintabbrev=DEFAULT_ABBREV;staticintnames=0,allocs=0;staticstructcommit_name{
@@ -50,13 +55,19 @@ static int get_name(const char *path, costructcommit*commit=lookup_commit_reference_gently(sha1,1);if(!commit)return0;+/* If --all, then any refs are used.+*If--tags,thenanytagsareused.+*Otherwiseonlyannotatedtagsareused.+*/if(!all){-structobject*object;if(strncmp(path,"refs/tags/",10))return0;-object=parse_object(sha1);-if(object->type!=tag_type)-return0;+if(!tags){+structobject*object;+object=parse_object(sha1);+if(object->type!=tag_type)+return0;+}}add_to_known_names(all?path:path+10,commit);return0;
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
@@ -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.
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
@@ -1,12 +1,13 @@#include"cache.h"#include"commit.h"+#include"tag.h"#include"refs.h"#define SEEN (1u << 0)staticconstchardescribe_usage[]="git-describe [--all] <committish>*";-staticintall=0;/* Default to tags only */+staticintall=0;/* Default to annotated tags only */staticintnames=0,allocs=0;staticstructcommit_name{
@@ -49,9 +50,15 @@ static int get_name(const char *path, costructcommit*commit=lookup_commit_reference_gently(sha1,1);if(!commit)return0;-if(!all&&strncmp(path,"refs/tags/",10))-return0;-add_to_known_names(path,commit);+if(!all){+structobject*object;+if(strncmp(path,"refs/tags/",10))+return0;+object=parse_object(sha1);+if(object->type!=tag_type)+return0;+}+add_to_known_names(all?path:path+10,commit);return0;}
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
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
@@ -17,6 +17,7 @@ static int abbrev = DEFAULT_ABBREV;staticintnames=0,allocs=0;staticstructcommit_name{conststructcommit*commit;+intprio;/* annotated tag = 2, tag = 1, head = 0 */charpath[];}**name_array=NULL;
@@ -53,23 +57,32 @@ static void add_to_known_names(const chastaticintget_name(constchar*path,constunsignedchar*sha1){structcommit*commit=lookup_commit_reference_gently(sha1,1);+structobject*object;+intprio;+if(!commit)return0;+object=parse_object(sha1);/* If --all, then any refs are used.*If--tags,thenanytagsareused.*Otherwiseonlyannotatedtagsareused.*/+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)+return0;+if(!tags&&prio<2)return0;-if(!tags){-structobject*object;-object=parse_object(sha1);-if(object->type!=tag_type)-return0;-}}-add_to_known_names(all?path:path+10,commit);+add_to_known_names(all?path+5:path+10,commit,prio);return0;}
@@ -79,6 +92,9 @@ static int compare_names(const void *_a,structcommit_name*b=*(structcommit_name**)_b;unsignedlonga_date=a->commit->date;unsignedlongb_date=b->commit->date;++if(a->prio!=b->prio)+returnb->prio-a->prio;return(a_date>b_date)?-1:(a_date==b_date)?0:1;}
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.
@@ -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+
@@ -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.
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
@@ -142,6 +189,9 @@ int main(int argc, char **argv)}elseif(!strcmp(*argv,"--tags")){tags_only=1;continue;+}elseif(!strcmp(*argv,"--inverse")){+inverse=1;+continue;}elseif(!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){+inti;++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));++return0;+}+for_each_ref(name_ref);if(transform_stdin){