Skip to main content
  1. Blog/

Git: Move Files Retaining History

·564 words·3 mins
Antonio Morrone
Author
Antonio Morrone
Staff/Tech Lead Software Engineer. Building software since 2013, in blockchain protocols & Web3 infrastructure since 2021. Security-minded. Remote from Italy.

Some time ago we needed to create a new product that shares many features with the existing one. The first idea was to extract some of the code into a library, to be then imported and used independently in the two projects.

After creating the repository for the library, the first thing I would have done was copy 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 more? It could be a very effective approach, but …

During a PR review, I learnt there was a clever way to perform this operation, and I wasn’t even aware such a thing was possible 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 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’s 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 in the repository. So, to recap:

Download the script, and change it according to 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 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 can be very useful.

Tip: The process could seem counterintuitive, so I suggest giving it a try with a test repository. After the first time, everything will be much clearer.

Resources
#