What is the most robust way to get the name of the reference last pointed to by HEAD?

107 views Asked by At

After an operation that moves HEAD (such as checkout, reset, etc.), you can always get the commit ID that HEAD pointed to before that operation by running, for instance,

git rev-parse @{1}

What I'm interested in, though, is getting the name of the reference (if any) that HEAD last pointed to. Here's an example that illustrates what I want. Let's say my repo looks as follows:

enter image description here

Then I check out the develop branch by running

git checkout develop

and end up with

enter image description here

How can I retrieve the information, from my repo's entrails, that HEAD was pointing at master, before that last checkout operation?

This answer suggests extracting the name from the reflog with awk as a possibility; for instance, with

git reflog -1 | awk '{ print $6; exit }'

(thanks to Ed and Etan for their suggestions).

As far as I can tell, that works well enough. It even prints the SHA of the previous commit, in case HEAD was detached before the last checkout.

However, the OP raises concerns about robustness and backward compatibility in his comment:

I am marking this answer as correct (which it is, technically), for want of a cleaner approach. However, I am not convinced that the string's format being hard-coded into Git's source code is a good thing, as it means it could break unexpectedly (i.e. in a later version of Git).

Are the OP's concerns legitimate? What is the most robust way to do that?

2

There are 2 answers

13
Etan Reisner On BEST ANSWER

In some quick tests this seems to work fairly well (though the tests were not at all thorough or stressing of weird scenarios):

git rev-parse --symbolic-full-name @{-1}

I was in the middle of posting a comment on @EmilDavtyan's answer saying that the multi-ref issue is problematic since I don't think git cares (and so doesn't track) the last location of the HEAD ref but then I remembered @{-1} and how that would be of much less use if it couldn't handle this situation and so after a quick test it seems that it does somehow (it might very well be parsing the message in the reflog for all that I know).

I should also point out that the linked OP is correct that manually parsing the reflog is not reliable. In fact when I went to test the awk snippet here I didn't get the correct results. Turns out that is because I have --decorate on by default which was padding the line with extra fields and throwing off the count.

2
Emil Davtyan On

I would just use :

git branch --contains HEAD@{1}

It will give you the branches that contain the previously checked out commit.


Did a little test using @Etan's answer and I don't think it is parsing the message in the git reflog for the branch it is just looking it up :

emil ~ git checkout -b temp2
Switched to a new branch 'temp2'
emil ~ git rev-parse --symbolic-full-name @{-1}
refs/heads/temp
emil ~ git branch -d temp
Deleted branch temp (was f3163f9).
emil ~ git rev-parse --symbolic-full-name @{-1}
@{-1}
fatal: ambiguous argument '@{-1}': unknown revision or path not in the working tree.
Use '--' to separate paths from revisions, like this:
'git <command> [<revision>...] -- [<file>...]'

So if the branch is removed or changed via commit, I think the method will fail.