One way of doing this is the inverse - remove everything but the file you want to keep.
Basically, make a copy of the repository, then use git filter-branch
to remove everything but the file/folders you want to keep.
For example, I have a project from which I wish to extract the file tvnamer.py
to a new repository:
git filter-branch --tree-filter 'for f in *; do if [ $f != "tvnamer.py" ]; then rm -rf $f; fi; done' HEAD
That uses git filter-branch --tree-filter
to go through each commit, run the command and recommit the resulting directories content. This is extremely destructive (so you should only do this on a copy of your repository!), and can take a while (about 1 minute on a repository with 300 commits and about 20 files)
The above command just runs the following shell-script on each revision, which you'd have to modify of course (to make it exclude your sub-directory instead of tvnamer.py
):
for f in *; do
if [ $f != "tvnamer.py" ]; then
rm -rf $f;
fi;
done
The biggest obvious problem is it leaves all commit messages, even if they are unrelated to the remaining file. The script git-remove-empty-commits, fixes this..
git filter-branch --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'
You need to use the -f
force argument run filter-branch
again with anything in refs/original/
(which basically a backup)
Of course this will never be perfect, for example if your commit messages mention other files, but it's about as close a git current allows (as far as I'm aware anyway).
Again, only ever run this on a copy of your repository! - but in summary, to remove all files but "thisismyfilename.txt":
git filter-branch --tree-filter 'for f in *; do if [ $f != "thisismyfilename.txt" ]; then rm -rf $f; fi; done' HEAD
git filter-branch -f --commit-filter 'if [ z$1 = z`git rev-parse $3^{tree}` ]; then skip_commit "$@"; else git commit-tree "$@"; fi'