1

I have a git branch B, off of branch master, M, the commit history of which looks like this:

M
 \
  B1-B2-B3-B4-B5-B6

M is the master branch, and B is the branch I want to split into 2. I want to end up with this:

  B'1-B'3-B'5
 /
M
 \
  B"1-B"2-B"4-B"6

The problem is that I want commits related to one set of files (which appeared in commits B1, B3, and B5) in branch B', and I want commits related to another set of files (which appeared in commits B1, B2, B4, and B6) in branch B". As you can see, The files are neatly separated into different commits, except for commit B1.

What is the best, cleanest way to separate branch B into branches B' and B" such that each of the new branches only have commit histories for the needed files?

LateCoder
  • 2,163
  • 4
  • 25
  • 44
  • 1
    if you only have a few commits you can manually `cherry-pick` them to a new branch and remove them manually using interactive rebase on an old branch – Arkadiusz Drabczyk Oct 16 '15 at 22:38

1 Answers1

1
  1. Checkout a new branch at commit M (git checkout -b new_branch M).
  2. Run git rev-list --reverse "..master" -- file1 file2 file3... | git cherry-pick --stdin to cherry pick all commits between new_branch (commit M) and master that touch file1, file2, or file3.
  3. Repeat for the second set of files.

Unfortunately, the command in step 2 doesn't follow renames. The following script does but may not order some commits correctly and is overly complicated:

#!/bin/bash

BRANCH="$1"
SINCE=$(git rev-parse HEAD)
shift

# Recursively combine commit logs.
merge() {
    if [[ $# -eq 1 ]]; then
        cat "$1"
        return
    fi

    local out="$1"
    local in="$2"
    shift 2

    merge <(git log --pretty=format:"%H" "${SINCE}..${BRANCH}" --follow "$in" | combine "$out" or -) "$@"
}

git cherry-pick $(merge /dev/null "$@" | tac)
Steven
  • 5,654
  • 1
  • 16
  • 19