Image

Communities

Writing
Writing
Codidact Meta
Codidact Meta
The Great Outdoors
The Great Outdoors
Photography & Video
Photography & Video
Scientific Speculation
Scientific Speculation
Cooking
Cooking
Electrical Engineering
Electrical Engineering
Judaism
Judaism
Languages & Linguistics
Languages & Linguistics
Software Development
Software Development
Mathematics
Mathematics
Christianity
Christianity
Code Golf
Code Golf
Music
Music
Physics
Physics
Linux Systems
Linux Systems
Power Users
Power Users
Tabletop RPGs
Tabletop RPGs
Community Proposals
Community Proposals
tag:snake search within a tag
answers:0 unanswered questions
user:xxxx search by author id
score:0.5 posts with 0.5+ score
"snake oil" exact phrase
votes:4 posts with 4+ votes
created:<1w created < 1 week ago
post_type:xxxx type of post
Search help
Notifications
Mark all as read See all your notifications »
Q&A

Welcome to Software Development on Codidact!

Will you help us build our independent community of developers helping developers? We're small and trying to grow. We welcome questions about all aspects of software development, from design to code to QA and more. Got questions? Got answers? Got code you'd like someone to review? Please join us.

How do I split a feature branch into two?

+10
−0

I just realized that I started writing code for what was going to become a second independent feature on the branch I was already working on.

--A--B--C  main
         \
          `Q--R--S--X--Y--Z  feature-1

How do I split my work into two different branches diverging from main?

--A--B--C  main
        |\
        | `Q--R--S  feature-1
         \
          `X--Y--Z  feature-2

My question is different from this one because in that question, upstream already merged feature-1 into their main.

History

0 comment threads

3 answers

+6
−0

In the most general case[1] that you're asking about, you want the three-term rebase --onto.

First, make your feature-2 branch:

git switch --create feature-2
--A--B--C  main
         \
          `Q--R--S--X--Y--Z  feature-1, feature-2

Next, move feature-2's commits. Notice the ^ that I use to mark the parent of the last commit I want to move.

git rebase --onto main feature-2 X^
--A--B--C  main
        |\
        | `Q--R--S--X--Y--Z  feature-1
         \
          `X'-Y'-Z' feature-2

Finally, move feature-1 back where it belongs:

git switch feature-1
git reset --hard S
--A--B--C  main
        |\
        | `Q--R--S  feature-1
         \
          `X'-Y'-Z' feature-2

The Git rebase documentation for the three-term --onto starts in a position where feature-1 is already where it needs to be.

For whatever reason, I'm much more likely to still be on feature-1 and realize that I've started making a second feature. For that I'd rather use the ^ on the last commit I need to move rather than trace the lines of a git log --graph down to the parent commit.


  1. There are some tricks in simplified cases involving cherry picking instead of a rebase. See hkotsubo's excellent range cherry-pick answer for more. ↩︎

History

0 comment threads

+4
−0

The rebase solution proposed in the other answer is the most straightforward way (and the one I'd use as well).

Just to provide an alternative, you can also use cherry-pick.

First, let's create branch feature-2 from main, and switch to it:

git switch --create feature-2 main

Now we have:

--A--B--C  main, feature-2 (HEAD)
         \
          `Q--R--S--X--Y--Z  feature-1

And feature-2 is the current branch. Then we cherry-pick all commits from X to Z:

git cherry-pick X^..Z

Obviously, replacing X and Z by their respective hashes. Note that after X there's a ^, so X^..Z corresponds to all commits reachable from Z but not reachable from X's parent (in this case, it'll be commits X, Y and Z).

This will apply commits X, Y and Z to feature-2, resulting in:

--A--B--C  main
        |\
        | `Q--R--S--X--Y--Z  feature-1
         \
          `X'-Y'-Z' feature-2

Finally, we just move feature-1 to S (this part is the same as the other answer):

git switch feature-1
git reset --hard S

And the repository will look like this:

--A--B--C  main
        |\
        | `Q--R--S  feature-1
         \
          `X'-Y'-Z' feature-2

Alternatively, you can also list the commits one by one, like git cherry-pick X Y Z. But if there are too many commits, this can be tedious and very error-prone, and the two-dots syntax is a better solution.


As I said, using rebase is more straightforward, I'd just wanted to show an alternative. After all, you can think of rebase as a faster way to cherry-pick a specific set of commits.

History

0 comment threads

+2
−0

A simple and flexible approach is to use interactive rebasing (rebase -i) which allows you to pick / drop / squash individual commits. In this case you would want to duplicate the branch (just check out a new branch on HEAD) and perform the interactive rebase.

# on branch both-features
git switch -c feature1
git rebase -i HEAD~6  # consider last 6 commits, alternatively put the commit id of the commit BEFORE the first you want to consider
# select the relevant commits for feature1, complete the rebase
git switch -  # back to last branch
git switch -c feature2
git rebase -i HEAD~6
# select the relevant commits for feature2, complete the rebase
git branch -D both-features  # optional: delete original branch
History

0 comment threads

Sign up to answer this question »