I have a git repository with the following structure:
.git/
project-root/
abc/
xyz/
And this is what I want:
project-root/
.git
abc/
xyz/
I've been researching how to achieve this and this is what I've found so far: http://git-scm.com/docs/git-filter-branch
According to that, I should achieve what I want with the following command:
git filter-branch --subdirectory-filter project-root -- --all
This rewrites the commit history but most of the non-fast-forward merge commits are lost (see image below).
Is there any way to preserve those commits?
As the docs point out here:
--subdirectory-filter <directory>
Only look at the history which touches the given subdirectory. The result will
contain that directory (and only that) as its project root. Implies
[Remap_to_ancestor].
I believe the problem is that the merge commits do not actually touch the given subdirectory, meaning that subdirectory-filter doesn't even look at them, and thus can't preserve them. Thus, we must use another filter which examines each and every commit. tree-filter is perfect for this, but we must write a script to do what we want done to each commit.
Furthermore, my problem was actually a bit broader than the example, as my project-root
folder had siblings which I wanted to remove. The subdirectory filter would remove them, but in order to do so with the tree filter it was helpful for find
to come to the rescue.
It was convenient to put the commands to be run each time in a separate file.
for a repo structured like this:
.git/
someDirectory/
someFile.txt
otherDirectory/
dontDeleteThisOne/
project-root/
xyz/
abc/
This is what worked for me:
git filter-branch --tree-filter /absolute/path/to/filterScript.sh --tag-name-filter cat -- --all
where /absolute/path/to/filterScript.sh is an executable script containing this:
#!/bin/bash
#save the folder dontDeleteThisOne so it won't get deleted
git mv -fk dontDeleteThisOne project-root && \
#remove everything not under project-root
find . -maxdepth 1 -mindepth 1 '(' -name project-root -o -name '.git' ')' -prune -o -exec git rm -rf --ignore-unmatch '{}' ';' && \
#move the contents of the project-root directory up to root
git mv -fk project-root/{,.[!.],..?}* .;
the resulting repo structure is then this:
.git/
dontDeleteThisOne/
xyz/
abc/
This result is equivalent to the result of git filter-branch --subdirectory-filter project-root
, EXCEPT that merge commits are preserved in the history, as desired.
Of course, this takes MUCH longer than using the subdirectory filter...
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