2626

How do I get the hash of the current commit in Git?

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Sardaukar
  • 29,034
  • 5
  • 26
  • 32
  • 3
    Use `git log` to retrieve recent commits, that will show full commit hash – onmyway133 Mar 15 '22 at 14:23
  • Useful question! I was able to use this in a build script: `"record_commit_hash_and_build_time": "now=$(date -u \"+%Y-%m-%d %H:%M:%S\") && last_commit=$(git rev-parse HEAD) && echo \"{\\\"commit\\\": \\\"$last_commit\\\", \\\"build_time\\\": \\\"$now\\\"}\" > frontend/dist/version.json",` https://stackoverflow.com/a/11493416/470749 – Ryan Nov 29 '22 at 18:53

25 Answers25

3827

To turn any extended object reference into a hash, use git-rev-parse:

git rev-parse HEAD

or

git rev-parse --verify HEAD

To retrieve the short hash:

git rev-parse --short HEAD

To turn references (e.g. branches and tags) into hashes, use git show-ref and git for-each-ref.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
Jakub Narębski
  • 309,089
  • 65
  • 217
  • 230
  • 101
    `--verify` implies that: `The parameter given must be usable as a single, valid object name. Otherwise barf and abort.` – Linus Unnebäck Jul 24 '11 at 17:50
  • 700
    `git rev-parse --short HEAD` returns the short version of the hash, just in case anyone was wondering. – Thane Brimhall Oct 25 '12 at 21:28
  • 67
    Adding to what Thane said, you can also add a specific length to `--short`, such as `--short=12`, to get a specific number of digits from the hash. – Tyson Phalp Feb 21 '14 at 17:18
  • 45
    @TysonPhalp: `--short=N` is about **minimal** number of digits; git uses larger number of digits if shortened one would be undistinguishable from shortened other commit. Try e.g. `git rev-parse --short=2 HEAD` or `git log --oneline --abbrev=2`. – Jakub Narębski Feb 21 '14 at 18:08
  • 45
    Adding to what Thane, Tyson, and Jakub said, you can print the full hash, but highlight the hexits necessary to identify the commit blue with `git rev-parse HEAD | GREP_COLORS='ms=34;1' grep $(git rev-parse --short=0 HEAD)` – Zaz Aug 05 '14 at 16:44
  • Neat trick Zaz. It doesn't seem to work with the grep on my Mac, but I substituted `ack` for a similar effect! – XP84 Jul 24 '15 at 15:14
  • 11
    @Zaz That didn't work for me either, but simply `git rev-parse HEAD | grep --color $(git rev-parse --short=0 HEAD)` does. – Theodore Murdock Dec 31 '15 at 01:47
  • 1
    Is there a way to do this but on a per-directory basis? For example, lets say I want the latest hash of a particular directory. Do I need to do a `git log ` and parse it out to grab the hash? – Aaron May 10 '16 at 18:23
  • 1
    @Aaron: roughly speaking yes, you need to run `git log -- `; but you can configure git-log so it returns only hash. – Jakub Narębski May 10 '16 at 23:53
  • 5
    Adding to what @ThaneBrimhall said, you can get the full SHA-1 reference from the short version of the hash using `git rev-parse ca003ae`. – Halil May 24 '16 at 13:14
  • Does this way work if you have a tag named "HEAD"? >:) – Sap Aug 03 '16 at 06:49
  • 1
    @EdRandall: `git rev-parse` main goal is to parse parameters and return revisions, so `--short` means shortened SHA-1 of revision. `git log` return more information, which includes SHA-1 of revisions, that can be abbreviated with `--abbrev` or `--abbrev-commit`. – Jakub Narębski Sep 07 '16 at 14:23
  • `git rev-parse HEAD | pbcopy` is great for quick clipboard’ing on mac – Kirk Strobeck Sep 07 '17 at 00:17
  • 2
    Cool! I have even added it to alias: `git config --global alias.sha 'rev-parse HEAD'`. Now I can simply use `git sha`. – milkovsky Jan 11 '18 at 15:50
  • 2
    This is answer is not always correct! If the HEAD is not at the your checkout commit (detached head) what you get back is incorrect. Only `git status` will give you the correct commit hash. – sschueller Jun 05 '18 at 14:28
  • 2
    I find this doesn't work with non-annotated tags. If you have a non-annotated tag pointing to head, `git rev-parse --short HEAD` _should_ give you the same result as `git show-ref -s7 ` and it doesn't I got two different hashes when doing this. This is because lightweight tags will only show their ref hash not the hash of the commit they are pointing to. You need to do `git rev-parse --short '^{}'` for that. – Cheruvim Aug 17 '20 at 19:44
  • When do I need `--verify`? – xjcl Dec 10 '20 at 13:38
  • 1
    @xjcl the `--verify` option is useful if you use `git rev-parse` together with something variable, like `git rev-parse --verify $branch`. For `git rev-parse --verify HEAD` it ensures that HEAD can be turned into SHA-1 / object identifier (and for example you are not on an unborn branch). – Jakub Narębski Dec 10 '20 at 16:38
  • If you pass this to pbcopy on mac to copy it into the clipboard, you'll get a new line at the end. To get rid of it, use `git rev-parse HEAD | tr -d '\n' | pbcopy` – squall3d May 18 '21 at 01:01
  • Small caveat for `git rev-parse xxx`: This results in `SHA + \n`, so it adds a new line at the very end. So if you want to get just the SHA, be sure to trim your result – Johannes Deml Jun 20 '22 at 14:38
  • Let's say that `git rev-parse --short HEAD` shows `4f5d5b9`, but `git status` is dirty. How can I make it show `4f5d5b9-dirty`, instead? – Gabriel Staples Aug 08 '23 at 01:39
  • I figured it out! [Here is my answer](https://stackoverflow.com/a/76856090/4561887) on how to print `4f5d5b9` if `git status` is clean, and `4f5d5b9-dirty` if it is dirty. – Gabriel Staples Aug 08 '23 at 02:09
542

To get the shortened commit hash, use the %h format specifier:

git log --pretty=format:'%h' -n 1

%H represents the long commit hash. Also, -1 can be used directly in place of -n 1.

Mateen Ulhaq
  • 24,552
  • 19
  • 101
  • 135
outofculture
  • 6,023
  • 1
  • 18
  • 20
  • 117
    Or, it seems, adding --short to the rev-parse command above seems to work. – outofculture Sep 30 '11 at 23:39
  • 24
    I think `git log` is porcelain and `git rev-parse` is plumbing. – Amedee Van Gasse Jan 29 '16 at 10:40
  • One of the benefits of this method is that it will return the short version of the hash with the right length adjusted to the hash collisions which happen for larger repos. At least in recent versions of git. – Ilia Sidorenko Dec 22 '16 at 14:26
  • 5
    This is a bad/ incorrect way of doing it because this method will give you the wrong hash if you have a detached head. For example if the current commit is 12ab34... and the previous commit was 33aa44... then if i do 'git checkout 33aa44' and then I run your command I will still be getting back 12ab34... despite my head actually pointing to 33aa44... – theQuestionMan Jul 17 '17 at 00:03
  • 5
    @theQuestionMan I don't experience the behavior you describe; `git checkout 33aa44; git log -n 1` gives me `33aa44`. What version of git are you using? – outofculture Jul 19 '17 at 17:32
  • 1
    @AmedeeVanGasse if `git log` is porcelain and `git rev-parse` is plumbing, what is `git status --porcelain`? :P – Patronics Jan 14 '21 at 20:32
  • @PatrickL I don't know, this is a question from 4 years ago and I don't feel like going back to something that's behind me. I recommend that you open a new question on Stack Overflow. – Amedee Van Gasse Jan 15 '21 at 02:48
  • 14
    @AmedeeVanGasse, ah! I HAD NO IDEA this is a toilet analogy! I've been seeing `porcelain` in the `git` `man` pages for years, but had NO idea it was referring to a toilet! The porcelain is the toilet, and it's "closer to the user" (who figuratively sits on this toilet) than the plumbing, which is lower-level and farther from the user--ie: below the "porcelain"! Mind blown. – Gabriel Staples Feb 21 '21 at 07:26
  • 2
    @PatrickL, I'd say `git status` is the plastic toilet seat. It's closer to the user than the porcelain, where "porcelain" refers to the porcelain bowl under the toilet seat. So, `git status` and `git log` are plastic. `git log --pretty=format` is porcelain, `git status --porcelain` is also porcelain, and `git rev-parse` is plumbing. – Gabriel Staples Feb 21 '21 at 07:29
  • 1
    Full pretty format options: https://git-scm.com/docs/pretty-formats#Documentation/pretty-formats.txt-emHem – cdalxndr Mar 30 '21 at 18:10
191

Another one, using git log:

git log -1 --format="%H"

It's very similar to the of @outofculture though a bit shorter.

Paul Pladijs
  • 18,628
  • 5
  • 28
  • 31
  • 1
    And the result is not single-quoted. – crokusek Feb 28 '17 at 23:11
  • 10
    This is the correct answer, since it works even if you checkout a specific commit instead of `HEAD`. – Parsa Feb 22 '19 at 19:16
  • 3
    @Parsa: when checking out a specific commit `HEAD` points to this commit rather than a named branche know as [detached head](https://git-scm.com/docs/git-checkout#_detached_head). – ChristofSenn Jan 28 '20 at 20:46
  • 2
    From the command line, to avoid pager: `git --no-pager log -1 --format="%H"` – ederag May 09 '21 at 20:27
  • @Parsa Your (mistakenly upvoted?) comment is misleading as the accepted answer of jakub-narębski 'git rev-parse HEAD' works after checking out somewhere else. – spawn May 12 '22 at 14:54
  • @Parsa - maybe we misunderstand each other: *after* checkout from commit a1b1 to c1d1 `git rev-parse HEAD` prints c1d1. So, HEAD points to the "current commit" which the OP asked for. From your upvoted comment I mistakenly concluded that `git rev-parse HEAD` was wrong. – spawn May 17 '22 at 12:31
155

To get the full SHA:

$ git rev-parse HEAD
cbf1b9a1be984a9f61b79a05f23b19f66d533537

To get the shortened version:

$ git rev-parse --short HEAD
cbf1b9a
Alexander
  • 105,104
  • 32
  • 201
  • 196
  • If two `git` `commit` hashes are needed, such as one from the `branch` you are currently working with and a `master` `branch`, you could also use `git rev-parse FETCH_HEAD` if you need the hash for the `master` `commit` that you `merge`d into your current `branch`. e.g. if you have `branch`es `master` and `feature/new-feature` for a given repo., while on `feature/new-feature` you could use `git fetch origin master && git merge FETCH_HEAD` and then `git rev-parse --short FETCH_HEAD` if you needed the `commit` hash from the `master` you just `merge`d in for any scripts you may have. – Mike Sep 03 '18 at 21:43
101

Commit hash

git show -s --format=%H

Abbreviated commit hash

git show -s --format=%h

The -s flag is same as --no-patch and stands for "Suppress diff output".

Click here for more git show examples.

Niko Föhr
  • 28,336
  • 10
  • 93
  • 96
ecwpz91
  • 1,527
  • 1
  • 13
  • 8
92

For completeness, since no one has suggested it yet. .git/refs/heads/master is a file that contains only one line: the hash of the latest commit on master. So you could just read it from there.

Or, as a command:

cat .git/refs/heads/master

Update:

Note that git now supports storing some head refs in the pack-ref file instead of as a file in the /refs/heads/ folder. https://www.kernel.org/pub/software/scm/git/docs/git-pack-refs.html

Pang
  • 9,564
  • 146
  • 81
  • 122
Deestan
  • 16,738
  • 4
  • 32
  • 48
  • 15
    This assumes the current branch is `master`, which is not necessarily true. – gavrie Oct 23 '12 at 15:10
  • 13
    Indeed. That's why I explicitly said this is for `master`. – Deestan Oct 23 '12 at 15:22
  • 1
    There is `.git/HEAD` which is a file that contains 1 line with the SHA1 of HEAD. I think that's what you meant. – kynan Nov 06 '12 at 20:39
  • 23
    `.git/HEAD` typically points to a ref, if you have a SHA1 in there, you are in detached head mode. – eckes Apr 09 '13 at 01:48
  • 11
    This isn't very robust compared to other approaches, in particular because it assumes that there is a `.git` subdirectory, which is not necessarily the case. See the `--separate-git-dir` flag in the `git init` man page. – jub0bs Dec 29 '14 at 17:44
  • 27
    +1 because sometimes you don't want git executable installed (e.g. in your Dockerfile) – wim Apr 07 '15 at 02:59
  • 3
    For finding the SHA- hash of a branch `.git/refs/heads/ ` – pushya Apr 07 '15 at 20:12
  • 2
    +1 -and- if you don't want to necessarily pull/rebase/merge and just want to get hash post-fetch `cat .git/refs/remotes/origin/master` – nhed May 24 '16 at 03:02
  • This is less robust because `.git` may always be a file containing the name of the real .git directory. – Carlo Wood Feb 02 '20 at 21:46
  • +1 because this may be faster and more straightforward as you don't have to call bash just to display the commit hash in the footer of the GUI. – jg6 May 05 '20 at 08:06
  • one step further `cat .git/refs/heads/$(git symbolic-ref --short HEAD)` – Richard Tyler Miles Jan 12 '23 at 18:31
61

There's always git describe as well. By default it gives you --

john@eleanor:/dev/shm/mpd/ncmpc/pkg (master)$ git describe --always
release-0.19-11-g7a68a75
kayleeFrye_onDeck
  • 6,648
  • 5
  • 69
  • 80
John Tyree
  • 1,069
  • 7
  • 14
  • 19
    Git describe returns the first TAG reachable from a commit. How does this help me get the SHA? – Sardaukar Sep 09 '11 at 13:45
  • 49
    I like `git describe --long --dirty --abbrev=10 --tags` it will give me something like `7.2.0.Final-447-g65bf4ef2d4` which is 447 commits after the 7.2.0.Final tag and the first 10 digest of the global SHA-1 at the current HEAD are "65bf4ef2d4". This is very good for version strings. With --long it will always add the count (-0-) and the hash, even if the tag happens to match exactly. – eckes Apr 09 '13 at 01:46
  • 19
    If no tags exist then `git describe --always` will "show uniquely abbreviated commit object as fallback" – Ronny Andersson Sep 18 '14 at 16:57
  • 3
    I use `git describe --tags --first-parent --abbrev=11 --long --dirty --always`. The `--always` option means it provides a result (hash) even if there are no tags. The `--first-parent` means it doesn't get confused by merge commits and only follows items on the current branch. Note also that `--dirty` will append `-dirty` to the result if the current branch has uncommitted changes. – ingyhere Jan 31 '20 at 07:05
39

Use git rev-list --max-count=1 HEAD

Robert Munteanu
  • 67,031
  • 36
  • 206
  • 278
31

If you need to store the hash in a variable during a script, you can use

last_commit=$(git rev-parse HEAD);

Or, if you only want the first 10 characters (like github.com does)

last_commit=$(git rev-parse --short=10 HEAD);
nik7
  • 806
  • 3
  • 12
  • 20
Henk
  • 556
  • 4
  • 5
  • Thanks. I was able to use this in a build script: `now=$(date -u "+%Y-%m-%d %H:%M:%S") && last_commit=$(git rev-parse HEAD) && echo "{\"commit\": \"$last_commit\", \"build_time\": \"$now\"}" > version.json` – Ryan Nov 29 '22 at 18:42
23

If you want the super-hacky way to do it:

cat .git/`cat .git/HEAD | cut -d \  -f 2`

Basically, git stores the location of HEAD in .git/HEAD, in the form ref: {path from .git}. This command reads that out, slices off the "ref: ", and reads out whatever file it pointed to.

This, of course, will fail in detached-head mode, as HEAD won't be "ref:...", but the hash itself - but you know, I don't think you expect that much smarts in your bash one-liners. If you don't think semicolons are cheating, though...

HASH="ref: HEAD"; while [[ $HASH == ref\:* ]]; do HASH="$(cat ".git/$(echo $HASH | cut -d \  -f 2)")"; done; echo $HASH
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Fordi
  • 2,798
  • 25
  • 20
  • 2
    no need to install git, I like it. (my docker build image does not have git) – Helin Wang Feb 15 '16 at 20:18
  • also useful because you can run this easily from outside the git repo – samaspin Jun 22 '16 at 16:30
  • 2
    I formalized this to a script for my local machine. Then, I thought, hey: the implementation I made are simple enough that it illustrates how to solve an unrelated problem (parsing arguments in raw POSIX shell scripts without external programs), but complex enough to provide a little variation and to exploit most of the features of `sh`. Half an hour of documentation comments later, and here's a Gist of it: https://gist.github.com/Fordi/29b8d6d1ef1662b306bfc2bd99151b07 – Fordi Jun 29 '16 at 14:41
  • 1
    Looking at it, I made a more extensive version for detecting Git and SVN, and grabbing the git hash/svn revision. Not a clean string this time, but easily command-line parsed, and usable as a version tag: https://gist.github.com/Fordi/8f1828efd820181f24302b292670b14e – Fordi Jun 29 '16 at 15:21
21

I needed something a little more different: display the full sha1 of the commit, but append an asterisk to the end if the working directory is not clean. Unless I wanted to use multiple commands, none of the options in the previous answers work.

Here is the one liner that does:
git describe --always --abbrev=0 --match "NOT A TAG" --dirty="*"
Result: f5366ccb21588c0d7a5f7d9fa1d3f85e9f9d1ffe*

Explanation: describes (using annotated tags) the current commit, but only with tags containing "NOT A TAG". Since tags cannot have spaces, this never matches a tag and since we want to show a result --always, the command falls back displaying the full (--abbrev=0) sha1 of the commit and it appends an asterisk if the working directory is --dirty.

If you don't want to append the asterisk, this works like all the other commands in the previous answers:
git describe --always --abbrev=0 --match "NOT A TAG"
Result: f5366ccb21588c0d7a5f7d9fa1d3f85e9f9d1ffe

Rado
  • 8,634
  • 7
  • 31
  • 44
  • Thanks, just stumbling over it and it spares me the one or other echo for that :) – hakre Feb 23 '18 at 20:43
  • 2
    It works for me without the `--match "NOT A TAG"`. Tested in git 2.18.0 as well as 2.7.4. Is there any situation where this argument is needed? – Thomas Aug 07 '18 at 07:24
  • 1
    @Thomas it won't work if you have an annotated tag anywhere in the history of the current commit. The fake tag makes sure that the describe command does not use a tag to describe the commit, – Rado Aug 08 '18 at 05:44
20

git rev-parse HEAD does the trick.

If you need to store it to checkout back later than saving actual branch if any may be preferable:

cat .git/HEAD

Example output:

ref: refs/heads/master

Parse it:

cat .git/HEAD | sed "s/^.\+ \(.\+\)$/\1/g"

If you have Windows then you may consider using wsl.exe:

wsl cat .git/HEAD | wsl sed "s/^.\+ \(.\+\)$/\1/g"

Output:

refs/heads/master

This value may be used to git checkout later but it becomes pointing to its SHA. To make it to point to the actual current branch by its name do:

wsl cat .git/HEAD | wsl sed "s/^.\+ \(.\+\)$/\1/g" | wsl sed "s/^refs\///g"  | wsl sed "s/^heads\///g"

Output:

master
sdgfsdh
  • 33,689
  • 26
  • 132
  • 245
Sergei Krivonos
  • 4,217
  • 3
  • 39
  • 54
18

Perhaps you want an alias so you don't have to remember all the nifty details. After doing one of the below steps, you will be able to simply type:

$ git lastcommit
49c03fc679ab11534e1b4b35687b1225c365c630

Following up on the accepted answer, here are two ways to set this up:

  1. Teach git the explicit way by editing the global config (my original answer):
# open the git config editor
$ git config --global --edit
# in the alias section, add
...
[alias]
  lastcommit = rev-parse HEAD
...
  1. Or if you like a shortcut to teach git a shortcut, as recently commented by Adrien:

    $ git config --global alias.lastcommit "rev-parse HEAD"

From here on, use git lastcommit to show the last commit's hash.

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
miraculixx
  • 10,034
  • 2
  • 41
  • 60
17

The most succinct way I know:

git show --pretty=%h 

If you want a specific number of digits of the hash you can add:

--abbrev=n
Brian Peterson
  • 2,800
  • 6
  • 29
  • 36
  • 16
    While this technically works, `git show` is what's known as a porcelain command (i.e. user-facing), and so should *not* be used in scripts because its output is subject to change. The answer above (`git rev-parse --short HEAD`) should be used instead. – jm3 Mar 15 '14 at 23:45
  • 5
    @jm3 that's backwards. "Porcelain" commands have stable outputs that are intended for scripts. Search `git help show` for `porcelain`. – John Tyree Jul 06 '15 at 21:32
  • 4
    @JohnTyree This is a confusing subject, but jm3 was right: porcelain commands are not meant to be parsed, but rather to be human-readable. In case you need to use a porcelain command in a script and you want to have a stable format, there's sometimes (for example with git status, push and blame) an option that does just that. Unfortunately, that option is called `--porcelain`, which is why this is confusing. You can find the details in [this great answer by VonC](https://stackoverflow.com/a/6978402/3982001) – Fabio says Reinstate Monica Jan 14 '19 at 17:20
12

Here is one-liner in Bash shell using direct read from git files:

(head=($(<.git/HEAD)); cat .git/${head[1]})

You need to run above command in your git root folder.

This method can be useful when you've repository files, but git command has been not installed.

If won't work, check in .git/refs/heads folder what kind of heads do you have present.

kenorb
  • 155,785
  • 88
  • 678
  • 743
9
git show-ref --head --hash head

If you're going for speed though, the approach mentioned by Deestan

cat .git/refs/heads/<branch-name>

is significantly faster than any other method listed here so far.

Community
  • 1
  • 1
Dennis
  • 56,821
  • 26
  • 143
  • 139
  • 1
    `show-ref` seems to me to be the best option for scripting, since it's a plumbing command and thus guaranteed (or at least very likely) to remain stable in future releases: other answers use `rev-parse`, `show`, `describe`, or `log`, which are all porcelain commands. And in cases where speed is *not* of the essence, the note from the `show-ref` manpage applies: ‘Use of this utility is encouraged in favor of directly accessing files under the .git directory.’ – Pont Oct 08 '18 at 18:21
7

in your home-dir in file ".gitconfig" add the following

[alias]
sha = rev-parse HEAD

then you will have an easier command to remember:

$ git sha
59fbfdbadb43ad0b6154c982c997041e9e53b600
jo_
  • 8,324
  • 1
  • 18
  • 14
5

On git bash, simply run $ git log -1

you will see, these lines following your command.

commit d25c95d88a5e8b7e15ba6c925a1631a5357095db .. (info about your head)

d25c95d88a5e8b7e15ba6c925a1631a5357095db, is your SHA for last commit.
Batty
  • 121
  • 1
  • 5
3

Pretty print of main git repo, and sub-modules:

echo "Main GIT repo:"
echo $(git show -s --format=%H) '(main)'
echo "Sub-modules:"
git submodule status | awk '{print $1,$2}'

Example output:

3a032b0992d7786b00a8822bbcbf192326160cf9 (main)
7de695d58f427c0887b094271ba1ae77a439084f sub-module-1
58f427c0887b01ba1ae77a439084947de695d27f sub-module-2
d58f427c0887de6957b09439084f4271ba1ae77a sub-module-3
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
leenremm
  • 1,083
  • 13
  • 19
2

How I would do it in python (based on @kenorb's bash answer)

def get_git_sha():
    # Which branch are we on?
    branch = open(".git/HEAD", "r").read()

    # Parse output "ref: refs/heads/my_branch" -> my_branch
    branch = branch.strip().split("/")[-1]

    # What's the latest commit in this branch?
    return open(f".git/refs/heads/{branch}").read().strip()
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
ignorant
  • 1,390
  • 1
  • 10
  • 14
2

Get the hash for the current commit, and see if git status is "clean" or "dirty"

I want to see 72361c8 or 72361c8-dirty for use in my build system version numbers injected into my program executables. Here is how:

Quick summary

# get a short commit hash, and see whether `git status` is clean or dirty
test -z "$(git status --porcelain)" \
    && echo "$(git rev-parse --short HEAD)" \
    || echo "$(git rev-parse --short HEAD)-dirty"

Sample output when git status is clean (there are no uncommitted changes):

72361c8

Sample output when git status is dirty (there are any uncommitted changes):

  • Meaning: there are any uncommitted changes, whether they are unstaged changes to tracked files, staged (git added) changes to tracked files, or the addition of new files
72361c8-dirty

Details

Just like git status will show when a submodule is "clean" or "dirty" when using Git Submodules, I really want to see my short commit hash with the word -dirty after it if git status is dirty! I'm using this inside my build system as a version number in my software, so I can easily see exactly which software version I'm running at any moment!

So, I combined this answer (git rev-parse --short HEAD) by @Jakub Narębski, and this answer: (test -n "$(git status --porcelain)") by @benzado on their answer to "Checking for a dirty index or untracked files with Git", I was able to come up with my "1-line" solution above.

The && part only runs if the return code to the previous command (git status --porcelain) is 0, meaning "success" (clean in this case), and the || part only runs if the return code of the previous command (git status --porcelain) is any other error code, meaning "error" (dirty in this case).

Going further

See my bash/git_get_short_hash.sh file in my eRCaGuy_hello_world repo.

Wrap it in a git_get_short_hash Bash function:

# Get a short commit hash, and see whether `git status` is clean or dirty.
# Example outputs:
# 1. Not in a git repo: `(not a git repo)`
# 2. In a repo which has a "dirty" `git status`: `72361c8-dirty`
#   - Note that "dirty" means there are pending uncommitted changes.
# 3. In a repo which has a "clean" `git status`: `72361c8`
git_get_short_hash() {
    # See: https://stackoverflow.com/a/16925062/4561887
    is_git_repo="$(git rev-parse --is-inside-work-tree)"

    if [ "$is_git_repo" != "true" ]; then
        echo "(not a git repo)"
        return $RETURN_CODE_SUCCESS
    fi

    # See my answer here: https://stackoverflow.com/a/76856090/4561887
    test -z "$(git status --porcelain)" \
        && echo "$(git rev-parse --short HEAD)" \
        || echo "$(git rev-parse --short HEAD)-dirty"
}

Even better, here's a whole program that you can run or source:

#!/usr/bin/env bash

# This file is part of eRCaGuy_hello_world: https://github.com/ElectricRCAircraftGuy/eRCaGuy_hello_world

RETURN_CODE_SUCCESS=0
RETURN_CODE_ERROR=1

# Get a short commit hash, and see whether `git status` is clean or dirty.
# Example outputs:
# 1. Not in a git repo: `(not a git repo)`
# 2. In a repo which has a "dirty" `git status`: `72361c8-dirty`
#   - Note that "dirty" means there are pending uncommitted changes.
# 3. In a repo which has a "clean" `git status`: `72361c8`
git_get_short_hash() {
    # See: https://stackoverflow.com/a/16925062/4561887
    is_git_repo="$(git rev-parse --is-inside-work-tree)"

    if [ "$is_git_repo" != "true" ]; then
        echo "(not a git repo)"
        return $RETURN_CODE_SUCCESS
    fi

    # See my answer here: https://stackoverflow.com/a/76856090/4561887
    test -z "$(git status --porcelain)" \
        && echo "$(git rev-parse --short HEAD)" \
        || echo "$(git rev-parse --short HEAD)-dirty"
}

main() {
    git_get_short_hash
}

# Determine if the script is being sourced or executed (run).
# See:
# 1. "eRCaGuy_hello_world/bash/if__name__==__main___check_if_sourced_or_executed_best.sh"
# 1. My answer: https://stackoverflow.com/a/70662116/4561887
if [ "${BASH_SOURCE[0]}" = "$0" ]; then
    # This script is being run.
    __name__="__main__"
else
    # This script is being sourced.
    __name__="__source__"
fi

# Only run `main` if this script is being **run**, NOT sourced (imported).
# - See my answer: https://stackoverflow.com/a/70662116/4561887
if [ "$__name__" = "__main__" ]; then
    main "$@"
fi

Sample run command and output when running it:

eRCaGuy_hello_world$ bash/git_get_short_hash.sh
fddb4ef-dirty

Sample run and output by sourcing it (see my answer here if you don't know what that means) and then running main or git_get_short_hash directly:

eRCaGuy_hello_world$ . bash/git_get_short_hash.sh
eRCaGuy_hello_world$ main
fddb4ef-dirty
eRCaGuy_hello_world$ git_get_short_hash
fddb4ef-dirty

For a Python version of the above program, see my file here: python/git_get_short_hash.py in my eRCaGuy_hello_world repo.

Example usage:

  • Use this function in another program of yours like this, for instance.
  • First, copy the git_get_short_hash.py script into another git project of yours.
  • Then, use it like this:
import git_get_short_hash
import textwrap

# Alias the function to a shorter name
git_get_short_hash = git_get_short_hash.git_get_short_hash3

git_short_hash = git_get_short_hash()
program_info_str = textwrap.dedent(f"""\
    My other program details here...
    Program version: {git_short_hash}
""")
print(program_info_str)
mylogfile.write(program_info_str)
Gabriel Staples
  • 36,492
  • 15
  • 194
  • 265
  • 1
    Nice use of the `git status --porcelain` [I described here](https://stackoverflow.com/a/6978402/6309). Upvoted. – VonC Aug 08 '23 at 05:51
1

Here is another direct-access implementation:

head="$(cat ".git/HEAD")"
while [ "$head" != "${head#ref: }" ]; do
  head="$(cat ".git/${head#ref: }")"
done

This also works over http which is useful for local package archives (I know: for public web sites it's not recommended to make the .git directory accessable):

head="$(curl -s "$baseurl/.git/HEAD")"
while [ "$head" != "${head#ref: }" ]; do
  head="$(curl -s "$baseurl/.git/${head#ref: }")"
done
Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Daniel Alder
  • 5,031
  • 2
  • 45
  • 55
0

Here is another way of doing it with :)

git log | grep -o '\w\{8,\}' | head -n 1
Marcelo Lazaroni
  • 9,819
  • 3
  • 35
  • 41
0
echo "printing last commit id# for current branch:";

git reflog

Image to show actul results

Benjamin Loison
  • 3,782
  • 4
  • 16
  • 33
Usman Ali Maan
  • 368
  • 2
  • 12
0

I wanted the newest commit on the origin/main branch so I use

git ls-remote origin | grep main$ | cut -f 1
mvndaai
  • 3,453
  • 3
  • 30
  • 34