Git: Move Files Retaining History
How to move files among git repository retaining the history
Sometimes ago we need to create a new product, that shares many functionalities with the existing one. The first idea was to extract some of the code in a library, to be then imported and used independently in the two projects.
After creating the repository for the library, the first action I would have done was copying the files from one repo to another. Well, it works like a charm, it’s fast and it doesn’t require any particular expertise, a normal drag and drop operation. What’s best? It could be a very effective approach, but …
During a PR review, I learnt there was a cleaver way to perform such operation and I wasn’t even aware of such possibility before:
Moving files from one git repository to another retaining the history.
Moving a single directory
From the source repository:
git clone <src_repository> <src_repository>_clone
# clone the src repository in new folder
cd <src_repository>_clone
git remote rm origin
# remove the origin to avoid disasters in case of error
git filter-branch --subdirectory-filter <directory> -- --all
# the magic, rewrite the git history
mkdir <directory>
mv * <directory>
# create the directory and move everything inside
In the destination repository:
git remote add <src_repository> <git repository A directory>
# clone clone the repository, if not previously done
git pull repo-A master --allow-unrelated-histories
# Import the src repository code into the dst repository
git mv <directory_1> <desired_position>
# Optionally, move the just imported files, into the desired position in the new repository
Moving code file-by-file
The operations required to move many files at once are conceptually the same. The extraction of the files to be moved will be performed by means of the following script (from Tom Hacohen blog article):
After changing the script to include the files to be imported, it must be executed just before the git filter-branch ...
command; this will create a single folder into the repository. So, to recap:
Download the script, and change it according with the files to be copied.
From the source repository:
git clone <src_repository> <src_repository>_clone
# clone the src repository in new folder
cd <src_repository>_clone
git remote rm origin
# remove the origin to avoid disasters in case of error
Execute the script previously modified here, so assuming the script has been saved in the parent folder:
bash $(pwd)/../git-move.sh
At this point, the repository contains only the folder and the files specified in the script.
git filter-branch --subdirectory-filter <directory> -- --all
# the magic, rewrite the git history
mkdir <directory>
mv * <directory>
# create the directory and move everything inside
In the destination repository:
git remote add <src_repository> <git repository A directory>
# clone clone the repository, if not previously done
git pull repo-A master --allow-unrelated-histories
# Import the src repository code into the dst repository
git mv <directory_1> <desired_position>
# Optionally, move the just imported files, into the desired position in the new repository
Conclusions
That easy? Yep. Despite the many commands to be run, the advantage of maintaining the history is priceless, especially when we need to investigate some issue, file history could be very useful.
Tip: The process could seem counterintuitive, so I suggest to give it a try with some test repository. After the first time, everything will be much clearer.