Monday, July 11, 2016

Moving a directory in git from one repo to another

I recently moved a directory from one repo to another, and preserved both commit history and last-modified times.

Summarizing what I've learned from a few different sources. These commands should be run in Linux; there is likely a PowerShell equivalent but it was not forthcoming. (Speaking of sharing a repo across platforms, adding a .gitattributes with * text=auto to handle newline characters has worked well for me).

Let's say we want to move "dir/directory_src" of "repo_one" to "dir/directory_dest" of "repo_two".

First let's move all files from directory_src to directory_dest and remove all files outside of directory_dest.

mkdir ~/repos
cd ~/repos
git clone https://servername/repo_one
cd repo_one
# intentionally prevent ourselves from pushing changes
git remote rm origin
# run the following and look at the resulting filenames to see if they look correct
git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-"
# rewrite history so that "dir/directory_src" moves to "dir/directory_dest"
git filter-branch --index-filter  'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$ git update-index --index-info && mv "$" "$GIT_INDEX_FILE"' HEAD
git filter-branch --index-filter  'git ls-files -s | sed "s-\tdir/directory_src/-\tdir/directory_dest/-" | GIT_INDEX_FILE=$ git update-index --index-info && if [ -f "$" ]; then mv "$" "$GIT_INDEX_FILE"; fi' HEAD
(move or delete the directory .git/refs/original)
cd ~/repos/repo_one

# run the following and look that files in dir/directory_dest aren't included
git ls-files | egrep -v ^dir/directory_dest/
# rewrite history and remove all other files
git filter-branch --tree-filter 'rm -rf $(git ls-files | egrep -v ^dir/directory_dest/)' -- --all
# delete empty commits (optional)
git filter-branch --commit-filter 'git_commit_non_empty_tree "$@"' HEAD
Now let's copy the files into repo_two.
cd ~/repos
git clone https://servername/repo_two
cd repo_two
git remote add from_repo_one ~/repos/repo_one
git pull from_repo_one master
git remote rm from_repo_one
# after confirming that everything looks right,
# run git push.
"repo_two" should now contain "dir/directory_dest" and all of its contents.

Note: the tree filter removal script will leave filenames that contain spaces or tabs.


Moving files from one Git repository to another

How can I move a directory in a Git repo

No comments: