Reputation: 16727
I've taken a look at these previous questions already:
They don't exactly address a particular issue though - there are other changes in the index! When running the rebase
command, git complains: Cannot rebase: You have unstaged changes.
The commit previous to the last one (do I refer to that as "2 HEADs ago"?) was a refactor commit. I currently have in the index many unstaged changes, but only some of which I want to add to the previous to last commit.
I'm imagining the way to do this would be to:
stash
all of my current changesrebase -i
to the previous to last commit (changing index and moving Head, right?)add -p
and commit --amend
to selectively modify this old commitrebase --continue
to finish (updates children, moves Head back to where I started, what happens to index?)Is this correct? If it is, how do I do step 3? If it isn't, what should I be doing instead?
Also, note that I'm still learning about git and am still not 100% sure I'm referencing things in git (Head, index, stash, etc) properly.
For anyone else this may help, these are the steps I actually took:
git stash
all of my current changesgit rebase -i <ID>
to the parent of the previous to last commit, changing index and moving Headgit stash apply
load the stash into my index without changing Head
git reset HEAD <file>
to unload files staging. Make sure staging is clear.add -p
and commit --amend
to selectively stage changes and commit themgit reset --hard
to discard index so it matches Headgit rebase --continue
to finish. updates children, moves Head back to very start, but with changes
Upvotes: 8
Views: 3734
Reputation: 1326994
With git1.8.4 (July 2013), you can choose to:
"
git rebase
" learned "--[no-]autostash
" option to save local changes instead of refusing to run (to which people's normal response was to stash them and re-run).
So in your case, this could work (and save your work in progress in the meantime):
git rebase --autostash -i <ID>
See commit 587947750bd73544a6a99811f0ddfd64e1ff1445:
This new feature allows a rebase to be executed on a dirty worktree or index.
It works by creating a temporary "dangling merge commit" out of the worktree and index changes (via 'git stash create'), and automatically applying it after a successful rebase or abort.
rebase
stores the SHA-1 hex of the temporary merge commit, along with the rest of the rebase state, in either.git/{rebase-merge,rebase-apply}/autostash
depending on the kind of rebase.
Since$state_dir
is automatically removed at the end of a successful rebase or abort, so is theautostash
.The advantage of this approach is that we do not affect the normal stash's reflogs, making the
autostash
invisible to the end-user.
This means that you can use 'git stash' during a rebase as usual.When the
autostash
application results in a conflict, we push$state_dir/autostash
onto the normal stash and remove$state_dir
ending the rebase.
The user can inspect the stash, and pop or drop at any time.
Note: git 2.0.1 (Jult 25th, 2014) handle the autostash case.
See commit e4244eb from Ramkumar Ramachandra (artagnon
) (also in his blog):
rebase -i
: handle "Nothing to do" case with autostash
When a user invokes
$ git rebase -i @~3
with dirty files and
rebase.autostash
turned on, and exits the$EDITOR
with an empty buffer, the autostash fails to apply.Although the primary focus of rr/rebase-autostash was to get the
git-rebase--backend.sh
scripts to return control togit-rebase.sh
, it missed this case ingit-rebase--interactive.sh
.
Since this case is unlike the other cases which return control for housekeeping, assign it a special return status and handle that return value explicitly in git-rebase.sh.
git config rebase.autostash true
to make rebase [-i]
work in a dirty worktree.This new feature allows a rebase to be executed on a dirty worktree or index.
It works by creating a temporary "dangling merge commit" out of the worktree and index changes (via 'git stash create
'), and automatically applying it after a successful rebase or abort.rebase stores the SHA-1 hex of the temporary merge commit, along with the rest of the rebase state, in either
.git/{rebase-merge,rebase-apply}/autostash
depending on the kind of rebase. Since$state_dir
is automatically removed at the end of a successful rebase or abort, so is theautostash
.The advantage of this approach is that we do not affect the normal stash's reflogs, making the autostash invisible to the end-user. This means that you can use '
git stash
' during a rebase as usual.
Upvotes: 5
Reputation: 4056
If I understand what you want to do, it's not much different than the answer on your first link, you just need to stash the changes you don't want to add to the older commit. You can do it like this:
aaaaaaa
and its parent is bbbbbbb
, you want bbbbbbb
.git rebase -i bbbbbbb
(substituting the correct commit as determined in step 3). Move the most recent commit ("add this") up to just below the commit you're modifying and change it from pick
to fixup
. This will add it to that commit without change that commit's message.Upvotes: 4
Reputation: 3000
`git stash`
`git checkout <COMMIT HASH from 2 commits ago>`
`git stash pop`
`git add` (selectively--one file at a time)
`git commit --amend`
`git stash` (to re-stash all the changes you didn't commit)
`git branch temp`
`git checkout master`
`git rebase temp -i`
This will open a text editor with a list of commits--one commit per row. Delete the first row, which is the old commit that you want to replace. Save and close.
`git branch -d temp` (to clean up the branch you created)
Upvotes: 0
Reputation: 43841
Your plan sounds good. A git stash apply
or git stash pop
will modify the working tree and/or index, but will not change HEAD, so you should be able to do it while in a rebase edit.
Upvotes: 3