As a SW/Ops/DB Engineer

riywo’s technology memo

Git Rollback

One day, I wanted to rollback git repository to a previous commit with a new “rollback” commit because the wrong commits had already pushed to the master.

MODIFIED: The best way is git revert -n INITHASH..HEAD!

Thanks, @miyagawa!

$ git init
$ echo important > important
$ touch important_empty
$ git add .
$ git commit -m 'init'
$ ls
important       important_empty

$ git rm important important_empty
$ echo wrong > wrong
$ touch wrong_empty
$ git add .
$ git commit -m 'wrong' ## WRONG COMMIT!
$ ls
wrong       wrong_empty

$ git revert -n INITHASH..HEAD
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   important
#   renamed:    wrong_empty -> important_empty
#   deleted:    wrong
#
$ ls
important       important_empty

old posts

Generally, git revert -n HASH works, however, if some files are added or deleted, it doesn’t work well. Here is an example.

git revert is not the way to revert “to HASH”, but to revert “HASH”.

$ git revert -n INITHASH  ## want to rollback to INITHASH
$ git status
# On branch master
nothing to commit, working directory clean
$ ls
wrong       wrong_empty

So, I tried some ways. First, git checkout INITHASN .. It worked only for deleted files.

$ git checkout INITHASH .
$ git status
# On branch master
# Changes to be committed:
#   (use "git reset HEAD <file>..." to unstage)
#
#   new file:   important
#   new file:   important_empty
#
$ ls
important       important_empty wrong           wrong_empty

Second, git checkout -b rollback INITHASH and git merge. Unfortunately, INITHASH had already merged, so it happened nothing.

$ git checkout -b rollback INITHASH
$ ls
important       important_empty
$ git checkout master
$ git merge rollback
Already up-to-date.

Third, git diff and patch. It affected only non-empty files because git diff output for empty files was not good for patch command.

$ git diff HEAD..INITHASH | patch -p1
patching file important
patching file wrong
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    wrong
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   important
no changes added to commit (use "git add" and/or "git commit -a")
$ ls
important   wrong_empty

Finally, I found the best way, that is git apply.

$ git diff HEAD..INITHASH > /tmp/patch
$ git apply /tmp/patch
$ git status
# On branch master
# Changes not staged for commit:
#   (use "git add/rm <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   deleted:    wrong
#   deleted:    wrong_empty
#
# Untracked files:
#   (use "git add <file>..." to include in what will be committed)
#
#   important
#   important_empty
no changes added to commit (use "git add" and/or "git commit -a")
$ ls
important       important_empty

Hey, git experts, is this the best way to create a new “rollback” commit?

Comments