19

Given a git branch with some commits on it (C is the most recent commit):

A -> B -> C

How do I reset my workspace so that all the files are in the state they were at commit B, but HEAD is still at C?

I've looked at git-reset, but none of the options seem to help. The man page suggests that all the different modes will move HEAD:

--soft
   Does not touch the index file or the working tree at all 
   (but resets the head to <commit>, just like all modes do).

I've tried git reset HEAD~ but that moves HEAD.

Wilfred Hughes
  • 29,846
  • 15
  • 139
  • 192

4 Answers4

20

git checkout [-p|--patch] [<tree-ish>] [--] <pathspec>...

git checkout with <paths> or --patch is used to restore modified or deleted paths to their original contents from the index or replace paths with the contents from a named <tree-ish> (most often a commit-ish).

So you need to run this at root of your repository (works fine for any sub-tree or file(s) too):

git checkout HEAD~ -- .

This will result in git applying changes necessary to revert files to HEAD~ state, the changes will be in the index.

xaizek
  • 5,098
  • 1
  • 34
  • 60
  • 1
    In case it's not obvious, if you want to go back to a different SHA you can replace `HEAD~`, i.e. `git checkout A -- .` – Carl Walsh Oct 22 '20 at 21:29
  • This doesn't remove files that were added since HEAD~ – Pod Jan 12 '21 at 10:48
  • @Pod, correct. `git clean` can remove untracked files, but be careful with it, it can remove files you want to keep. – xaizek Jan 12 '21 at 11:45
11

You could use a combination of hard and soft resets:

git reset --hard B
git reset --soft C

The first would move HEAD to B and make all your files look like B. The second would then move it back to C without changing any files.

This method has the advantage that you are not in a detached-head state and all the differences between B and C will just show up as inverse diffs of your last actual commit. You will still be on your original branch.

You would probably have to specify C as a SHA-1 rather than a ref name, unless you specifically created one for the purpose.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
1

As pointed out in this answer to a similar question, you can use git restore for that in Git 2.23+, like so:

git restore -s HEAD~ .

According to its manpage, git restore

[restores] specified paths in the working tree with some contents from a restore source.

You use -s (or --source) to specify which commit you want to use as the restore source (here HEAD~) and specify the path(s) to restore (here everything, so .) at the end.

sh-at-cs
  • 64
  • 7
-1

Just use git checkout:

$ git checkout HEAD~
$ # Or
$ git checkout HEAD^
$ # Or
$ git checkout B

This will bring you into a detached head state where the HEAD is detached from a branch.

Mad Physicist
  • 107,652
  • 25
  • 181
  • 264
das_j
  • 4,444
  • 5
  • 31
  • 47