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:
| |___ folder3_1
| |___ folder3_2
| |___ folder3_3
| |___ folder3_4
And what I need was in my new bare repository:
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 email@example.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:
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.
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 firstname.lastname@example.org: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.