0

I have made some changes and staged them all into the index. Now I would like to throw away the working tree, and make it look exactly like the committed HEAD, without throwing away the index. How can this be achieved?

Everything I've looked at is geared toward the opposite: preserving the working tree while manipulating the index, or else manipulating both.

For instance, is there some way of stashing that index, and then later staging those stashed changes without doing anything to the working tree?

For reference, the following hacky achieves the effect, at least when the materials involved are patchable text files. Assume all changes have been staged with git add:

# apply the staged diff to the working tree, in reverse; i.e. undo it.
git diff --cached | git apply -R

After this, git diff --cached continues to show the staged changes, and git diff shows the reverse diff: those changes being undone relative to the index.

Answers to this question must reproduce the effect of the above command pipeline.

Kaz
  • 55,781
  • 9
  • 100
  • 149
  • 1
    `git checkout -- .` should reset your working directory only, leaving the stage untouched. – Tim Biegeleisen Sep 17 '19 at 00:31
  • @TimBiegelsen Have you read the documentation? Without a argument, this will reset the working copy to the index. Since I have staged everything (index == working), it has no effect. With a argument, it affects the index! – Kaz Sep 17 '19 at 00:49
  • 1
    Write the index as a commit with `git write-tree`. Save the resulting hash ID, do the stuff that wrecks the index, then fix it up with `git read-tree` from the written tree. – torek Sep 17 '19 at 01:04
  • @TimBiegeleisen: not a dup, as the goal is to save the *index* across this operation. – torek Sep 17 '19 at 01:05
  • I guess I found it a bit confusing about the question this part: "I would like to throw away the working tree, and make it look exactly like the committed HEAD, without throwing away the index." and then in the comment you write "Since I have staged everything (index == working),". If index == working, doesn't a simple stash solve your problem? And what also if index == working what do you mean by "throw away the working tree, and make it look exactly like the committed HEAD, without throwing away the index" – Emil Vatai Sep 17 '19 at 01:53

2 Answers2

0

There's a method to do this with git stash. I happen to really dislike git stash but I'll write it out anyway:

  1. Run git stash: this saves both the index and the work-tree, under the name refs/stash. It then runs git reset --hard so now your index and work-tree look the way you want.

  2. Do whatever you like with your work-tree

  3. To restore the index and the work-tree to the state they had in step 1, run git stash apply --index or git stash branch branch-name. The latter will create a new branch using the hash ID of the parent of the stash (the commit that was active at the time you made the stash), git checkout that branch, and run git stash apply --index.

Note that this also restores the work-tree, which you did not necessarily want. It restores the index using git apply --index, and restores the work-tree using git-merge-recursive. Either or both steps can fail, except when applying to something that matches the original commit (hence git stash branch always succeeds).

Since you only really want to save and restore the index, we can go lower-level:

  1. hash=git write-tree: writes out the index as a new (or re-used) Git tree object and saves the resulting hash
  2. git reset --hard: work-tree and index both match commit now
  3. git read-tree $hash: updates the index to match the given hash, without touching the work-tree.

The read-tree command is pretty complicated and can do a lot of other things, but this particular operation is not.

torek
  • 448,244
  • 59
  • 642
  • 775
  • I will try that; what I boiled down my approach to is simply this: `git diff --cached | git apply -R`! That's it. But it involves generating a patch that trombones out of git and back, so it's kind of ugly and perhaps won't work with nicely with binary content (not a problem I have in my scenario). – Kaz Sep 17 '19 at 01:30
  • BTW it's not at all clear to me *why* you want to do this. The only thing you can really do with the index is commit it, for which `git write-tree` is the first step (the rest being `git commit-tree` and `git update-ref`). – torek Sep 17 '19 at 01:35
  • I have files that are newly added in HEAD, and would like to simplify these, while keeping the full definitions to go into a new commit. I think it can be done like this, too: commit the changes into a new commit. Then commit a revert of that commit. Then interactively rebase, squashing the two older commits. – Kaz Sep 17 '19 at 01:44
0

Does git stash --keep-index help? Something along the lines:

git stash --keep-index # stash working tree without the index
git stash # stash just the index
git stash drop <stashid-of-wt-without-index>

EDIT: Sorry for the confusion. I use this from a GUI (magit) and it's kinda clearer which one you'd need.

Emil Vatai
  • 2,298
  • 1
  • 18
  • 16
  • 1
    The only documented effect of `--no-keep-index` in `stash` is to override an implicit `--keep-index` set up by `-p/--patch`. – Kaz Sep 17 '19 at 01:27