253

We use SVN at work, but for my personal projects I decided to use Git. So I installed Git yesterday, and I wonder what is the revision number equivalent in Git.

Let's say we work on version 3.0.8 and every bug fix has its own revision number we can use when we talk about this bug fix. So if I tag the code in Git to 3.0.8 what then I can use as a revision number or some other more detailed kind of identification? I find the hash not so user friendly for humans.

Mateusz Piotrowski
  • 6,087
  • 9
  • 44
  • 71
Radek
  • 14,395
  • 48
  • 147
  • 231
  • possible duplicate of [How to get the git commit count?](http://stackoverflow.com/questions/677436/how-to-get-the-git-commit-count) – Laurence Gonsalves Nov 07 '10 at 22:28
  • There's an [article](http://gitsvn.x10.mx/?p=111) for one of possible solutions. It could look a bit heavy but intergrated into "git log" output and requires no additional commands other than "git push/pull/fetch". – Dmitry Pavlenko Sep 22 '12 at 20:22
  • Unfortunate the linked article from @DmitryPavlenko is on a dead domain: gitsvn.x10.mx. With no indication of the subject, it will be difficult for anyone to find it elsewhere. – Michael Donohue Feb 02 '14 at 02:19
  • Note: no more abbrev needed with git describe: see http://stackoverflow.com/a/41308073/6309 – VonC Dec 23 '16 at 21:36
  • 3
    No, Laurence Gonsalves, this is not a duplicate of "how to get the git commit count?" - this is about version number, and even though the commit count can be fiddled to look a bit alike - the hash version is very different than the commit count :-) – David Svarrer Jul 01 '17 at 13:52
  • The differences between Git and SVN are so profound that trying to understand Git in SVN terms will essentially not work. It is far easier to pretend you knew nothing whatsoever about version control, read the [Git book](https://git-scm.com/book/en/v2) **from scratch** and **TAKE YOUR TIME**. Git is very nearly mindblowing. – Lutz Prechelt Sep 06 '19 at 16:05

19 Answers19

194

With modern Git (1.8.3.4 in my case) and not using branches you can do:

$ git rev-list --count HEAD
68
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
max
  • 26,552
  • 10
  • 48
  • 72
  • 22
    Consider using `git rev-list --count --first-parent HEAD` – Flimm Jan 17 '14 at 12:25
  • 8
    Given this information (the number 68), is there a way to determine the revision for re-acquiring the code? Suppose "revision 68" is released to a testing environment, development continues, and later a developer needs to re-acquire "revision 68" from source control. How would he target the specific version to clone? Or am I missing something about Git that makes this unnecessary? – David Jan 17 '14 at 15:07
  • 9
    Keep in mind that this solution would yield different results for different developers. Also, calculating backwards from the "count" to the commit would yield different commits for different developers!! This is because of the distributed nature of Git and that commits happen in different repositories during the same chunk of time but may not be counted in `git rev-list --count HEAD` depending on when the last pushes and pulls were made. – Jason Jul 20 '14 at 18:53
  • 2
    @Jason, does David's comment about adding `--first-parent` address your concern? I'd hate to avoid a seemingly-simplest solution because of a rare edge case if there is an equally-simple workaround or way to make it more robust. – MarkHu Feb 09 '16 at 21:26
  • 4
    @MarkHu, `--first-parent` helps. As long as no rebasing is done and the same branch is always used for making releases (and calculating this release number, of course), I think this could be used. Though, I'm still uncertain it would always uniquely identify the commit that the release came from. There are so many things that can go wrong here... but at the moment I cannot think of something that would definitely break this (given my above "as long as" statements). The `git describe` method mentioned in another answer is the way to go. Create a tag if you want something human readable. – Jason Feb 11 '16 at 15:44
  • 2
    Great answer, but how to go back? Lets say I have a version of my project 1.8.456. How can I go back from 456 -> 5ghj34ghj1234 ? – Sparrow_ua Oct 06 '16 at 14:47
  • @Sparrow_ua Telling us what kind of thing `5ghj34ghj1234` is supposed to be would help. – underscore_d Nov 04 '16 at 18:43
  • 1
    @Jason, to solve both the "incremental revision" issue (that is, be able to tell that version B is newer than version A) and the "where does it come from" issue, I would propose using a 4-component versioning scheme like this: `major.minor.git-rev-count.git-short-hash`. That would give you versions like `1.18.68.e72f6d2`. Basically, that is what `git describe` with tags from Greg Hewgill's answer does, but without tags. – Alexander Amelkin Feb 07 '17 at 10:29
  • 1
    @underscore_d I think it's reasonable to assume that Sparrow_ua is talking about a commit hash – forresthopkinsa Feb 22 '17 at 00:54
  • I use a combination of this and a hash of the commit for uniqueness – kiedysktos May 09 '17 at 12:43
  • This worked well for me. BRANCH=$(git branch|grep \*|awk '{print $2}') [[ $BRANCH == "master" ]] && { REVISION_NUMBER=$(git rev-list --count HEAD) true } || { REVISION_NUMBER=${BRANCH}-$(git rev-list --count HEAD) } – LinuxGuru Nov 30 '18 at 14:59
  • I use master branch for major version and dev branch for minor version eg `1.68` – Jacksonkr Mar 12 '20 at 14:17
154

Good or bad news for you, that hash IS the revision number. I also had trouble with this when I made the switch from SVN to git.

You can use "tagging" in git to tag a certain revision as the "release" for a specific version, making it easy to refer to that revision. Check out this blog post.

The key thing to understand is that git cannot have revision numbers - think about the decentralized nature. If users A and B are both committing to their local repositories, how can git reasonably assign a sequential revision number? A has no knowledge of B before they push/pull each other's changes.

Another thing to look at is simplified branching for bugfix branches:

Start with a release: 3.0.8. Then, after that release, do this:

git branch bugfixes308

This will create a branch for bugfixes. Checkout the branch:

git checkout bugfixes308

Now make any bugfix changes you want.

git commit -a

Commit them, and switch back to the master branch:

git checkout master

Then pull in those changes from the other branch:

git merge bugfixes308

That way, you have a separate release-specific bugfix branch, but you're still pulling the bugfix changes into your main dev trunk.

makdad
  • 6,262
  • 3
  • 29
  • 55
  • 11
    I understood that hash is the revision number but I hoped it was not :-))) thank you for very nice explanation and for suggestion now to deal with it. – Radek Nov 07 '10 at 22:29
  • 3
    You're welcome. I was very frustrated with git when I first picked it up from SVN, but now I swing the other way... – makdad Nov 07 '10 at 22:52
  • 4
    ALSO, it's been awhile since I posted this, but remember that you can also do "git checkout -b new_branch_name" to do "git branch foo" and "git checkout foo" as a one-liner. – makdad Jun 19 '12 at 16:28
  • 1
    Am I correct that hashes are "true" hashes, and not even remotely sequential? So, importantly, if the bug database says `fixed in 547cc3e..c4b2eba`, and you have some other revision, you have no idea whether or not your code is supposed to contain the fix?! Surely the gits at git-central have a solution for this?!?! – Olie May 23 '13 at 22:29
  • 1
    @Olie yes, you are correct there. You use `git log` for that. – Cole Johnson Jun 29 '13 at 01:23
  • @Olie `{ git rev-list 547cc3e..c4b2eba; git rev-list HEAD; } | sort | uniq -d`. [Here's a decent starter kit on unix text tools](http://www.ibm.com/developerworks/aix/library/au-unixtext/). [Here's another](http://tldp.org/LDP/GNU-Linux-Tools-Summary/html/text-manipulation-tools.html). – jthill Oct 24 '13 at 17:44
  • 3
    In SVN, the fact that a bug is fixed in r42 does not tell you about whether it's also fixed in r43 as soon as you use branches actually. – Matthieu Moy Apr 29 '15 at 07:51
  • What is the name of the hash-string identifier? – aaronbauman May 07 '15 at 18:29
  • 1
    I usually call it the commit SHA (pronounced "shah"). – makdad May 09 '15 at 10:04
107

The git describe command creates a slightly more human readable name that refers to a specific commit. For example, from the documentation:

With something like git.git current tree, I get:

[torvalds@g5 git]$ git describe parent
v1.0.4-14-g2414721

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, describe has added the number of additional commits ("14") and an abbreviated object name for the commit itself ("2414721") at the end.

As long as you use sensibly named tags to tag particular releases, this can be considered to be roughly equivalent to a SVN "revision number".

Alex Jasmin
  • 37,102
  • 6
  • 72
  • 63
Greg Hewgill
  • 828,234
  • 170
  • 1,097
  • 1,237
  • 8
    I'd just like to note that this works _only_ if your `git` repository already has tags; if it doesn't, you might get [git describe fails with "fatal: No names found, cannot describe anything." - Stack Overflow](http://stackoverflow.com/questions/4916492/git-describe-fails-with-fatal-no-names-found-cannot-describe-anything); meaning that you'd have to set up tags yourself. – sdaau Apr 21 '13 at 19:41
  • 14
    @sdaau: If you are using this in a script or something and want `git describe` to never fail, use the `git describe --always` option. – Greg Hewgill Apr 21 '13 at 19:52
  • Can the output of `git describe` be used to find the source commit, somehow? Short of manually counting commits in the log, I mean. – Lii Oct 15 '18 at 18:34
  • @Lii: What do you mean by "source commit"? Both the nearest tag (`v1.0.4`) and the most recent commit id (`2414721`) are already part of the git describe output. – Greg Hewgill Oct 15 '18 at 19:34
  • @GregHewgill: Yes, thanks, when I asked the question I didn't realize that the *"abbreviated object name"* is a value which can be used to identify the commit. That's brilliant! – Lii Oct 16 '18 at 07:09
68

The other posters are right, there is no "revision-number".

I think the best way is to use Tags for "releases"!

But I made use of the following to fake revision numbers (just for clients to see revisions and the progress, as they wanted to have the same increasing revisions from git as they where use to by subversion).

Show the "current revision" of "HEAD" is simulated by using this:

git rev-list HEAD | wc -l

But what if the client tells me that there is a bug in "revision" 1302 ?

For this I added the following to the [alias] section of my ~/.gitconfig:

show-rev-number = !sh -c 'git rev-list --reverse HEAD | nl | awk \"{ if(\\$1 == "$0") { print \\$2 }}\"'

using git show-rev-number 1302 will then print the hash for the "revision" :)

I made a Blog Post (in german) about that "technique" some time ago.

Eporediese
  • 720
  • 9
  • 11
OderWat
  • 4,553
  • 2
  • 22
  • 25
  • @Radek - "sometimes need to know what 'code change=commit' fixed something" - then [`git bisect` (link)](http://git-scm.com/book/en/Git-Tools-Debugging-with-Git) is the appropriate tool to identify what changed when by who. – michael Jul 05 '12 at 13:06
  • @Radek Yes it is an ever increasing number. It just counts the revisions you checked in on the HEAD. So every commit is a new revision. This will not work for different branches. – OderWat Oct 19 '12 at 23:32
  • 1
    I like your solution. Please notice that you can simplify it: show-rev-number = !sh -c 'git rev-list --reverse HEAD | awk NR==$0' – avner Nov 04 '12 at 14:12
  • @avner Thank you! I did not use a lot of awk in my life, obviously :) – OderWat Nov 26 '12 at 22:33
  • 3
    I had to use `git rev-list --reverse HEAD | awk "{ print NR }" | tail -n 1` – Gerry Mar 22 '13 at 06:58
  • You might want to strip the whitespace if you are assigning the number to a script variable. This will do it: `git rev-list HEAD | wc -l | tr -d [[:space:]]` – gauss256 Jul 03 '13 at 01:09
  • The `--topo-order` option helps in environments where branches are used: go from commit number to git hash using `show-rev-number = !sh -c 'git rev-list --topo-order --reverse HEAD | awk \"NR==$1 {print}\"' -` Having said that, I am still unconvinced that this reliably goes from commit numbers to hash in an environment where branches are used. You could be sure by repeatedly looking for a such that `git rev-list | wc -l` renders the exact commit number. – John McGehee May 19 '15 at 22:04
  • OK, I'm coming into this discussion a few years late. I am quite impressed by this solution, especially that it allows getting the hash from the "build number". Is this reliable? If I take the number (Rev-list count), will that *always* relate back to the same hash, or are there changes that could happen in the git which would change the correlation? Thank you. – Basya Nov 06 '17 at 14:15
  • Linux ways... the lookout could be heavily shortened with sed this way: `snr = !sh -c 'git rev-list --reverse HEAD | sed -n "$0p"'` – MoonCactus Aug 02 '18 at 07:32
  • Hi, it seems like `git rev-list --count HEAD` is doing the same – Arunas Bartisius Aug 21 '19 at 14:20
28

Git does not have the same concept of revision numbers as subversion. Instead each given snapshot made with a commit is tagged by a SHA1 checksum. Why? There are several problems with a running revno in a distributed version control system:

First, since development is not linear at all, the attachment of a number is rather hard as a problem to solve in a way which will satisfy your need as a programmer. Trying to fix this by adding a number might quickly become problematic when the number does not behave as you expect.

Second, revision numbers may be generated on different machines. This makes synchronization of numbers much harder - especially since connectivity is one-way; you may not even have access to all machines that has the repository.

Third, in git, somewhat pioneered by the now defunct OpenCM system, the identity of a commit (what the commit is) is equivalent to its name (the SHA id). This naming = identity concept is very strong. When you sit with a commit name in hand it also identifies the commit in an unforgeable way. This in turn lets you check all of your commits back to the first initial one for corruption with the git fsck command.

Now, since we have a DAG (Directed Acyclic Graph) of revisions and these constitute the current tree, we need some tools to solve your problem: How do we discriminate different versions. First, you can omit part of the hash if a given prefix, 1516bd say, uniquely identifies your commit. But this is also rather contrived. Instead, the trick is to use tags and or branches. A tag or branch is akin to a "yellow stick it note" you attach to a given commit SHA1-id. Tags are, in essence, meant to be non-moving whereas a branch will move when new commits are made to its HEAD. There are ways to refer to a commit around a tag or branch, see the man page of git-rev-parse.

Usually, if you need to work on a specific piece of code, that piece is undergoing changes and should as such be a branch with a saying topic name. Creating lots of branches (20-30 per programmer is not unheard of, with some 4-5 published for others to work on) is the trick for effective git. Every piece of work should start as its own branch and then be merged in when it is tested. Unpublished branches can be rewritten entirely and this part of destroying history is a force of git.

When the change is accepted into master it somewhat freezes and becomes archeology. At that point, you can tag it, but more often a reference to the particular commit is made in a bug tracker or issue tracker via the sha1 sum. Tags tend to be reserved for version bumps and branch points for maintenance branches (for old versions).

Thomas Weller
  • 43,638
  • 16
  • 101
  • 185
I GIVE CRAP ANSWERS
  • 18,279
  • 2
  • 40
  • 46
19

If you're interested, I managed version numbers automatically from git infos here under the format

<major>.<minor>.<patch>-b<build>

where build is the total number of commits. You'll see the interesting code in the Makefile. Here is the relevant part to access the different part of the version number:

LAST_TAG_COMMIT = $(shell git rev-list --tags --max-count=1)
LAST_TAG = $(shell git describe --tags $(LAST_TAG_COMMIT) )
TAG_PREFIX = "latex-tutorial-v"

VERSION  = $(shell head VERSION)
# OR try to guess directly from the last git tag
#VERSION    = $(shell  git describe --tags $(LAST_TAG_COMMIT) | sed "s/^$(TAG_PREFIX)//")
MAJOR      = $(shell echo $(VERSION) | sed "s/^\([0-9]*\).*/\1/")
MINOR      = $(shell echo $(VERSION) | sed "s/[0-9]*\.\([0-9]*\).*/\1/")
PATCH      = $(shell echo $(VERSION) | sed "s/[0-9]*\.[0-9]*\.\([0-9]*\).*/\1/")
# total number of commits       
BUILD      = $(shell git log --oneline | wc -l | sed -e "s/[ \t]*//g")

#REVISION   = $(shell git rev-list $(LAST_TAG).. --count)
#ROOTDIR    = $(shell git rev-parse --show-toplevel)
NEXT_MAJOR_VERSION = $(shell expr $(MAJOR) + 1).0.0-b$(BUILD)
NEXT_MINOR_VERSION = $(MAJOR).$(shell expr $(MINOR) + 1).0-b$(BUILD)
NEXT_PATCH_VERSION = $(MAJOR).$(MINOR).$(shell expr $(PATCH) + 1)-b$(BUILD)
Sebastien Varrette
  • 3,716
  • 1
  • 21
  • 19
10

A Bash function:

git_rev ()
{
    d=`date +%Y%m%d`
    c=`git rev-list --full-history --all --abbrev-commit | wc -l | sed -e 's/^ *//'`
    h=`git rev-list --full-history --all --abbrev-commit | head -1`
    echo ${c}:${h}:${d}
}

outputs something like

$ git_rev
2:0f8e14e:20130220

That is

commit_count:last_abbrev_commit:date_YYmmdd
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
siznax
  • 434
  • 4
  • 7
  • That sort of thing could be useful, but if one is interested in incremental version numbers, they would swap the field positions so the (non-incremental) hash is last. – MarkHu Feb 09 '16 at 01:13
9

The SHA1 hash of the commit is the equivalent to a Subversion revision number.

Richard Fearn
  • 23,275
  • 6
  • 55
  • 54
  • 8
    Unfortunately it has quite different properties from a revision number. It's rather long, and it doesn't increase monotonously. Guess that's the price to pay for distribution... – CodesInChaos Nov 07 '10 at 22:27
  • 1
    @CodeInChaos: I know what you are getting at, but it is generally possible to refer to git commits with only the first 6 or 8 characters of the hash code. – Chris Pitman Nov 07 '10 at 23:03
  • @CIC: it's long, but you don't have to use the whole hash, first several characters (4 in small repositories) are enough. – svick Nov 07 '10 at 23:13
  • 5
    @Radek: They are not guaranteed unique (though if you find a collision you can win some small amount of celebrity). – Slartibartfast Nov 08 '10 at 00:32
  • cool, so could somebody tell me what 'small repository' mean? – Radek Nov 08 '10 at 00:38
  • 8
    @Radek According to the http://en.wikipedia.org/wiki/Collision_attack, with 4 chars of the hash you have a 16bit Id, which means that in a repo with 256(=2^(16/2)) commits there is a 50% chance that two commits have the same four-char-prefix (and with 65536 commits it is certain, since then the range of 4-char Ids is exhausted). When you add one char, you have a 20 bit Id, which meant that the 50% threshold is at 1024 commits. But since this is a statistical parameter, nothing guarantees that such collisions doesn't happen earlier. – Rudi Nov 08 '10 at 08:00
  • 2
    Since the hash is based on the contents, it's still a probability whether two commits have the same hash prefix, not a certainty. At 65536 commits it's very likely that two would have the same four-char-prefix, but it's still not certain. As an aside, the full hash doesn't have a collision yet, but git is working on it :) http://stackoverflow.com/questions/3475648/sha1-collision-demo-example#comment6046971_3476791 – goodeye May 19 '13 at 00:55
7

This is what I did in my makefile based on others solutions. Note not only does this give your code a revision number, it also appends the hash which allows you to recreate the release.

# Set the source control revision similar to subversion to use in 'c'
# files as a define.
# You must build in the master branch otherwise the build branch will
# be prepended to the revision and/or "dirty" appended. This is to
# clearly ID developer builds.
REPO_REVISION_:=$(shell git rev-list HEAD --count)
BUILD_BRANCH:=$(shell git rev-parse --abbrev-ref HEAD)
BUILD_REV_ID:=$(shell git rev-parse HEAD)
BUILD_REV_ID_SHORT:=$(shell git describe --long --tags --dirty --always)
ifeq ($(BUILD_BRANCH), master)
REPO_REVISION:=$(REPO_REVISION_)_g$(BUILD_REV_ID_SHORT)
else
REPO_REVISION:=$(BUILD_BRANCH)_$(REPO_REVISION_)_r$(BUILD_REV_ID_SHORT)
endif
export REPO_REVISION
export BUILD_BRANCH
export BUILD_REV_ID
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
  • 3
    It seems the safest way to use `git-describe` to avoid errors, is with `git describe --always --dirty --long --tags` which works in all cases I can think of. – MarkHu Feb 12 '16 at 19:52
6

The problem with using the git hash as the build number is that it's not monotonically increasing. OSGi suggests using a time-stamp for the build number. It looks like the number of commits to the branch could be used in place of the subversion or perforce change number.

Jim Belton
  • 221
  • 2
  • 8
6

I wrote some PowerShell utilities for retrieving version information from Git and simplifying tagging

functions: Get-LastVersion, Get-Revision, Get-NextMajorVersion, Get-NextMinorVersion, TagNextMajorVersion, TagNextMinorVersion:

# Returns the last version by analysing existing tags,
# assumes an initial tag is present, and
# assumes tags are named v{major}.{minor}.[{revision}]
#
function Get-LastVersion(){
  $lastTagCommit = git rev-list --tags --max-count=1
  $lastTag = git describe --tags $lastTagCommit
  $tagPrefix = "v"
  $versionString = $lastTag -replace "$tagPrefix", ""
  Write-Host -NoNewline "last tagged commit "
  Write-Host -NoNewline -ForegroundColor "yellow" $lastTag
  Write-Host -NoNewline " revision "
  Write-Host -ForegroundColor "yellow" "$lastTagCommit"
  [reflection.assembly]::LoadWithPartialName("System.Version")

  $version = New-Object System.Version($versionString)
  return $version;
}

# Returns current revision by counting the number of commits to HEAD
function Get-Revision(){
   $lastTagCommit = git rev-list HEAD
   $revs  = git rev-list $lastTagCommit |  Measure-Object -Line
   return $revs.Lines
}

# Returns the next major version {major}.{minor}.{revision}
function Get-NextMajorVersion(){
    $version = Get-LastVersion;
    [reflection.assembly]::LoadWithPartialName("System.Version")
    [int] $major = $version.Major+1;
    $rev = Get-Revision
    $nextMajor = New-Object System.Version($major, 0, $rev);
    return $nextMajor;
}

# Returns the next minor version {major}.{minor}.{revision}
function Get-NextMinorVersion(){
    $version = Get-LastVersion;
    [reflection.assembly]::LoadWithPartialName("System.Version")
    [int] $minor = $version.Minor+1;
    $rev = Get-Revision
    $next = New-Object System.Version($version.Major, $minor, $rev);
    return $next;
}

# Creates a tag with the next minor version
function TagNextMinorVersion($tagMessage){
    $version = Get-NextMinorVersion;
    $tagName = "v{0}" -f "$version".Trim();
    Write-Host -NoNewline "Tagging next minor version to ";
    Write-Host -ForegroundColor DarkYellow "$tagName";
    git tag -a $tagName -m $tagMessage
}

# Creates a tag with the next major version (minor version starts again at 0)
function TagNextMajorVersion($tagMessage){
    $version = Get-NextMajorVersion;
    $tagName = "v{0}" -f "$version".Trim();
    Write-Host -NoNewline "Tagging next majo version to ";
    Write-Host -ForegroundColor DarkYellow "$tagName";
    git tag -a $tagName -m $tagMessage
}
Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
toeb
  • 405
  • 5
  • 9
5

Each commit has a unique hash. Other than that there are no revision numbers in git. You'll have to tag commits yourself if you want more user-friendliness.

Core Xii
  • 5,871
  • 4
  • 27
  • 41
4

I'd just like to note another possible approach - and that is by using git git-notes(1), in existence since v 1.6.6 (Note to Self - Git) (I'm using git version 1.7.9.5).

Basically, I used git svn to clone an SVN repository with linear history (no standard layout, no branches, no tags), and I wanted to compare revision numbers in the cloned git repository. This git clone doesn't have tags by default, so I cannot use git describe. The strategy here likely would work only for linear history - not sure how it would turn out with merges etc.; but here is the basic strategy:

  • Ask git rev-list for list of all commit history
    • Since rev-list is by default in "reverse chronological order", we'd use its --reverse switch to get list of commits sorted by oldest first
  • Use bash shell to
    • increase a counter variable on each commit as a revision counter,
    • generate and add a "temporary" git note for each commit
  • Then, browse the log by using git log with --notes, which will also dump a commit's note, which in this case would be the "revision number"
  • When done, erase the temporary notes (NB: I'm not sure if these notes are committed or not; they don't really show in git status)

First, let's note that git has a default location of notes - but you can also specify a ref(erence) for notes - which would store them in a different directory under .git; for instance, while in a git repo folder, you can call git notes get-ref to see what directory that will be:

$ git notes get-ref
refs/notes/commits
$ git notes --ref=whatever get-ref
refs/notes/whatever

The thing to be noted is that if you notes add with a --ref, you must also afterwards use that reference again - otherwise you may get errors like "No note found for object XXX...".

For this example, I have chosen to call the ref of the notes "linrev" (for linear revision) - this also means it is not likely the procedure will interfere with already existing notes. I am also using the --git-dir switch, since being a git newbie, I had some problems understanding it - so I'd like to "remember for later" :); and I also use --no-pager to suppress spawning of less when using git log.

So, assuming you're in a directory, with a subfolder myrepo_git which is a git repository; one could do:

### check for already existing notes:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.

### iterate through rev-list three, oldest first,
### create a cmdline adding a revision count as note to each revision

$ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \
  TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \
  TCMD="$TCMD add $ih -m \"(r$((++ix)))\""; \
  echo "$TCMD"; \
  eval "$TCMD"; \
done

# git --git-dir=./myrepo_git/.git notes --ref linrev add 6886bbb7be18e63fc4be68ba41917b48f02e09d7 -m "(r1)"
# git --git-dir=./myrepo_git/.git notes --ref linrev add f34910dbeeee33a40806d29dd956062d6ab3ad97 -m "(r2)"
# ...
# git --git-dir=./myrepo_git/.git notes --ref linrev add 04051f98ece25cff67e62d13c548dacbee6c1e33 -m "(r15)"

### check status - adding notes seem to not affect it:

$ cd myrepo_git/
$ git status
# # On branch master
# nothing to commit (working directory clean)
$ cd ../

### check notes again:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# (r15)

### note is saved - now let's issue a `git log` command, using a format string and notes:

$ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD
# 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)
# 77f3902: _user_: Sun Apr 21 18:29:00 2013 +0000:  >>test message 14<< (r14)
# ...
# 6886bbb: _user_: Sun Apr 21 17:11:52 2013 +0000:  >>initial test message 1<< (r1)

### test git log with range:

$ git --git-dir=./myrepo_git/.git --no-pager log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
# 04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)

### erase notes - again must iterate through rev-list

$ ix=0; for ih in $(git --git-dir=./myrepo_git/.git rev-list --reverse HEAD); do \
  TCMD="git --git-dir=./myrepo_git/.git notes --ref linrev"; \
  TCMD="$TCMD remove $ih"; \
  echo "$TCMD"; \
  eval "$TCMD"; \
done
# git --git-dir=./myrepo_git/.git notes --ref linrev remove 6886bbb7be18e63fc4be68ba41917b48f02e09d7
# Removing note for object 6886bbb7be18e63fc4be68ba41917b48f02e09d7
# git --git-dir=./myrepo_git/.git notes --ref linrev remove f34910dbeeee33a40806d29dd956062d6ab3ad97
# Removing note for object f34910dbeeee33a40806d29dd956062d6ab3ad97
# ...
# git --git-dir=./myrepo_git/.git notes --ref linrev remove 04051f98ece25cff67e62d13c548dacbee6c1e33
# Removing note for object 04051f98ece25cff67e62d13c548dacbee6c1e33

### check notes again:

$ git --git-dir=./myrepo_git/.git notes show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.
$ git --git-dir=./myrepo_git/.git notes --ref=linrev show
# error: No note found for object 04051f98ece25cff67e62d13c548dacbee6c1e33.

So, at least in my specific case of fully linear history with no branches, the revision numbers seem to match with this approach - and additionally, it seems that this approach will allow using git log with revision ranges, while still getting the right revision numbers - YMMV with a different context, though...

Hope this helps someone,
Cheers!


EDIT: Ok, here it is a bit easier, with git aliases for the above loops, called setlinrev and unsetlinrev; when in your git repository folder, do (Note the nasty bash escaping, see also #16136745 - Add a Git alias containing a semicolon):

cat >> .git/config <<"EOF"
[alias]
  setlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\
      TCMD=\"git notes --ref linrev\"; \n\
      TCMD=\"$TCMD add $ih -m \\\"(r\\$((++ix)))\\\"\"; \n\
      #echo \"$TCMD\"; \n\
      eval \"$TCMD\"; \n\
    done; \n\
    echo \"Linear revision notes are set.\" '"

  unsetlinrev = "!bash -c 'ix=0; for ih in $(git rev-list --reverse HEAD); do \n\
      TCMD=\"git notes --ref linrev\"; \n\
      TCMD=\"$TCMD remove $ih\"; \n\
      #echo \"$TCMD\"; \n\
      eval \"$TCMD 2>/dev/null\"; \n\
    done; \n\
    echo \"Linear revision notes are unset.\" '"
EOF

... so you can simply invoke git setlinrev before trying to do log involving linear revision notes; and git unsetlinrev to delete those notes when you're done; an example from inside the git repo directory:

$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 <<

$ git setlinrev
Linear revision notes are set.
$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 << (r15)
$ git unsetlinrev
Linear revision notes are unset.

$ git log --notes=linrev --format=format:"%h: %an: %ad:  >>%s<< %N" HEAD^..HEAD
04051f9: _user_: Sun Apr 21 18:29:02 2013 +0000:  >>test message 15 <<

The time it would take the shell to complete these aliases, would depend on the size of the repository history.

Community
  • 1
  • 1
sdaau
  • 32,015
  • 34
  • 178
  • 244
3

For people who have an Ant build process, you can generate a version number for a project on git with this target:

<target name="generate-version">

    <exec executable="git" outputproperty="version.revisions">
        <arg value="log"/>
        <arg value="--oneline"/>
    </exec>

    <resourcecount property="version.revision" count="0" when="eq">
        <tokens>
            <concat>
                <filterchain>
                    <tokenfilter>
                        <stringtokenizer delims="\r" />
                    </tokenfilter>
                </filterchain>
            <propertyresource name="version.revisions" />
            </concat>
        </tokens>
    </resourcecount>
    <echo>Revision : ${version.revision}</echo>

    <exec executable="git" outputproperty="version.hash">
        <arg value="rev-parse"/>
        <arg value="--short"/>
        <arg value="HEAD"/>
    </exec>
    <echo>Hash : ${version.hash}</echo>


    <exec executable="git" outputproperty="version.branch">
        <arg value="rev-parse"/>
        <arg value="--abbrev-ref"/>
        <arg value="HEAD"/>
    </exec>
    <echo>Branch : ${version.branch}</echo>

    <exec executable="git" outputproperty="version.diff">
        <arg value="diff"/>
    </exec>

    <condition property="version.dirty" value="" else="-dirty">
        <equals arg1="${version.diff}" arg2=""/>
    </condition>

    <tstamp>
        <format property="version.date" pattern="yyyy-mm-dd.HH:mm:ss" locale="en,US"/>
    </tstamp>
    <echo>Date : ${version.date}</echo>

    <property name="version" value="${version.revision}.${version.hash}.${version.branch}${version.dirty}.${version.date}" />

    <echo>Version : ${version}</echo>

    <echo file="version.properties" append="false">version = ${version}</echo>

</target>

The result looks like this:

generate-version:
    [echo] Generate version
    [echo] Revision : 47
    [echo] Hash : 2af0b99
    [echo] Branch : master
    [echo] Date : 2015-04-20.15:04:03
    [echo] Version : 47.2af0b99.master-dirty.2015-04-20.15:04:03

The dirty flag is here when you have file(s) not committed when you generate the version number. Because usually, when you build/package your application, every code modification has to be in the repository.

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
Fredo
  • 31
  • 2
3

We're using this command to get version and revision from git:

git describe --always --tags --dirty

It returns

  • commit hash as revision when no tagging is used (e.g. gcc7b71f)
  • tag name as version when on a tag (e.g. v2.1.0, used for releases)
  • tag name, revision number since last tag and commit hash when after a tag (e.g. v5.3.0-88-gcc7b71f)
  • same as above plus a "dirty" tag if the working tree has local modifications (e.g. v5.3.0-88-gcc7b71f-dirty)

See also: https://www.git-scm.com/docs/git-describe#Documentation/git-describe.txt

thisismydesign
  • 8,745
  • 4
  • 63
  • 58
2

Along with the SHA-1 id of the commit, date and time of the server time would have helped?

Something like this:

commit happened at 11:30:25 on 19 aug 2013 would show as 6886bbb7be18e63fc4be68ba41917b48f02e09d7_19aug2013_113025

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
2

From the Git manual, tags are a brilliant answer to this issue:

Creating an annotated tag in Git is simple. The easiest way is to specify -a when you run the tag command:

$ git tag -a v1.4 -m 'my version 1.4'

$ git tag
v0.1
v1.3
v1.4

Check out 2.6 Git Basics - Tagging

Peter Mortensen
  • 28,342
  • 21
  • 95
  • 123
DJ Far
  • 438
  • 4
  • 12
  • Too bad you have to jump through hoops to change the defaults: ```By default, the git push command doesn’t transfer tags to remote servers.``` – MarkHu Feb 08 '16 at 21:24
0

Post build event for Visual Studio

echo  >RevisionNumber.cs static class Git { public static int RevisionNumber =
git  >>RevisionNumber.cs rev-list --count HEAD
echo >>RevisionNumber.cs ; }
Polluks
  • 350
  • 4
  • 13
-2

Consider to use

git-rev-label

Gives information about Git repository revision in format like master-c73-gabc6bec. Can fill template string or file with environment variables and information from Git. Useful to provide information about version of the program: branch, tag, commit hash, commits count, dirty status, date and time. One of the most useful things is count of commits, not taking into account merged branches - only first parent.

kyb
  • 4,784
  • 4
  • 30
  • 64