Extract git subdirectory while keeping history WITH renames
I'm splitting a big source tree into two separate components and a shared submodule. In order to prepare for this split, I first moved the shared stuff into a single "common" directory, updated all the references, and made a commit. So far so good. Now I'd like to extract that directory into a submodule.
Normally I'd do this with
git filter-branch --subdirectory-filter
开发者_JS百科
But in this case, all the interesting history happened outside that subdirectory, so the history gets lost.
I understand that it doesn't make sense to keep the full history, since that wouldn't be filtering out any data at all. But I'm not really going for the ability to go back in time and build, I just want to be able to look at the commits each file was a member of.
Is there a way to keep the filter-branch behavior while keeping the history of the individual files?
Not really. --subdirectory-filter
is a bit of a special case in that it's actually modifying the contents of trees significantly (since it's moving things up one or more directory nesting levels).
As such, there's not really a good mapping between files that are outside of the subdirectory you're filtering on and trees that could be stored as part of commits in the result.
Remember that filter-branch
is completely rewriting your history - the output is an entirely new set of commits, and there aren't any "linkages" to the old commits, so any extra information has to be expressible as part of the new commits.
Here's how I just solved a similar problem. I had started a project in a quasi-private "misc" repository, renamed some files, and then I wanted to upload the project to GitHub at https://github.com/kragen/aikidraw.
$ git clone misc aikidraw
$ cat > aikidraw-wanted
aikidraw.js
aikidraw.html
caposketchra.html
caposketchra.js
jquery-1.2.6.js
^D
$ cd aikidraw
$ git filter-branch --tree-filter 'bash -c "comm -23 <(/bin/ls | sort) <(sort ~/devel/aikidraw-wanted) | xargs rm -rf"' HEAD
That seems to have worked okay so far, aside from not deleting dotfiles (like .git
, good, and .gitignore
, bad) but apparently my version of Git (1.6.0.4) doesn't have git filter-branch --prune-empty
. So now I clone the new, smaller repo (in order to make it faster to copy it over the network) and copy the repo to another machine that has Git 1.7.2.5 on it:
$ time git clone aikidraw aikidraw-smaller
$ du -sh aikidraw/.git aikidraw-smaller/.git
8.6M aikidraw/.git
1.2M aikidraw-smaller/.git
$ time rsync -Pav aikidraw-smaller panacea.canonical.org:devel/aikidraw/
real 1m23.251s
And then on panacea.canonical.org:
$ cd ~/devel/aikidraw/aikidraw-smaller # Oops. I hate rsync sometimes.
$ git checkout # otherwise I get "Cannot rewrite branch(es) with a dirty working directory."
$ git filter-branch --prune-empty HEAD
$ cd ../..
$ mv aikidraw i-hate-rsync
$ mv i-hate-rsync/aikidraw-smaller/ aikidraw
Then back on my netbook:
$ mv aikidraw aikidraw-big
$ git clone panacea.canonical.org:devel/aikidraw
$ du -sh aikidraw/.git
268K aikidraw/.git
Now, if you were doing this with two directories instead of five files, you might at this point want to rename everything inside the remaining subdirectory into the root of the repository, using git mv
. In my case I've already done my renaming.
$ git remote add github git@github.com:kragen/aikidraw.git
$ git push github master
Hope this helps!
精彩评论