2

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?

Community
  • 1
  • 1
jub0bs
  • 60,866
  • 25
  • 183
  • 186
  • 2
    I can't speak to the main point of your question, but never do `cmd | head -1 | awk '{ print $6 }'` as awk is perfectly capably of just operating on the first line of a file without the extra intermediate head and pipe: `cmd | awk 'NR==1{ print $6; exit }'`. – Ed Morton Sep 02 '14 at 14:07
  • 1
    @EdMorton Thanks Ed. I'm not very familiar with `awk` yet. I'll edit my question. – jub0bs Sep 02 '14 at 14:11
  • 1
    There's the additional detail here that `git-reflog` is perfectly capable of limiting its own output to a single line. `git reflog -1`. – Etan Reisner Sep 02 '14 at 14:35
  • @EtanReisner You're right. Silly me. – jub0bs Sep 02 '14 at 14:42

2 Answers2

2

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.

Etan Reisner
  • 77,877
  • 8
  • 106
  • 148
  • After a checkout of what from what? Also what version of git? It works here for branch switching checkouts. – Etan Reisner Sep 02 '14 at 15:05
  • Forget my last comment. This seems to be the answer. You might want to add that your command outputs nothing if `HEAD` was detached before the last checkout. – jub0bs Sep 02 '14 at 15:07
  • @EtanReisner What does `{-1}` refer to? I can't find documentation about it. – Emil Davtyan Sep 02 '14 at 15:28
  • Never mind found it https://www.kernel.org/pub/software/scm/git/docs/git-rev-parse.html – Emil Davtyan Sep 02 '14 at 15:29
  • @EtanReisner I don't think this is "parsing the message" it is simply looking it up in the data store, if the branch is removed or moved this will fail. – Emil Davtyan Sep 02 '14 at 15:45
  • @EmilDavtyan Looking *what* up in the data store? The reflog doesn't point to a ref it points to a commit. The commit doesn't have a one-to-one mapping to a given ref. I fully admit that I don't know where it is getting the information from and haven't had a chance to look. – Etan Reisner Sep 02 '14 at 16:47
  • @EtanReisner It is looking up the refs pointing to the commit, if there are none it fails. I'm just pointing out that it is not parsing the message in the reflog and can fail if the refs are changed ( i.e. `git pull` ) or deleted. – Emil Davtyan Sep 02 '14 at 17:29
  • @EmilDavtyan Then how does it get the right ref (and it did in my testing) when there is more than one ref pointing at that commit? – Etan Reisner Sep 02 '14 at 17:41
  • @EtanReisner I don't know how you tested it, so I can't explain it. But if it was parsing the reflog message that information remains in `.git/logs/HEAD` even after ref deletion. – Emil Davtyan Sep 02 '14 at 17:53
  • @EmilDavtyan I never said it **was** parsing the reflog message. I said "it might very well be parsing the message in the reflog for all that I know". It certainly seems to be the case that it isn't given the failure when the ref gets deleted but that still leaves open the question of what it is actually doing since, assuming my tests weren't horribly flawed, it can't be mapping from revision to ref. – Etan Reisner Sep 02 '14 at 18:05
  • @EtanReisner I understand what you said, just noting my finding. – Emil Davtyan Sep 02 '14 at 18:13
  • 1
    @EmilDavtyan So it turns out git *is* [parsing the reflog message](https://github.com/git/git/blob/master/sha1_name.c#L898). I don't know what fails before it gets there to prevent it from working in your test case but that's a different question. That's called from `interpret_nth_prior_checkout`. – Etan Reisner Sep 02 '14 at 18:24
  • Which would seem to mean, oddly enough, that the original attempt (made to handle default `--decorate`/etc.) is robust enough to be used since git itself uses it at least in some places. – Etan Reisner Sep 02 '14 at 18:25
1

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.

Emil Davtyan
  • 13,808
  • 5
  • 44
  • 66
  • 1
    Unfortunately, if multiple references point to the previously checked out commit, which of those `HEAD` pointed to is not given by `git branch --contains @{1}`. – jub0bs Sep 02 '14 at 14:48
  • Yes, but I think `git branch --contains HEAD@{1}` does. – Emil Davtyan Sep 02 '14 at 15:24