Git – Moving folders with history and all related branches

In my case, I had to move 2 different subfolders with their histories and all effected branches to the new bare repository. I had a folder structure like below:

old repo
|___ folder1
|___ folder2
|___ folder3
| |___ folder3_1
| |___ folder3_2
| |___ folder3_3
| |___ folder3_4
|
|___ folder4

And what I need was in my new bare repository:

new repo
|___ folder3_2
|___ folder3_4

To achieve this task, I cloned my old repository content like it’s my new repository. With this way, I don’t need to move files from one folder to another folder. If you afraid to break something in old repository, don’t! As long as you don’t force a git push, you are safe.

git clone git@github.com:aakin/old-repo.git new-repo && cd new-repo

Then I removed everything and edit git history except the directories I want to keep:

git filter-branch --index-filter 'git rm --cached -qr --ignore-unmatch -- . && git reset -q $GIT_COMMIT -- folder3/folder3_2 folder3/folder3_4' --prune-empty -- --all

Above command is the most important one to understand. Because it is where the magic happens. We used filter-branch option to rewrite our git history. Inside the quotes, we deleted everything in the repository and then we just reset the folders we wanted keep. $GIT_COMMIT is a variable that can be used in filter-branch command. And don’t forget to change folder3/folder3_2 folder3/folder3_4 part for the directories you want to keep. Also, I want to mention –all and –prune-empty arguments. Thanks to –all argument, we are able to filter all the branches, not only the checked out one. And –prune-empty helps us to eliminate empty commits in these branches.

After this command succeeded, my folder structure looks like this:

new repo
|___ folder3
|___ folder3_2
|___ folder3_4

As you can see, I need another step to achieve desired folder structure. Basically, I’m gonna use the same logic but this time with different filter option.

git filter-branch -f --subdirectory-filter folder3 --prune-empty -- --all

This time I want to mention -f option. When we first run the filter-branch command, we actually did very dangerous thing: we rewrote the git history. Because of this, git created a backup in case that we want to rollback from that command. When we run this command in second time, if we don’t force it, command is going to fail because there is already one backup file from last command run. With forcing, we bypassed this error. One other option is to delete this backup file manually.

After last command, I managed to create my desired folder structure.

new repo
|___ folder3_2
|___ folder3_4

As a last step, I need to push my branches to new repository. But, if I change my remote repository address to new one right now, I will have detached head branches for all the existing ones. That is why we need to first checkout each branches and create a local copy on our new repository.

for remote in `git branch -r | grep -v master`; do git checkout --track $remote; done

Now we can change our remote repository and push:

git remote rm origin
git remote add origin git@github.com:aakin/new-repo.git
git push --all

–all argument in push command helps us to push all branches in one command. Now if you want you can remove unnecessary branches from your local computer.

Note: If you get an error during last push, you’ve probably initialized repository with some files. To solve this, you need to merge or force the push.

Developers Rock!!!

Advertisements
This entry was posted in Git. Bookmark the permalink.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s