Last Updated: February 25, 2016
·
16.1K
· bartuz

Git revert only specific file from a commit (when conflicts may occur...)

tl;dr - use git revert --no-commit SHA

In my contribution to i18n gem someone pointed that my test is useless,

He was totally right so I decided to remove it. My first thought was "Oh, I can just git revert this commit".

NOPE!

I realised this commit also contains code which is fine and I want to keep it.

So the first important lesson here: keep your commits small

I was stubborn to use git so after few googles queries, some poor/unsatisfying solutions* I finally found this: --no-commit flag

As name suggests it just doesn't commit changes. So I can probably edit them or... checkout them!

To make my thing work I did following:

  • git revert --no-commit ee27f3d

P.S. you can use @~1 to revert last commit.

@ is alias for HEAD and ~1 is a number of commits before it

git revert ee23f3d~2 would also work and revert two commits before ee23...)

  • git status which returned conflicts and unnwanted changes:
You are currently reverting commit ee27f3d.
  (fix conflicts and run "git revert --continue")
  (use "git revert --abort" to cancel the revert operation)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   lib/i18n/tests/lookup.rb
    modified:   lib/i18n/tests/unwanted.rb

Unmerged paths:
  (use "git reset HEAD <file>..." to unstage)
  (use "git add <file>..." to mark resolution)

    both modified:   lib/i18n/backend/base.rb
    both modified:   lib/i18n/tests/interpolation.rb

two bad things happened here: there are conflicts and file which I don't want to revert.

Firstly let's deal with unwanted file.
+ git reset HEAD lib/i18n/tests/unwanted.rb

we receive message:

Unstaged changes after reset:
M   lib/i18n/tests/unwanted.rb

Let's do git status and scroll it a bit:

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)

    modified:    lib/i18n/tests/unwanted.rb

which means this file will be ignored during commit (but changes will remain).

Next files with conflicts. As we didn't want to change them anyway we can simple put them to Changes not staged for commit (like we did before with unwanted file).

  • git reset lib/i18n/backend/base.rb

and

  • git reset ib/i18n/tests/interpolation.rb

P.S. In case we would like to revert a file with conflicts in it we would have to manually fix them and than git add fixed/file.rb

now git status prints:

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   lib/i18n/tests/lookup.rb

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)

    modified:   lib/i18n/tests/unwanted.rb
    modified:   lib/i18n/backend/base.rb
    modified:   lib/i18n/tests/interpolation.rb

Now we can reject all changes in files we didn't intend to change:

  • git checkout .

(don't worry, it doesn't touch files under changes to be commited)

and now git status prints:

You are currently reverting commit ee27f3d.
  (fix conflicts and run "git revert --continue")
  (use "git revert --abort" to cancel the revert operation)

Changes to be committed:
  (use "git reset HEAD <file>..." to unstage)

    modified:   lib/i18n/tests/lookup.rb

we can only see modified: lib/i18n/tests/lookup.rb there so we can now write:

  • git revert --continue

to commit all we did.

The last step is commit message. By default:

1 Revert "Interpolate now works for array"
 2
 3 This reverts commit ee27f3d85ab275757b144aa216c1a282ef9bb345.
 4

which IMHO has to be changed because we aren't really reverting a commit (we used git revert just as a helper tool).

I removed Revert from title and added a note to description:

1 Remove redundant test for lookup
2
3 This reverts PART of commit ee27f3d85ab275757b144aa216c1a282ef9bb345.
4 thanks @clemens
5 https://github.com/svenfuchs/i18n/pull/282/files#r37780129

and we're done!

* git reset --hard does not satisfies me