开发者

What is the shortest way to swap staged and unstaged changes in git?

If some changes are added to the index and there are some changes that are not added to the index, how 开发者_开发问答do I swap this two sets of changes?


It think that this is easiest to do with temporary commits. When you have staged and unstaged commits, you have the possibility of conflicts when trying to reorder the changes.

Make a commit with the staged changes, create a branch for later use:

git commit -m "Saved staged"
git branch save-staged

Make a commit with the unstaged changes (if the unstaged changes include new files you may need to explicitly git add them first):

git commit -a -m "Unstaged changes"

Rebase the unstaged changes onto the original HEAD (may involve conflict resolution):

git rebase --onto HEAD^^ HEAD^

Rebase the staged changes onto the unstaged changes (may involve conflict resolution):

git reset --hard save-staged
git rebase --onto HEAD@{1} HEAD^

Finally, reset the index to the (originally) unstaged changes:

git reset HEAD^

And move the branch pointer back to the original HEAD:

git reset --soft HEAD^

Removed temporary branch:

git branch -D save-staged


For a lower-level solution, you can use a bit of plumbing to talk directly to the index:

INDEXTREE=`git write-tree`
git add -A
WORKTREE=`git write-tree`
git checkout $INDEXTREE -- .
git clean -f
git read-tree $WORKTREE

What that does is build a couple of temporary tree objects in the git store, one for the index and one for the working copy. Then, it restores the old index and checks it out into the working tree. Finally. it resets the index to the version representing the old working tree.

I haven't tested this, so I'm not sure how well it handles added files in either the index or the working tree.


The way with patches (it doesn't work for binary changes):

Save patches for both staged and unstaged states

git diff >> unstaged.patch
git diff --cached >> staged.patch

Apply originally unstaged changes

git reset --hard
git apply unstaged.patch

Stage this changes except the patch files

git add -A
git reset -- staged.patch unstaged.patch

Apply originally staged changes

git apply staged.patch

Remove patch files

rm staged.patch unstaged.patch


This is based on Walter Mundt's answer, but works better when new files are staged. This is intended to be used as a script, e.g. git-invert-index

#!/bin/sh

# first, go to the root of the git repo
pushd `git rev-parse --show-toplevel`

# write out a tree with only the stuff in staging
INDEXTREE=`git write-tree`

# now write out a tree with everything
git add -A
ALL=`git write-tree`

# get back to a clean state with no changes, staged or otherwise
git reset -q --hard
git clean -fd

# apply the changes that were originally staged, that we want to
# be unstaged
git checkout $INDEXTREE -- .
git reset

# apply the originally unstaged changes to the index
git diff-tree -p $INDEXTREE $ALL | git apply --index --reject

# return to the original folder
popd


Charles Bailey has a more complete solution involving commits and managing potential conflict resolution.

I was originally trying to use only git stash, except what I initially overlooked was that git stash save will save both the index (staged changes) and the unstaged changes (which is inconvenient when you want to swap the index content with the unstaged changes).

So I modified to the following approach instead:

  • git commit -m "temp commit" (create a commit for the current index)
  • git stash (stashing obviously what is not yet added to the index)
  • git reset --soft HEAD^ (preserve the files previously committed)
  • git stash again
  • git stash pop stash@{1} (applying not what you just stashed, but what you stashed before, i.e the initial changes that weren't yet added to the index)
  • git add -A
  • git stash drop stash@{1} to clean up the stash we previously applied ( stash@{0} still contains what was originally in the index)

At the end:

  • what was not added to the index is now added.
  • what was initially in the index ends up being stashed


use `git rebase -i the best solution.

Alternative (too long):

Note the name of your current branch as:
<name-of-your-branch>

git checkout -b temp-unstaged
git commit -m "staged"
git branch temp-staged
git add .
git commit -m "unstaged"
git rebase --onto HEAD~2 HEAD~1
git checkout temp-staged
git rebase temp-unstaged
git reset HEAD~1
git reset --soft HEAD~1
git checkout <name-of-your-branch>
git branch -D temp-staged
git branch -D temp-unstaged

At last one can verify that the staged and untaged changes are swapped.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜