So here's the scenario. I've got a script that runs some tests. I need to make another script that accepts as a parameter a git commit name and then does the following:
- Saves the current commit state - branch name or unnamed commit.
- Switches to a detached HEAD at the specified commit
- Runs the test script against that commit
- Switches back so HEAD is the same as it was before this business
I need to make sure this script is robust so that it's never destructive no matter the state of the repository. It should work when it's run from a detached HEAD or from a regular branch, and preferably it should work even when there are uncommitted or unstaged changes around.
I feel like this should be an easy question to answer, since running a test script against a previous commit seems like a really common task to want to automate. But I can't seem to find any simple series of commands to do it.
(Similar to what pushd / cd / popd do for current working directory).
try (not tested)
to "switches back so
HEAD
" as it was before yourcheckout xxx
.See also "
HEAD
andORIG_HEAD
in Git"All the other revision specification are here: rev_parse, "SPECIFYING REVISIONS" section.
For instance, to get back to the previous branch, you can try
@{-1}
.Just tested it (the "previous
HEAD
" option):Simple git repo with 3 files added in three commits (
a
, thenb
, thenc
):I checkout the first commit (
DETACHED HEAD
)I try to get back to the previous
HEAD
before making theDETACHED HEAD
:It works!You get back the right commit, but not the right branch (i.e. you are still in a detached mode)
In this configuration, trying to get back to the previous branch wouldn't work
So the only real solution to get back to the HEAD (not to a commit in a detached mode) is to memorize it first
See Jefromi's answer.
If it's in a script, for only this one use case, you don't need to do anything super-fancy, just store where HEAD was before, and check it out again after:
This has the advantage of working no matter what you do in the middle - in particular you could do a lot of git operations there - perhaps a test merge, or cherry-pick a commit for testing (maybe testing that commit, maybe it contains some build configuration settings purely for testing). Since those operations create commits, they'd cause the
HEAD@{1}
approach to fail (you'd wantHEAD@{2}
instead). Even better, if your testing actually involves creating temporary branches, this will still work, while the@{-1}
approach would not.(Plus, as far as I can tell,
HEAD@{1}
always checks out the commit referred to by HEAD at that point, not the branch that was then pointing to that commit. That kind of makes sense, as the branch could've conceivably changed since then.)