Information in this page is outdated. Last update was made on 19 June 2017.
How to use git filter-branch to move a directory from one repository to the other including its history of commits
Table of contents
Scenario
Assumption
- Original repository:
A
. - Other repository:
B
. - Directory in
A
that we need to copy with commit history:www/modules/custom/sms_gateway
- Directory in
B
that we need to copy into the files and commits fromA
: git root directory - Local path:
/var/www/demo/
.
Case
Suppose, you want to move a directory from a repository A
to be merged into
another repository B
. The noob approach would be just to copy the contents
of the said directory from A
to (that of) B
.
That however, will render all efforts of commits made in the direcotry in A
to just one commit in B
. This is something we want to avoid especially to
open source projects where commit history is important to the developers who
made them, and to the company they work in as credits.
Suppose, you want to retain git commit history made in repository A
in that
particular directory. This can be done by one-by-one re-committing changes
made in that directory in A
to B
.
Soon you will realize that you re-wrote the commit history made in the said
directory in A
to B
with you as the committer, author, and the dates of the
commits were done just now, not on the date those commits were originally made
in A
.
That can be solved, by setting up the env variable for git committer name, author name, and the date committed. This however, would be very tedious.
Solution
Let’s use git’s filter-branch
command.
Steps
-
Clone
A
. Let’s clone it in/var/www/demo/
. Any path will do. I would suggest to clone a new one, than to use existing one where you currently work on.git clone A-git-url
-
Go to
A
.cd /path/to/A
-
Checkout to the branch you want. E.g.,
develop
. -
Let’s remove the remote origin. This is to make sure we don’t accidentally push changes we will about to make.
git remote remove origin
-
Now, let’s do the filter-branch. This process will move the directory set in the filter-branch command, to the root of the repository. This means, all other files will be removed, and the ones that are left are the ones in the branch set in filter-branch.
git filter-branch --subdirectory-filter www/modules/custom/sms_gateway -- --all
-
Now, before we go ahead and merge the files and commit history to
B
, we need to have a new clone repositoryB
first. Same withA
, it’s better that we clone a new one, than working on the existing one.git clone B-git-url
-
In
B
git checkout to the branch of your choice and then branch out to a new branch to do the merging of commits there. You can do this to the original branch, but it’s better to branch out to a different branch. There is no need to remove the remote origin from the newly clonedB
. Let’s say, you branched out tofeature-commits-from-A
branch. -
In
B
add a new remote to be named to whatever you want, e.g.,from-A
. Confirm remotes, withgit remote -v
. You should see, aside fromorigin
, thefrom-A
. The remote url would be the local path of the newly clonedA
earlier. The easiest way to do this is to go to /path/to/A and thenpwd
in its root.cd /path/to/A pwd cd /path/to/B git remote add from-A /path/to/A
In command
pwd
above, just note or copy the its result as you need this in the last command to be substituted for/path/to/A
.Again, in the last command, the
from-A
is the alias of the remote you just added./path/to/A
is the local path of you newly clonedA
you did a while ago. Note that you should do this inside the newly clonedB
. -
Now, let’s do the merging.
git fetch from-A git merge from-A/feature-commits-from-A
If you encounter some error related to unrelated histories, you should add
--allow-unrelated-histories
in the second command.git merge from-A/feature-commits-from-A --allow-unrelated-histories
-
Confirm commit histories. In
B
in branchfeature-commits-from-A
, justgit log
and see the commits. You can check back inA
for reference. -
If you are satisfied, merge back branch
feature-commits-from-A
to the original branch, e.g,develop
, or8.x-1.x
- to the branch wherefeature-commits-from-A
was branched out.git checkout 8.x-1.x git merge feature-commits-from-A git log
-
After all of the above, we now need to push to
B
. This is just a simple push.git push origin 8.x-1.x
That’s it.
Summary
Below are all commands used.
# Go to the local path where we want to do stuff.
cd /var/www/demo/
# Clone repos.
git clone A-git-url
git clone B-git-url
# In A.
cd /path/to/A
git checkout develop
git remote remove origin
git filter-branch --subdirectory-filter www/modules/custom/sms_gateway -- --all
# In B.
cd /path/to/B
git checkout 8.x-1.x
git checkout -b feature-commits-from-A
git remote add from-A /path/to/A
git fetch from-A
git merge from-A/feature-commits-from-A --allow-unrelated-histories
git checkout 8.x-1.x
git merge feature-commits-from-A
git log
git push origin 8.x-1.x
For clarification(s), and/or question(s), please let me know.