Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

git: Split history of some files into a separate branch

Tags:

git

branch

rebase

Say I introduced <feature.c> a while ago and now notice it shouldn't have been part of my main branch but rather a branch feature. Is it possible to use e.g. git-filter-branch to automatically move all of <feature.c>'s history out of my main branch into the feature branch?

like image 371
Tobias Kienzler Avatar asked Nov 25 '10 14:11

Tobias Kienzler


2 Answers

It sounds like you're doing something fairly insane! :)

That said, I see a few options, none of which are particularly automated.

  1. If you've got a ton of commits with that file present, just admit the mistake, make a new branch off of your HEAD, and put further commits with that feature into that branch until they're stable. If you're repo's shared, this becomes the only real option. No one wants to have divergent history, especially if that feature was committed deep in the history.

  2. If you're only talking about 10 or so commits that have actually touched that file, and they're fairly recently committed without many other commits interleaved, you could check out a new branch on HEAD, and revert the branch you don't want this feature in back to before you added the feature, and then cherry pick commits you need out of the feature branch until you're ready to commit them all in at a later date.

  3. If you're dealing with a ton of history, lots of interleaved commits, and you really don't want to have that feature present at all, you could write up a little shell script that takes the output of git log and cherry picks it into a new branch. Something along the lines of:

    $ cd git-repo
    $ git checkout -b feature-x
    $ the-perfect-shell-script `git log --pretty=format:"%H" path/to/feature.c`
    

    Once you have that feature branch with all of the commits cherry picked out, you can then use git filter-branch to filter out all of the commits that reference that file. The man page has a simple example that does exactly that.

    Once you've got that, you can then git rebase feature-x --onto <filtered-branch> and you should be good to go.

    Of course that should be quite discouraged, especially if any of that is published.

like image 60
Tim Visher Avatar answered Nov 13 '22 06:11

Tim Visher


Ok, here's my go:

Had I not commits manipulating <feature.c>, I could have branched of from <feature.c>'s first commit and then used git cherry-pick with git log in a loop, as suggested by Tim Visher. Starting from master I guess this should work:

#!/bin/bash
# create the feature branch starting from feature.c's first commit
FIRSTCOMMIT=$(git log --pretty=format:"%H" feature.c | tail -n1)
git checkout -b feature $FIRSTCOMMIT

# find all commits concerning feature.c...
for i in $(git log feature..master --reverse --pretty=format:"%H" feature.c)
do
  # ... cherry-pick them ...
  git cherry-pick $i
  # ... and copy ONE modified tag of it if existing
  git describe --tags --exact-match $i && xargs taghelperscript
done
# now eliminate feature.c from master
git filter-branch --prune-empty --tag-name-filter "cat" --index-filter 'git rm --cached --ignore-unmatch feature.c' $FIRSTCOMMIT..master

With taghelperscript being something like git tag prefix.$1 (maybe this can be done better?). The tagging part probably only works for the lightweight tags I use. Also be advised that this does not work if <feature.c> has been renamed at some point, and if it existed already in the initial commit this might either cause two separate histories, or (my guess) a commit in master which contains the deletion of <feature.c> which is likely to cause a merge conflict or confusion later.


The trouble is, some of my commits modify other files, thus causing git cherry-pick to trigger an unresolved merge, or introduces these other files. So instead, I'll try some git filter-branch magic. Later. Stay tuned...

like image 1
Tobias Kienzler Avatar answered Nov 13 '22 07:11

Tobias Kienzler