179

How do I undo parts of my unstaged changes in git but keep the rest as unstaged? The way I figured out is:

git commit --interactive
# Choose the parts I want to delete
# Commit the changes
git stash
git rebase -i master # (I am an ancestor of master)
# Delete the line of the most recent commit
git stash apply

This works, but it would be nice if there were something like git commit --interactive only for reverting changes. Any better methods?

asmeurer
  • 86,894
  • 26
  • 169
  • 240

7 Answers7

308

You can use git checkout -p, which lets you choose individual hunks from the diff between your working copy and index to revert. Likewise, git add -p allows you to choose hunks to add to the index, and git reset -p allows you to choose individual hunks from the diff between the index and HEAD to back out of the index.

$ git checkout -p file/to/partially/revert
# or ...
$ git checkout -p .

If you wish to snapshot your git repository beforehand to preserve these changes before reverting them, I like to do:

$ git stash; git stash apply

If you use that often, you might want to alias it:

[alias]
    checkpoint = !git stash; git stash apply

Reverting individual hunks or lines can be even easier if you use a good editor mode or plugin, which may provide support for selecting lines directly to revert, as -p can be a bit clumsy to use sometimes. I use Magit, an Emacs mode that is very helpful for working with Git. In Magit, you can run magit-status, find the diffs for the changes that you want to revert, select the lines you wish to revert (or simply put the cursor on hunks you wish to revert if you want to revert a hunk at a time instead of a line at a time), and press k to revert those specific lines. I highly recommend Magit if you use Emacs.

Brian Campbell
  • 322,767
  • 57
  • 360
  • 340
  • I think this is about as complex as the solution I originally came up with, but I think my original solution has the benefit of saving the removed lines for 30 days because they are committed. I'm thinking maybe it would be nice to have a shell script written around it, though. – asmeurer Jan 01 '10 at 17:38
  • 2
    Sorry, I just realized that `git checkout` also has a `-p` flag, which does exactly what you were asking for in a single command. Apologies for the previous complex set of steps; you can just use `git checkout -p`. As far as saving stuff goes, before doing something potentially destructive I often do a `git stash; git stash apply` (or create an alias that does that as `git checkpoint` or something) to record the current tree in a stash so I can get back to it if something goes wrong. – Brian Campbell Jan 01 '10 at 18:57
  • There you go, that's the solution! As far as the alias, I am thinking something like `git commit -a -m "Backup Commit" --edit; git reset HEAD^` would be better, because then it would not dirty my stash state, which I may be using for something else. Then, as long as you have the SHA1 you can cherry-pick it within the next 30 days. The --edit lets you add info to the commit message to assist you in finding the SHA1 later if you want it. On the other hand, this would dirty up the git reflog, so I guess it's a tradeoff based on what you do. – asmeurer Jan 05 '10 at 18:11
  • The checkpoint alias does not work for me: only the first command is executed, not the second one. That is sad, because I would have liked it. I'm using git version 1.7.10.4. – Fabien Feb 10 '15 at 14:39
  • Is there a way to make `git checkout -p` not apply the patches to the index but stage them only? – Geremia Jun 30 '16 at 20:03
  • Use `git stash && git stash apply` instead of `git stash; git stash apply`: You don’t want to apply an old stash if `git stash` fails for some reason. – Lucas Sep 28 '20 at 15:42
34
git diff > patchfile

Then edit the patchfile and remove the parts you don't want to undo, then:

patch -R < patchfile
Bence Kaulics
  • 7,066
  • 7
  • 33
  • 63
octoberblu3
  • 341
  • 3
  • 2
5

You could do

git checkout master -- path/to/file

For each file you want to reset.

Drew Hoskins
  • 4,168
  • 20
  • 23
1

How about

  1. Back out the affected files with changes from the index
  2. use git add -p to only add back the changes that you want to the index.
Abizern
  • 146,289
  • 39
  • 203
  • 257
1

When I run 'git status', it says:

$ git status
# On branch fr/fr.002
# Changed but not updated:
#   (use "git add <file>..." to update what will be committed)
#   (use "git checkout -- <file>..." to discard changes in working directory)
#
#   modified:   makefile
#
no changes added to commit (use "git add" and/or "git commit -a")
$

So, to cancel the unstaged edits, it tells me to run:

git checkout -- makefile
Jonathan Leffler
  • 730,956
  • 141
  • 904
  • 1,278
  • 3
    I think that he was wondering about how to revert individual hunks, not an entire file at a time. This is, of course, the solution if you just need to undo all the changes in a file. – Brian Campbell Jan 05 '10 at 19:36
  • Yup - I suspect you're correct. The 'git checkout -p' and 'git add -p' options seem likely to be what is wanted - as you said. – Jonathan Leffler Jan 05 '10 at 22:30
1

Brian Campbell's answer crashes my git, version 1.9.2.msysgit.0, for reasons unknown, so my approach is to stage hunks I want to keep, discard changes in the working copy, then unstage.

$ git add -p
   ... select the hunks to keep
$ git checkout -- .
$ git reset HEAD .
Community
  • 1
  • 1
G-Wiz
  • 7,370
  • 1
  • 36
  • 47
  • 1
    There is also `git stash -p`. You could do `git stash -p; git reset --hard; git stash pop`. This is effectively the same as what you're doing except you don't have to write a commit message. – asmeurer Nov 16 '15 at 18:22
  • @asmeurer cheers. Mine doesn't require a commit message, but it does prob make more sense to use the stash than the index for this. – G-Wiz Nov 16 '15 at 18:23
0

you can do git checkout and give it name names of the parts you want to undo.

Andrew
  • 570
  • 4
  • 11