For example, a
Changes to be committed:
(use "git reset HEAD <file>..." to unstage)
new file: app/src/[....]
new file: test/unit/[....]
Changes not staged for commit:
(use "git add <file>..." to update what will be committed)
(use "git checkout -- <file>..." to discard changes in working directory)
(use "git add <file>..." to include in what will be committed)
Git's basic storage mechanism is "the commit"—in fact, all
git stash does is make a couple of somewhat unusual commits—so Nick Volynkin's answer is a correct one. It could perhaps use a bit of expansion though, and there are easier (well, potentially easier) methods.
I'm not a very big fan of
git stash, but if you're used to using it, here's the simplest of the other methods:
git stash save (aka
git stash). This writes two commits, one based on the current index and a second to hold as-yet-unstaged work-tree files. (If you need to hold untracked files, you can add the
-u flag, and then the stash script adds yet a third commit. Usually you can just leave these untracked files floating around in your work tree untracked, though.) These commits are not on any branch, they are only on/in the special "stash" ref. Meanwhile you're still on the "wrong" branch, which I'll call
git checkout the branch you want these on. Now you're on the right branch.
git stash apply --index. This uses the special stash commits made in step 1, while also leaving them there (
apply) in the stash. The
--index is very important: it tells the stash script to keep the index and unstaged files separate, i.e., give you back the staged and unstaged setup you had earlier.
If all goes well, you're now in a position to
git commit the changes to the branch you want them on. The previously staged files are again staged, and the unstaged files are still unstaged, because you
apply-ed the stash with
--index. The commit will commit the staged files, leaving the unstaged files unstaged.
Now you can get back on the other "wrong" branch (where you made the stash initially) and
git stash apply or
git stash pop with or without
--index. You may need to clean out any unstaged files (and it's safe to do so, they are still in the stash):
git reset --hard, followed by
git checkout wrongbr, then
git stash pop. Note that
pop is just
apply followed by
drop: we didn't want to drop the stash in step 3 (the stash has the only copy of the original modified-but-unstaged files), which is why we used
apply there, but now we (presumably) do want to drop the stash, so using
pop is OK.
There's a big potential pitfall in step 3, though: it's possible the stash won't apply correctly. If so, you must use some other method. This is one reason I don't really like the
stash system: it falls down in hard cases and leaves you needing to know the tools you can use if you don't use
stash. In which case, you can just use those tools most the time ... and then use
stash as a convenient short-cut for cases where you're sure it will work.
Background: a commit takes whatever is in the index now—
git ls-files --cached will show the complete contents, while
git status trims this down to the "interesting" ones and adds additional useful information—and makes a commit out of them, with all the necessary tree objects and so on. The new commit's parent commit is whatever the current commit was before.
You want your new commit made on another branch. One way to do this is to make it now, on the current branch; then copy that commit to a new, different commit on a different branch. To do the "copy commit to new, different commit on different branch" you can use
git cherry-pick. It's true that you can use
git rebase: under the covers, it uses
git cherry-pick itself. But
rebase is not quite the right tool: it's designed for en-masse cherry-picking, and you have just one commit; and at the end it uses
git reset to move a branch label, but not quite in the way you want. You can make it work, but there are some more appropriate tools.
Let's go back to the original problem though: you want to take the current index and use it to make a new commit, but on another branch. This would be easiest if you could just switch to the other branch now, without doing anything else, and then make the new commit.
Chances are good that you can do this. Just
git checkout otherbranch and then
git commit. There are three possible cases here:
The other branch doesn't exist yet. Great! Use
git checkout -b newbranch to create it, starting from where you are now. Then
git commit. You're done, unless you want to rebase the new branch to start from somewhere other than "where you are now". If so, use
git rebase on the new branch. Note that you can do that rebase later, after you've taken care of the unstaged files.
The other branch does exist, and—lucky you—
git checkout otherbranch works fine. Do that and commit, and you're done. You can then
git checkout whatever branch you want for the unstaged files.
The most annoying case: the other branch does exist, but
git checkout tells you that you'll overwrite something that you haven't committed.
Case 3 is the one where you need to commit-or-stash.
What to do here depends on what you are most comfortable with. You can, for instance, try the four-step
stash method described above as the simplest alternative.
For myself, though, I would just commit now, on the "wrong" branch, then commit again (or use
git stash) to get the unstaged files out of the way. This gives me a commit I can
git cherry-pick into the right branch. Here's an example sequence that's likely to work:
git committo make the commit, but on the "wrong" branch (let's call your current branch
wrongbrfor reference below).
git stash saveto save the unstaged changes (or, with
-u, untracked files too).
git checkoutthe branch you want the commit to have been on, e.g.,
git checkout rightbr.
git cherry-pick wrongbr. If this succeeds, good; if not, edit files as needed to clean up after the merge issues, then
git committhe result.
git checkout wrongbr: we will now fix this up by removing the commit copied in step 4.
git reset --hard HEAD^: this drops the commit that we copied.
git stash pop(or
git stash apply && git stash dropwhich does the same thing, the
applyvariant simply giving you the opportunity to inspect the result before you
Note step 4 here:
git cherry-pick takes the named commit (the tip of
wrongbr, which contains the commit-we-want that's simply on the wrong branch), compares it with its parent, and then attempts to apply the resulting diff to the current branch. This may need to do a 3-way merge, if the files in the current branch differ a lot from their corresponding files in
wrongbr. This is the same place the complications occur in the simple case of just checking out
rightbr and committing initially. That is, we're doing this long version because the "most annoying" case occurred when we tried to just
git checkout rightbr before committing, so there's a good chance we need to do some fixing-up. This is also likely to cause problems with the original 4-step