20

First, I have seen the answer to this question here before, but it is buried by so many "answers" which don't answer my question properly, that I cannot find it again.

So here it goes: How do I restore in Git to a previous version in my history, so that it becomes the new commit on top of my current history?

Basically, this is the most fundamental thing I would like to do with a version control system. Simply doing a reset doesn't work, because it throws away my history, a simple revert doesn't work either, because sometimes it gives me error messages (what I want to do should be possible without any error messages).

Edit: If I just do a git revert this error happens:

git revert HEAD~1
error: could not revert f1b44e3... Towards translating API to kernel.
hint: after resolving the conflicts, mark the corrected paths
hint: with 'git add <paths>' or 'git rm <paths>'
hint: and commit the result with 'git commit'
Steven Obua
  • 934
  • 1
  • 8
  • 17
  • What's the error when reverting? – Julian Jun 23 '17 at 18:26
  • 1
    `git revert` means "back out one single, specific commit", not "go back to a previous version". Mercurial's name for this operation is better: `hg backout`. Mercurial has an `hg revert` with the meaning you expected here! Meanwhile [Dai's answer](https://stackoverflow.com/a/44727815/1256452) is correct. – torek Jun 23 '17 at 21:51

4 Answers4

38

Simply "checkout the commit". This will overwrite your current working directory with the specified snapshot (commit) of your repo from history and make that your new working-set which you can stage and commit as you wish. If you commit immediately afterwards then your repo will have the same filesystem contents as the commit you performed the checkout to (assuming you have no other unstaged or staged changes)

This will not rewrite history nor edit or delete any previous commits - so it works similar to a "rollback" on Mediawiki:

cd ~/git/your-repo-root
git log
# find the commit id you want
git checkout <commitId> . 
# IMPORTANT NOTE: the trailing `.` in the previous line is important!
git commit -m "Restoring old source code"

See also: Rollback to an old Git commit in a public repo

Regarding the . (dot)

The . (dot) character means "current directory" - it is not anything special or unique to git, it's a standard command-line filesystem convention that's the same on Windows, Linux, macOS, and even MS-DOS. It works similar to how .. means "parent directory". I recommend reading these:

Regarding checkout

Be aware: checkout is an overloaded command in git - it can mean to switch branches (a la svn switch) or to get a specific file or commit from history and put into your workspace (a la svn update -r <id>). It can mean other things too: https://git-scm.com/docs/git-checkout - I appreciate it can confuse people, especially myself when I started using git after using TFS for years (where "Checkout" means something else entirely).

Dai
  • 141,631
  • 28
  • 261
  • 374
  • 2
    Argh. This won't work either. I want the old stuff committed on top of my last commit. Please guys, don't spam with wrong answers if you don't know how to do what I want. – Steven Obua Jun 23 '17 at 19:39
  • 1
    @StevenObua But that's what this does - it will take the old snapshot, then when you commit it, it will be the new HEAD. I've modified my answer to include the final `git commit`. – Dai Jun 23 '17 at 20:37
  • Ok, will try that out when I am home again! – Steven Obua Jun 23 '17 at 20:41
  • What does the `.` do in the checkout? Do you have any idea how hard it is to google that? – Joe Phillips Jun 23 '17 at 21:09
  • 1
    That's exactly what I wanted Dai. Thank you very much, and I am sorry for not reading your answer careful enough. In my mind checkout always created a new branch for the specified version, I didn't know about the other meaning. – Steven Obua Jun 23 '17 at 23:42
  • Note: `git checkout ` (without the .) will put you in a detached HEAD state. The "." dot causes git to simply make changes to the working directory without affecting the HEAD. – Able Mac Aug 20 '19 at 19:08
  • 3
    If you, like me, missed the trailing `.` and end up in a detached HEAD state, then `git checkout master` (or presumably substitute `master` for whatever branch you were working on) will essentially undo that command. – Andrew Mar 05 '20 at 00:00
  • I find that this rollback is incomplete, it doesn't remove any added files. Any way to get those removed as well, so it is completely reset to the old version? – nomadoda Aug 10 '21 at 10:39
3

Edit: I see that you want to keep the history, so my answer below is nullified. However, it is still useful. You can reorder the lines in the editor, then proceed.

Now, back to the original answer.

You could try the rebase command.

git rebase -i HEAD~n

Where n is about one more than the number of commits between current and the one you want to revert to. So let's say, deleting the last 3 commits:

git rebase -i HEAD~4

Once you're there, it will open in VIM or Nano (or other editor). Simply delete the lines of the commits to remove, and exit the editor.

In VIM that would be esc and type :exit enter.

Now, simply push it. That will result in an error, so do a force push.

git push -f

You might have to specify branch name and upstream too. That should do it!

This method completely deletes the bad commits, so it won't just add a new commit with the revert changes.

Here's the docs: https://git-scm.com/docs/rebase

Jacob Birkett
  • 1,927
  • 3
  • 24
  • 49
0

Use git revert https://git-scm.com/docs/git-revert

git revert HEAD~1
Vyacheslav Enis
  • 1,676
  • 12
  • 17
0

Take a note of the commit hash of current HEAD

  1. git reset --hard <old-commit>
  2. git reset --soft <previous-HEAD>
  3. git add . && git commit -m "message"

Done!

nomadoda
  • 4,561
  • 3
  • 30
  • 45
dzcpy
  • 129
  • 5