I've been attempting to detatch a project sub-library into a new repo, but I want to detatch the full folder path, not just the target folder down.
When I filter, the resulting folder structure is parented from the target folder rather than the repository root (or an ancestor I specify) which effectively breaks package structure as classes no longer correctly match folders:
Original repo:
+- repo/master
+- lib1
+- lib2
+- lib3
+- ClassA
+- ClassB
+- ClassC
Git command:
git subtree split -P lib3 -b new-branch
Resulting structure:
+- repo/new-branch
+- ClassA
+- ClassB
+- ClassC
Required structure:
+- repo/new-branch
+- lib3
+- ClassA
+- ClassB
+- ClassC
Is there any way to do this? I've bean reading about rebasing to somehow add the root "core" folder as if it was there before the split, but my git-fu is not strong enough, sadly :(
The other thing I tried is filter-branch to remove other directories, but I could only seem to remove one root-level folder, then git started complaining when I tried others.
Thanks :)
git subtree split
doesn't appear to offer an option for what you want (but it sounds useful so maybe you could contribute one to the project!)
So, there's two ways to do this, depending what you want.
This takes advantage of the fact you want to move to another repo, so we can extract the subtree, and then relocate it in separate steps.
Use git subtree split
to extract the files you want to the an intermediate branch in your repository (you have already done this).
git subtree split -P lib3 -b new-branch
Create a new, empty repository:
git init lib3-repo
cd lib3-repo
git commit --allow-empty -m 'Initial commit'
Add the contents of the intermediate branch as a subtree:
git subtree add -P lib3 repo new-branch
This should rewrite the history again and reinsert the missing directory level.
Every time you want to exchange history between the two repos you'll have to go through the intermediate branch (i.e. subtree split
, then subtree pull
), but it ought to work.
To keep multiple, specific subtrees, you'll need git filter-branch
.
There are lots of ways to pick and choose which commits and files to keep or discard, but this recipe uses --index-filter
to select files without having any access to the contents of the files.
To keep all files in the "lib3" and "src/core" directories, without editing their locations in any way.
git co -b new-branch
git filter-branch --index-filter \
'git ls-files \
| grep -v "^lib3/\|^src/core/" \
| xargs --no-run-if-empty git rm --cached' \
HEAD
The filter code is a shell-script that edits the Git index (we're using --index-filter
, remember).
git ls-files
is the same as ls
except that it lists files in the repo, not in the working tree.
grep -v <pattern>
gives everything that does not match the pattern, and \|
in the pattern is an alternative, so we get the list of files to delete.
xargs --no-run-if-empty
runs a command for each filename in the input from the pipe (unless there aren't any).
git rm --cached
deletes files from the index.
This creates a branch (new-branch
) that has the filtered files you want. You can import them into another repo using a normal pull command:
git init new-repo
cd new-repo
git remote add origin /path/to/old-repo
git pull origin new-branch
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With