Reputation: 59343
This is the scenario:
Then, you realize the last few changes you made doesn't belong in this feature branch. Instead, these changes should be committed to a different feature branch, a hotfix branch or even directly to develop.
The "straight forward" way to handle this would be to stash your changes, check out the correct branch, commit your changes there, check out the feature branch again, pop the stash, resume working. (Optionally merge in the new branch.)
While simple in theory, this can be extremely impractical and tedious in many cases. First of all, the changes you want to move got stashed... So how exactly do you get them into the new branch? You could maybe pop the stash in the new branch? But if you are several commits deep into your feature branch, I wouldn't expect the current stash to be compatible with the develop branch.
I wish I could just commit the off-topic changes, then move that commit to a different branch with a single command, without leaving my current branch.
NB: I have seen other similar questions to this on SO, namely "how do I commit changes to a different branch without checking it out". The answers are pretty much always "you can't". But this is not what I'm asking here, I want to know the most practical way to get your changes moved to a different branch, when you're currently neck deep in a feature branch.
Upvotes: 1
Views: 432
Reputation: 6491
I find it useful to use git worktree
.
I'll assume a near worst-case scenario, where you are on a feature branch with a lot of changes committed, with some changes that have not been committed nor staged which belong on the feature branch, and that you've already started making some of the cleanup changes touching some of the same files as the unstaged feature changes. Also, the build system is slow and based on timestamps, so you want to avoid switching to a branch based off develop and back as that would ruin your day.
Stash the cleanup changes: Run git stash -p
and select the hunks that should be part of the cleanup commit. (You may want to commit files containing only feature changes first, in order to reduce the number of chunks to go through here.) If a hunk contains both feature and cleanup changes, you'll need to manually extricate them, so stash or not depending on where you want that hunk to go initially.
Add a worktree (or recycle one): Run git worktree add -b cleanup/the-thing-to-fix ../name_for_new_directory develop
to create a branch named cleanup/the-thing-to-fix
based off of develop
and checked out in a new worktree at ../name_for_new_directory
.
Switch to the worktree: Run cd ../name_for_new_directory
Unstash changes from step 1: Run git stash pop
. This works because we're still in the same git repo / clone as before, just on a separate branch checked out in a separate directory.
Commit the cleanup changes as normal: Run git commit -a
(or use whatever options you usually use to commit.
Now that the changes are separated, you can continue working on either one as you choose based on your specific circumstances.
Upvotes: 1
Reputation: 60313
- While working on some code you didn't write, you notice and immediately fix a bug
- Alternatively, you notice a function is missing documentation so you quickly add some pro tips for the next developer that comes along
In the usual case, where there's no mixing of changes (changes that should be committed separately are in separate files)
git checkout otherbranch
git commit -- files to change
git checkout -
because when you check out a different branch, changes in the index and work tree stay there (git won't overwrite changes you've made, see the next paragraph, but it's happy to let them stick around so long as they're not in the way), and when you specify a list of paths to git commit
it bypasses the index and adds just those files to the current tip.
If you've made changes to files that otherbranch
also changed, you can learn about merge checkouts, add the -m
switch (also imho pronounced "magic"). Practice this with work you've git stash; git stash apply
'd so you can easily git read-tree -um
to get the contents back.
Upvotes: 0
Reputation: 488539
My preferred method is to start by committing now, on the branch you're on. Sometimes this is optional, depending on the remaining steps, so read through them first.
Now that you've committed everything or not, the next step depends on whether it's OK to mess with the current index and work-tree. If not, and you have Git 2.5 or later—preferably 2.15 or later because of a fairly nasty bug first fixed in 2.15—use git worktree add
to create a new work-tree that is set up to work on/in the branch where you want to make these fixes:
# this assumes you're in the top level of your repository
git worktree add ../quick-fix develop
cd ../quick-fix
Here ../quick-fix
is the new work-tree you'll create, which is on branch develop
. By doing cd ../quick-fix
—or opening a new Terminal window on your Mac, perhaps, and going into the directory there—you're now working in your main repository, but on the develop
branch, not the feature branch.
At this point, because you already committed in your feature
branch, you have access to all of the commits you have made in your feature branch and can use git cherry-pick
, or git show | git apply
, or whatever you like—any of Git's tools or any of your system's tools—to work in the develop
branch. If you didn't commit, you can't cherry-pick that last commit, of course.
When you're done updating your develop
branch, you can return to your feature
branch work-tree, run rm -rf ../quick-fix
to remove the other work-tree, and run git worktree prune
to get git worktree
to realize that the other work-tree is now gone. (It's safe to remove once you've made all the commits you intend to make, but you do have to run git worktree prune
at some point to remind Git that you don't have develop
checked out any more.)
If you cannot or do not wish to use git worktree add
, you'll have to work in your main work-tree, so you definitely need to have done that git commit
. You can now git checkout develop
here, work as usual, and commit as usual. Once you're done, git checkout feature
again to get back to your feature.
Either way, you are now back to one work-tree, which has feature
checked out. It's now time to remove the commit you made on feature
, assuming you made one on feature. To remove that one commit, run:
git reset HEAD^
(or use HEAD~
instead of HEAD^
if that's easier for you to type and/or better in your shell / CLI, e.g., if ^
has special meaning to your shell). This removes the commit and resets the index, but keeps the work-tree from the temporary commit you made.
We make a temporary commit if necessary. It is necessary if:
Otherwise, it is not necessary.
We use git worktree add
to create a new work-tree/index pair that allows you to do work in another branch, without having to disturb the main work-tree and Git's main index.
We use git reset
in its default (--mixed
) mode to erase the temporary commit once we are done with it. This also resets the index, so if you are using the index for special tricks—sparse checkout, or git update-index
with --assume-unchanged
or --skip-worktree
—be at least a little bit careful here (but git reset
does coördinate with these).
Upvotes: 1
Reputation: 8527
You could do the following:
git add --interactive
(or more specifically, git add --patch
). More details here. git add --patch
git commit
develop
branch. git stash
git checkout develop
develop
from feature
. git cherry-pick <commit-id-from-step-1-in-feature>
feature
branch and restore other changes. git checkout feature
git stash pop
Upvotes: 1