How to stash my previous commit?
As usual in Git, there are many ways to skin this cat, but because we're talking about 2 commits (rather than many), the simplest thing that comes to mind upon reading this question is:
git reset --hard HEAD~2 # set branch to point to 2 commits ago (parent of 222)
git cherry-pick 111
git push
git cherry-pick 222
The question asks about stashing the previous commit, but I would think under normal circumstances you wouldn't need to anymore since you've already accomplished your goal of pushing the desired commit. So you would be done here and can go on with your development. But if you still want to stash your top commit instead, you would continue with:
git reset --soft HEAD~1
git stash
Note: the reason this is (slightly) easier than an interactive rebase is because there are only 2 commits to cherry pick, and you likely want to push in between them.
An alternative solution uses the stash:
Before:
~/dev/gitpro $git stash list
~/dev/gitpro $git log --oneline -3
* 7049dd5 (HEAD -> master) c111
* 3f1fa3d c222
* 0a0f6c4 c333
- git reset head~1 <--- head shifted one back to c222; working still contains c111 changes
- git stash push -m "commit 111" <--- staging/working (containing c111 changes) stashed; staging/working rolled back to revised head (containing c222 changes)
- git reset head~1 <--- head shifted one back to c333; working still contains c222 changes
- git stash push -m "commit 222" <--- staging/working (containing c222 changes) stashed; staging/working rolled back to revised head (containing c333 changes)
- git stash pop stash@{1} <--- oldest stash entry with c111 changes removed & applied to staging/working
- git commit -am "commit 111" <-- new commit with c111's changes becomes new head
note you cannot run 'git stash pop' without specifying the stash@{1} entry. The stash is a LIFO stack -- not FIFO -- so that would incorrectly pop the stash@{0} entry with c222's changes (instead of stash@{1} with c111's changes).
note if there are conflicting chunks between commits 111 and 222, then you'll be forced to resolve them when attempting to pop. (This would be the case if you went with an alternative rebase solution as well.)
After:
~/dev/gitpro $git stash list
stash@{0}: On master: c222
~/dev/gitpro $git log -2 --oneline
* edbd9e8 (HEAD -> master) c111
* 0a0f6c4 c333
If it were me, I would avoid any risky revision editing and do the following instead:
Create a new branch on the SHA where 222 was committed, basically as a bookmark.
Switch back to the main branch. In it, revert commit 222.
Push all the commits that have been made, which will push commit 111 only, because 222 was reverted.
Work on the branch from step #1 if needed. Merge from the trunk to it as needed to keep it up to date. I wouldn't bother with stash.
When it's time for the changes in commit 222 to go in, that branch can be merged to trunk.
If you've not pushed either commit to your remote repository, you could use interactive rebasing to 'reorder' your commits and stash the (new) most recent commit's changes only.
Assuming you have the tip of your current branch (commit 111 in your example) checked out, execute the following:
git rebase -i HEAD~2
This will open your default editor, listing most recent 2 commits and provide you with some instructions. Be very cautious as to what you do here, as you are going to effectively 'rewrite' the history of your repository, and can potentially lose work if you aren't careful (make a backup of the whole repository first if necessary). I've estimated commit hashes/titles below for example
pick 222 commit to be stashed
pick 111 commit to be pushed to remote
# Rebase 111..222 onto 333
#
# Commands:
# p, pick = use commit
# r, reword = use commit, but edit the commit message
# e, edit = use commit, but stop for amending
# s, squash = use commit, but meld into previous commit
# f, fixup = like "squash", but discard this commit's log message
# x, exec = run command (the rest of the line) using shell
#
# These lines can be re-ordered; they are executed from top to bottom.
#
# If you remove a line here THAT COMMIT WILL BE LOST.
#
# However, if you remove everything, the rebase will be aborted.
#
# Note that empty commits are commented out
Reorder the two commits (they are listed oldest => newest) like this:
pick 111 commit to be pushed to remote
pick 222 commit to be stashed
Save and exit, at which point git will do some processing to rewrite the two commits you have changed. Assuming no issues, you should have reversed the order of your two changesets. This can be confirmed with git log --oneline -5
which will output newest-first.
At this point, you can simply do a soft-reset on the most recent commit, and stash your working changes:
git reset --soft HEAD~1
git stash
It's important to mention that this option is only really viable if you have not previously pushed any of these changes to your remote, otherwise it can cause issues for everyone using the repository.