27

I'd like to restore the files of the git working copy to a given commit, without setting that commit as the HEAD commit.

If I git checkout to a given commit I obtain a detached HEAD, and after commiting changes, the commit tree would look something like:

A
|
B
|
C  
| \
D  E

While the behaviour I'd like to obtain is:

A
|
B
|
C
|
D
| <- git command so my files are restored to C, but my HEAD still points to D
E

Thanks

  • 2
    Wouldn't commit be on top of `C`, not `B`? And how do you plan to handle merge conflicts? – Benjamin Bannier Dec 15 '12 at 21:20
  • 3
    Not sure what you are really trying to achieve, and a lot less sure how you will handle merge conflicts/working tree changes, but have you tried `git checkout .`? – knittl Dec 15 '12 at 22:00
  • Yes, it should hang from C, my bad. I'll edit the question. My intention is to replace all the files of my workspace with the files from commit 'C', WHITOUT setting C as my head commit. If 'C' is setted as my HEAD commit, it will be in a detached head state. – Josep Rodríguez López Dec 15 '12 at 22:22

3 Answers3

29

This should do it:

git reset --hard C
git reset --soft D

First you reset the HEAD, index and worktree to C.
Then you reset HEAD (and only HEAD, as explained in "Practical uses of git reset --soft?") to D.

Note that a commit at this point would create a new commit with C content, replacing D by a D' looking like C.
That changes the history, and is not much different than a simple git reset --hard C.

Another option would git revert C on top of D, but D would still be visible in the history, which might be what you don't want.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
27

VonC answer requires you to do a roundtrip. You can achieve the same thing with a single 'git checkout'

git checkout C ./

Note that you have to provide ./ otherwise git will actually check out the specified branch or commit.

Willem D'Haeseleer
  • 19,661
  • 9
  • 66
  • 99
  • 5
    @JamesJohnston: This answer doesn't handle the case where files have been deleted or moved in branch C, it simply leaves them there. – Dietrich Epp Feb 03 '16 at 22:52
7

The plumbing commands for this (for anyone scripting this sort of thing) are:

git read-tree C
git checkout-index -f -a

Although for some reason I can't fathom, when I run this from a script, I also have to do the following after the above commands, otherwise I get an error about patches not applying to the index:

git update-index -q --refresh
joachim
  • 28,554
  • 13
  • 41
  • 44
  • 1
    The part about `git update-index -q --refresh` just now helped me with something completely unrelated. Thanks :-) – Jan Jan 19 '17 at 14:44
  • Nice. Note that this leaves the files from `C` staged in the index (`read-tree` puts them in the index and `checkout-index` updates the working tree), whereas the other answers change the working tree only. – Scott McPeak Jun 22 '21 at 10:24