Search code examples
gitgit-rewrite-historygit-filter-repo

Use git filter-repo to move a subdirectory to root while retaining files in root


I am attempting to split up a repository that contains a client and server into two repositories, and I want to move the respective folders to the root while also keeping the files I already had in root - project readmes, .gitlab-ci.yml, .gitignore, .gitattributes` and so on.

I tried the following line with no luck:

git filter-repo --path .gitattributes --path .gitignore [...] --subdirectory-filter server

The result was something I still don't quite understand; the subdir filter seemed to have worked, but whole commits full of changes ended up missing. The above clearly wasn't the correct way to do this, assuming there even is a correct way.

I hacked my way out of it by using separate calls with --path-rename-match to first move the root files to the server subdir so that they would be moved back to root by a final call with --subdirectory-filter server, but that doesn't seem right at all.


Solution

  • If I understand you correctly, you have something similar to the following project structure.

    client/
    server/
    .gitlab-ci.yml
    .gitignore
    

    If you want to extract the client and server directories each to a dedicated project but retain the files in the root directory for each, you could use the --invert-paths option.

    Using a fresh clone of the original, remove the server directory, set the origin to the new client project and push to the main branch.

    git filter-repo --path server --invert-paths
    

    Take a new clone of the original, remove the client directory, and push to the new server project.

    git filter-repo --path client --invert-paths
    

    If you want to update the path, use --path-rename. This could be used to rename a child directory or to move a child directory up in the hierarchy. For example, if you wanted to remove the enclosing server directory and move the content to the root of the project.

    git filter-repo --path-rename server/foo:foo
    

    Depending on your structure there are probably more elegant ways to achieve what you're looking for, but a combination of the above should solve for your use case.

    Don't forget to process and push any tags you might have as well.