0

My question is related to that one: git-new-workdir: Commit in working tree A causes bogus changes in tree B. I use git-new-workdir to have multiple working directories from the same Git repository. Usually, I have work on different branches in each working directory, but it may happen that I commit in branch from a given working directory, while another working directory is a checkout of the same branch. When that happens, the second working directory gets staged changes that are the reverse of the commit I just did. Now comes my question...

How can one discard staged changes, without discarding non-staged modifications?

Edit: I do not want to unstage staged changes (like with git reset), but I would like to discard the staged changes completely.

Community
  • 1
  • 1
lrineau
  • 6,036
  • 3
  • 34
  • 47

4 Answers4

1

What one can do is the following, using git-diff and patch:

git diff HEAD > my.patch
git reset --hard
patch -p1 < my.patch

but there must be a better way using git commands only.

lrineau
  • 6,036
  • 3
  • 34
  • 47
  • 1
    The question is about discarding *staged* changes, without losing the work. If you don't care about the staged changes, then the ones you want are those given by `git diff HEAD`, not `git diff`. If you have staged changes, `git diff` will give you the difference between those staged changes and the working tree. If the above worked for you, then you asked the wrong question. – Kaz Dec 13 '14 at 01:32
  • I think you are right. Actually `git diff` worked for me because the staged changes and the other changes in my working tree were modifying different files. I have modified the answer accordingly. – lrineau Dec 17 '14 at 10:22
0

facepalmed when I found it

git reset HEAD
git checkout -- .

then remove any local files you may have created as well with git rm filename

Harry Moreno
  • 10,231
  • 7
  • 64
  • 116
  • `git checkout` discards non-staged modifications to tracked files. Unfortunately, that does not answer to my question. – lrineau Oct 08 '14 at 11:47
  • you need to consider the effect of both commands, reset unstages any staged changes, checkout sets all files to the latest commit. Together these commands effectively, "discard staged changes". – Harry Moreno Feb 18 '23 at 09:34
0

To discard the index without touching the working tree, you can just do this:

git reset

That is what it does. This is equivalent to your self answer, "using git commands only". The end effect is exactly like squirreling away the results of git diff HEAD, rolling everything back to the topmost commit, and then re-applying that diff to HEAD to re-create the original work tree.

But suppose you want to throw away the changes from the index such that these discarded changes are actually subtracted from your work. In that case, what I would do would be to use interactive rebase to splice the changes out:

git commit    # commit the index
git commit -a # commit the remaining changes

git rebase --interactive HEAD^^  # rebase top two commits onto same branch

In the editor window that comes up, I would simply delete the commit which holds the unwanted changes that came from the unwanted index, and then save and quit, letting the interactive rebase do the rest. There might be merge conflicts, which would have to be resolved, since the previously unstaged wanted changes might have depended on the staged unwanted changes.

At the end, I would have a single commit which has just the changes that were previously unstaged, and the unwanted changes are gone. At that point, I could get rid of that commit, turning it into unstaged changes:

git reset HEAD^

or else just keep the commit and work with it by doing more hacking and git commit --amend to add to it and update its message.

Everything that rebase --interactive does (and in fact, that non-interactive rebase does) can be done with cherry-pick commands, but it provides a convenient "workflow automation".

For any scenarios involving the re-ordering of commits, deletion of commits, squashing of multiple commits into one, rewriting commit messages (other than in the topmost patch) and that sort of thing, it's good to know how to use interactive rebase.

The idea is to turn the representation of all your changes into separate commits, then work with the commit-munging kung-fu, and finally "de-commit" something, if necessary.

For doing complex things, git is better at working with commits than with unstaged changes or the index, which are sort of temporary areas; so you want to get your work out of there and into commits first. In fact, if you have unstaged or staged changes, git prevents you from doing various useful things.

One reason to get your work into commits is that in git is very easy to lose the working copy or the index forever. The working copy and index are not commit objects and so they are not recoverable from the reflog. Type the wrong command that wipes out either or both of these and they are gone for good. But any changes that are commits are recoverable even if you lose them, via the reflog, or via branch pointers or tags that you can set.

Kaz
  • 55,781
  • 9
  • 100
  • 149
0

from git-console-hints:

use "git restore --staged < file>..." to unstage
use "git restore < file>..." to discard changes in working directory
Batiaev
  • 1,173
  • 1
  • 14
  • 30
igor.live
  • 45
  • 7