13

I'm splitting out a Git repository using the --subdirectory-filter option of filter-branch which is working great except it pulls everything up to the root of the repository.

I currently have

ABC - DEF - GHI - JKL - MNO 

And the result of this command:

git filter-branch -f --subdirectory-filter ABC/DEF --prune-empty -- --all 

Generates this:

GHI JKL 

Where what I really want is this:

ABC - DEF - GHI - JKL 

I can't see anything in the Git docs that shows a filter option which preserves (or sets) the directory structure and I haven't been able to find a command I can run after the filtering to remap the structure to how I want it.

Is this possible?

4 Answers 4

3

I found an answer here which does the trick.

The command:

git filter-branch -f --index-filter 'git ls-files -s \ | sed "s-\t-&ABC/DEF/-" \ | GIT_INDEX_FILE=$GIT_INDEX_FILE.new git update-index --index-info \ && mv $GIT_INDEX_FILE.new $GIT_INDEX_FILE' 

works perfectly

Sign up to request clarification or add additional context in comments.

5 Comments

Worked perfectly for me too. Thanks for posting it. I should add that this command doesn't replace the --subdirectory-filter command; you still execute that. This command replaces the git mv * ... command that other documents recommend. But without the ugly massive commit the git mv would have generated.
Unfortunately this doesn't work for me. I'm on Windows running git-bash, and it fails saying it can't find index.new ($GIT_INDEX_FILE.new). I wish I could use this solution.
@BerinLoritsch $GIT_INDEX_FILE is an environment variable which most tools I've used sets for you; Both MsysGit and CygWin along with several git bash for Windows alternatives. What's your exact setup?
It's using bit-bash on windows. It's not behaving like a full unix system would.
On Windows git-bash, this appears to create a new subdirectory ("ABC/DEF") and then move all the files into that subdirectory, so instead of "ABC/DEF/GHI" you end up with "ABC/DEF/", which contains the existing repository, having moved everything into that subdirectory while removing nothing.
2

I've only given this cursory testing myself, but I think you can use git filter-branch with --tree-filter to rewrite the branch, but removing all files apart from those in the subdirectory ABC/DEF, with something like the following:

git filter-branch --tree-filter \ 'find . -path ./ABC/DEF -prune -o -type f -print0 | xargs -0 rm -f' \ --prune-empty HEAD 

Note that the find command only removes files, not directories, but since git doesn't store empty directories, this should still produce the result you want.

3 Comments

Thanks for that. With the size of this repository I really need something that works off the index (index-filter rather than tree-filter). Your answer is definitely on the right track but would have been very slow in my situation. I found an answer but I'm not sure of the etiquette of accepting my own answer when this would probably suit most cases.
Answering your own question is encouraged and if it's the answer that solved your question you should accept it too. You have to wait 48 hours though. The --tree-filter version command I tested above was indeed pretty slow, but typically with filter-branch it's a one-off thing, so I wouldn't care if I had to leave it going overnight...
This is the only answer I could find that a) Left the existing directory structure intact and b) Actually filtered out everything that wasn't in the target directory. It took ages, but when I asked it to filter "/ABC", I actually had "/ABC/file.name", not "/file.name" or "/ABC/ABC/file.name", and that's all that matters.
0

Use:

git filter-branch -f --to-subdirectory-filter ABC/DEF --prune-empty -- --all 

1 Comment

While this code may answer the question, it is recommended to give an explaination for what it is doing and how it solves the problem
0

2020+: A better option, considering git filter-branch or BFG are obsolete after Git 2.22 (Q4 2019) or more, is to use git filter-repo (python-based, to be installed first):

git filter-repo --path ./ABC/DEF 

That will preserve the directory structure.

Note: git filter-repo will force you to make a backup first, then proceed to change the history of the local repository you are filtering.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.