ChatGPT解决这个技术问题 Extra ChatGPT

How to retrieve the hash for the current commit in Git?

I would like to retain (for now) the ability to link Git changesets to workitems stored in TFS.

I already wrote a tool (using a hook from Git) in which I can inject workitem identifiers into the message of a Git changeset.

I would also like to store the hash of the Git commit in a custom TFS workitem field. This way I can examine a workitem in TFS and see what Git changesets are associated with the workitem.

How can I easily retrieve the hash from the current commit from Git?

Use git log to retrieve recent commits, that will show full commit hash

m
maxcountryman

To turn arbitrary extended object reference into SHA-1, use simply git-rev-parse, for example

git rev-parse HEAD

or

git rev-parse --verify HEAD

You can also retrieve the short version like this

git rev-parse --short HEAD

Sidenote: If you want to turn references (branches and tags) into SHA-1, there is git show-ref and git for-each-ref.


--verify implies that: The parameter given must be usable as a single, valid object name. Otherwise barf and abort.
git rev-parse --short HEAD returns the short version of the hash, just in case anyone was wondering.
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.
@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.
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)
G
Gabriel Staples

If you only want the shortened commit hash:

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

Furthermore, using %H is another way to get the long commit hash, and simply -1 can be used in place of -n 1.


Or, it seems, adding --short to the rev-parse command above seems to work.
I think git log is porcelain and git rev-parse is plumbing.
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 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?
@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.
P
Paul Pladijs

Another one, using git log:

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

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


And the result is not single-quoted.
This is the correct answer, since it works even if you checkout a specific commit instead of HEAD.
@Parsa: when checking out a specific commit HEAD points to this commit rather than a named branche know as detached head.
From the command line, to avoid pager: git --no-pager log -1 --format="%H"
@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.
A
Alexander

To get the full SHA:

$ git rev-parse HEAD
cbf1b9a1be984a9f61b79a05f23b19f66d533537

To get the shortened version:

$ git rev-parse --short HEAD
cbf1b9a

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 merged into your current branch. e.g. if you have branches 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 merged in for any scripts you may have.
n
np8

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.


P
Pang

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


This assumes the current branch is master, which is not necessarily true.
Indeed. That's why I explicitly said this is for master.
.git/HEAD typically points to a ref, if you have a SHA1 in there, you are in detached head mode.
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.
+1 because sometimes you don't want git executable installed (e.g. in your Dockerfile)
k
kayleeFrye_onDeck

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

Git describe returns the first TAG reachable from a commit. How does this help me get the SHA?
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.
If no tags exist then git describe --always will "show uniquely abbreviated commit object as fallback"
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.
R
Robert Munteanu

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


git-rev-list is about generating list of commit objects; it is git-rev-parse to translate object name (e.g. HEAD) into SHA-1
n
nik7

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);

d
dave1010

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

no need to install git, I like it. (my docker build image does not have git)
also useful because you can run this easily from outside the git repo
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: gist.github.com/Fordi/29b8d6d1ef1662b306bfc2bd99151b07
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: gist.github.com/Fordi/8f1828efd820181f24302b292670b14e
R
Rado

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


Thanks, just stumbling over it and it spares me the one or other echo for that :)
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 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,
s
sdgfsdh

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

B
Brian Peterson

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

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 that's backwards. "Porcelain" commands have stable outputs that are intended for scripts. Search git help show for porcelain.
@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
C
Community

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

2) 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.


k
kenorb

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.


C
Community
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.


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.’
j
jo_

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

B
Batty

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.

l
leenremm

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

i
ignorant

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()

D
Daniel Alder

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

M
Marcelo Lazaroni

Here is another way of doing it with :)

git log | grep -o '\w\{8,\}' | head -n 1

U
Usman Ali Maan

echo "printing last commit id# for current branch:";

 git reflog

https://i.stack.imgur.com/B1t2F.jpg