简体   繁体   中英

Programmatically get the revision that git will jump to with `git checkout -`

If I use git checkout - , git will jump back to the revision I was in before. This is true if it was a detached HEAD (in which case HEAD will be detached in the same revision) or a branch (in which case HEAD will go back to being on that branch).

I would like to be able to know the revision I was just in, the one git will jump to if I run that, ideally, without actually changing HEAD.

So if HEAD was last in a branch, I'd like to get the name of that branch; and if it wasn't, I'd like to get the SHA of that commit.

@{-1} corresponds to the revision of interest. From the git-checkout man page:

You can use the @{-N} syntax to refer to the N-th last branch/commit checked out using git checkout operation. You may also specify - which is synonymous to @{-1} .

(emphasis mine)

However, whether or not you can recover the name of the branch (if any) that was last checked out is unclear to me. The git-reflog man page mentions that

the HEAD reflog records branch switching

but Git doesn't appear to provide any functionality to tap into that information...

If you mean you want to be able to read some output so that you, as a user, know what git checkout - would do, then you can do

git reflog

and it will usually tell you the right thing. It will show the HEAD reflog, with output like

c5d4ede HEAD@{0}: checkout: moving from master to branch
38c594f HEAD@{1}: checkout: moving from branch to master
c5d4ede HEAD@{2}: checkout: moving from 
c5d4ede3ffa2633bd7b8b0f19a100832a3b3ac86 to branch
c5d4ede HEAD@{3}: checkout: moving from master to HEAD^
38c594f HEAD@{4}: commit: 2
c5d4ede HEAD@{5}: commit (initial): 1

You can see that HEAD@{1} is what was last checked out, the abbreviated hash for the commit is 38c594f , and based on the description it must be that master was checked out.

There is one little problem, though. In this case, the output doesn't show you whether master has moved since you were on master . There aren't a lot of ways that would happen, but if you have to be sure, and the HEAD reflog shows that a branch would be checked out, then you would follow up with

git reflog master

to see where master is now; because git checkout - will checkout the branch, not a specific commit, in this situation.

(In the event that HEAD@{1} shows that you were in detached state, you won't have to worry about possible branch moves and you'd know that chekcout - would take you to the commit identified in the log.)

If you want something for a script's consumption, that's a tougher problem. You can get the commit ID you were on with

git rev-parse HEAD@{1}

But knowing what branch you were on isn't as easy (because a commit could have zero, one, or many branches pointing to it), and without knowing which branch you were on you can't know if the branch might have moved.

It seems that git is able to figure this out when executing a checkout , but as far as "how", we've reached the edge of my knowledge. The obvious answer would be for it to use the reflog, but I don't see anything that looks structured enough in the reflog files for that to be correct.

For what it's worth, here's how to do it if changing the position of HEAD is not a disqualifier.

get_last_head() { 
  local REV; 
  git checkout -q -; 
  REV=$(git symbolic-ref --short -q HEAD) || 
  REV=$(git rev-parse HEAD); 
  git checkout -q -; 
  echo $REV; 
}; 
get_last_head

Another possibility is to use rev-parse --symbolic-full-name , although that means manipulating the output to remove the initial refs/head/

get_last_head() {
    local REV=$(
        git rev-parse --symbolic-full-name @{-1} |
        sed -re 's%^refs/heads/%%' # Not sure how safe this is
    );
    if [ -z "$REV" ]; then REV=$(git rev-parse HEAD); fi
    echo $REV;
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM