6

From the Git manual:

git checkout [--detach] <commit> Prepare to work on top of <commit>, by detaching HEAD at it (see "DETACHED HEAD" section), and updating the index and the files in the working tree. Local modifications to the files in the working tree are kept, so that the resulting working tree will be the state recorded in the commit plus the local modifications.

It seems there is only one option: HEAD detached. Can I specify an option not let HEAD detached?

[Upgrade 1] I asked a similar question in the comment in the post "Revert Git repo to a previous commit"

# To get just one, you could use `rebase -i` to squash them afterwards
# Or, you could do it manually (be sure to do this at top level of the repo)
# get your index and work tree into the desired state, without changing HEAD:
git checkout 0d1d7fc32 .

# Then commit. Be sure and write a good message describing what you just did
git commit

My Question to him: @Jefromi, after "git checkout 0d1d7fc32 .", is the HEAD already detached? Then "git commit" cannot do anything with the dangled HEAD.

to check out the state from 0d1d7fc32 in the current directory, but it leaves HEAD where it was. It's the same as using something like git checkout other-branch path/to/file - that will leave you on your current branch, and just check out the given file. In this case we're checking out the entire current directory instead of just one file, but still not changing HEAD. – Jefromi

So it seems we can let HEAD not attached by adding "."

[Update 2] From the answers and comments below, I think a neat one is: git reset --hard

But then now my question is: Is Jefromi's method correct?

git checkout 0d1d7fc32 .
git commit

See the above link for more details of his method.

Community
  • 1
  • 1
user1914692
  • 3,033
  • 5
  • 36
  • 61
  • 6
    Checking out a commit (rather than a branch) will indeed put you in detached-HEAD state, but you can immediately create and check out a branch to "recover" from that state, by running `git checkout -b `; you may find [this answer](http://stackoverflow.com/questions/25670173/why-does-git-tell-me-not-currently-on-any-branch-after-i-run-git-checkout-ori/25670296#25670296) useful. – jub0bs Nov 11 '15 at 17:22
  • But what's so bad about detaching the `HEAD`? Scaring users about potential horrors of working in a detached `HEAD` state is an idiotic trend, so don't subsume to it. When you're working in that state, drastic `HEAD` movements are recorded both in the reflog and in the `ORIG_HEAD` special ref so to really lose work done in that state, one have to work hard against Git. – kostix Nov 11 '15 at 18:13
  • @kostix Is this comment directed at me? I'm not trying to scare anybody about detached-HEAD state; did you notice the quotes around "recover"? Also, quoting from the answer I link to: *It's not the end of the world, but avoiding landing in that state as much as possible (at least at the beginning of your Git learning) will likely spare you some surprises.* I don't think that qualifies as scaremongering. – jub0bs Nov 11 '15 at 18:48
  • @Jubobs, no, certainly not to you so please take no offence! I supposed the OP has an XY problem: it's typical to read a random guide on the net which says the detached HEAD is a no-no which would logically lead to the question we're discussing. – kostix Nov 11 '15 at 19:59
  • @Martin, that's what `git checkout -b ` is for. – kostix Nov 11 '15 at 20:00
  • @kostix, "But what's so bad about detaching the HEAD?". In some situations I may need to checkout one old commit and start to working on it since. It is like revert, but I think git checkout commit is more straight in this case. So I hope I could check out a commit without HEAD detached. – user1914692 Nov 12 '15 at 17:11
  • @user1914692, to me, it seems like you're maintaining a wrong mental model of how Git manages history. Each commit represents some state of the project, and a tip commit of a branch represent the last such state in a series. Now there are two cases for checking out a past commit: 1) You want to just see how the project looked like back then. Say build it and run. 2) You need to develop a feature or fix a bug but decide that the current tip commit of a branch is not suitable for this (say, you're in the middle of implementing something complicated, and it's not finished/tested/whatever). – kostix Nov 12 '15 at 17:52
  • @user1914692, the first case is pretty self-explanatory. The crucial bit of the second is that once you've checked out that commit and started recording new commits, you effectively maintain a *separate* line of history. That is, separate to the tips of any branches you might have in your repository. It's not at all like reverting. On the other hand, you might merge the work done into the tip of the original branch. In this case, you might consider the initial checkout as a sort of "temporary revert" -- to just get a well-known state before you begin implementing the feature/bugfix. – kostix Nov 12 '15 at 17:56
  • @user1914692, So... If you really want to revert a single commit or a series of commits, there's `git revert`. If you really meant you'd like to *throw away* all commits in a branch recorded on top of the one you want "to check out", then look at `git reset`. – kostix Nov 12 '15 at 17:58
  • Not sure if this helps, but sometimes I find myself wanting to restore just one file from a past commit which I had deleted in some further commit, and in that case I'll do `git checkout -- path/to/file`. – emilyfy Dec 19 '19 at 02:18

3 Answers3

7

If you want to check out a commit that isn't the last commit of any branch, it's fundamentally impossible to check that out while at the same time having HEAD refer to a branch.

You can create or update a branch to point to that specific commit, as indicated in the comments using git checkout's -b option (or -B to overwrite an existing branch), or if you're already on a branch that you want to have point to that specific commit, you can update it and check out at the same time using git reset --hard <commit>.

  • *[...] it's fundamentally impossible [...].* "Impossible" is too strong a word. [Michael's suggestion](http://stackoverflow.com/a/33656416/2541573) is sound. – jub0bs Nov 11 '15 at 17:40
  • 2
    @Jubobs No, I mean exactly what I wrote. It is impossible. What *is* possible is update a branch to point to that commit (so that "that isn't the last commit of any branch" no longer applies), and check out that newly updated branch. That's basically what `git checkout` does when you use the `-b` option. I'm suggesting the same thing in my answer that Michael is also suggesting. –  Nov 11 '15 at 17:46
  • 1
    I don't want to work on a new branch. I still work on the same branch. – user1914692 Nov 12 '15 at 21:20
6

If you want to check out a given commit hash without detaching from HEAD, you can assign it to a new branch. Just add -b <newbranch> to the end of your git checkout command (replacing <newbranch> with the name of your new branch)

git checkout <hash> -b mynewbranch
mkrufky
  • 3,268
  • 2
  • 17
  • 37
  • 1
    But I don't want to work on a new branch. I still work on the same branch. I know I can merge the new branch back to my current branch. Just think it is not that neat... – user1914692 Nov 12 '15 at 21:20
-1

If you want to checkout in branch-mode, and you know for sure it already is sitting at the tip of a branch, you'll need to first identify that branch name (using "git show --decorate"):

git show --decorate <commit>

For example, "git show --decorate 85d3204" on my computer right now tells me that "85d3204" is at the tip of "MY-BRANCH":

commit 85d3204cd5c081abce3329729691fd9e056dcc19 (MY-BRANCH)

And so to checkout this commit in branch-mode (and assuming I want to continue development on "MY-BRANCH"), I would follow this with:

git checkout MY-BRANCH
Julius Musseau
  • 4,037
  • 23
  • 27