Table of contents
This note describes how to extract part of a Git repository and graft it into another repository.
The process detailed here is not the only one nor the fastest. But it's straightforward enough and requires nothing but standard Git.
If you want to follow along, you can setup the scene running the script in the appendix section at the end.
# The scene
Let's set up the scene. We have a repository
forest with information about trees. We also have a repository
garden with information about trees we like.
The goal is to copy the
garden keeping its history, i.e. preserving all commits related to
apple-tree but nothing else from the
To twist it a bit, although we are interested in
apple-tree we don't want in our
forest looks as follow:
forest ├── almond-tree │ └── index.md ├── apple-tree │ ├── domestic-apple │ │ ├── index.md │ │ ├── picture1.jpg │ │ └── picture2.jpg │ ├── forest-apple │ │ ├── index.md │ │ └── picture1.jpg │ └── history.md ├── elm-tree │ └── index.md ├── mango-tree │ └── index.md ├── oak-tree │ └── index.md └── README.md
garden looks as follow:
garden ├── hazel-tree │ └── index.md ├── orange-tree │ └── index.md └── plum-tree └── index.md
And we want our
garden to look like:
garden ├── apple-tree │ └── domestic-apple │ ├── index.md │ ├── picture1.jpg │ └── picture2.jpg ├── hazel-tree │ ├── ... │ ... ├── orange-tree │ ├── ... │ ... ├── plum-tree │ ├── ... └── ...
This is a destructive process that will rewrite history to allow us to preserve the information about historic changes.
First, clone the
forest repository to a dedicated copy to preserve the original
Then, prepare the
The above registers a remote
pruned-forest pointing to the recently created
pruned-forest repository, creates a branch
apple-tree and switches to it. This way, if the result is not satisfactory you will only need to remove this branch.
# Pruning the forest
Now it's time to remove anything we don't want to preserve. We need to change the shape of
apple-tree, our target directory, to our final needs.
This scenario is fairly simple, in more complex situations you will have to move and remove files and directories until you end up with a directory that contains the desired result.
# Rewriting history
This is the destructive step. We are working with the copy
pruned-forest so it's fine.
Note that Git (version 2.25.1) will prompt a warning. Ignore it for this task.
WARNING: git-filter-branch has a glut of gotchas generating mangled history rewrites. Hit Ctrl-C before proceeding to abort, then use an alternative filtering tool such as 'git filter-repo' (https://github.com/newren/git-filter-repo/) instead. See the filter-branch manual page for more details; to squelch this warning, set FILTER_BRANCH_SQUELCH_WARNING=1.
# still in pruned-forest
pruned-forest has exclusively the content of the
pruned-forest └── domestic-apple ├── index.md ├── picture1.jpg └── picture2.jpg
Check the history with:
You'll see all commits that at some point affected any of the contents of
apple-tree are preserved but no other commit is there.
Finally, we want to graft this into the
garden inside an
apple-tree directory so let's prepare it this way:
# still in pruned-forest
# Grafting the garden
Last step. The
--allow-unrelated-histories is the key to allow this kind of grafting to happen.
And with that, we are able to graft a different history into
garden preserving the history from
The following bash script creates both
#! /usr/bin/env bash ## Create the forest ## Create the garden
Git grafting is a big and diverse topic. This note barely scratches the surface. Check git replace to see an example of splitting a repository in two reconnectable parts.