読者です 読者をやめる 読者になる 読者になる

Gitリポジトリのディレクトリ構成を変えて別リポジトリにする

Gitリポジトリの一部を別リポジトリに分離する(リポジトリの分割)

  • あるリポジトリでドキュメントを書いていたらモジュール構成が変わってしまって、複数リポジトリにしたくなった。
    • 複数のモジュールをそれぞれリポジトリを分けて管理するが、仕様については1箇所で管理したいのでドキュメント用リポジトリを新たに作る。
    • ドキュメントと一部のソースが1つのリポジトリにあるのでドキュメントのファイルと履歴だけ引き継いで新しいリポジトリに移したい。

前提条件

  • 基本的に仕事ではgit-flowを採用している。
  • 今回は、まだ作り始めて間もないのでmasterはREADME.mdと.gitignoreしかない。
  • developは、最小限の動くソースとドキュメントが入っている。
  • 書きかけのfeatureブランチがあり、新しいドキュメント用リポジトリに移った後で書き続けたい。

どうやって実現する?

  • 調べるとまずgit filter-branch --subdirectory-filter sub_dir_name HEADというやり方が引っかかる。
    • これをやってみると、sub_dir_nameで指定したディレクトリがトップレベルになってしまうのでダメ。(ディレクトリの階層構造は維持したまま分割したい)
  • git filter-branch --tree-filter 'command'というやり方でリポジトリを改変できるらしい。
    • これはスゴイ。必要なディレクトリだけ残して消してみよう。

分割を実施

  • リポジトリ

    • オリジナルのローカルリポジトリ~/projects/original_repoとします。
    • オリジナルのリモートリポジトリはssh://git@git.example.com:7999/hogehoge/original_repoとします。
    • 新しいドキュメントのリモートリポジトリはssh://git@git.example.com:7999/hogehoge/foo_docとします。
  • masterについては、新しいリポジトリ(foo_doc)をnew_originというリモートリポジトリに登録してからPushするだけ。

    • new_originというリモート設定はどうせ消すので適当。
projects/ $ cd original_repo
original_repo/ (develop) $ git checkout master
Switched to branch 'master'
Your branch is up-to-date with 'origin/master'.
original_repo/ (master) $ ls
README.md
original_repo/ (master) $ git remote add new_origin ssh://git@git.example.com:7999/hogehoge/foo_doc
original_repo/ (master) $ git push new_origin
Counting objects: 4, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 718 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 3 (delta 0)
To ssh://git@git.example.com:7999/hogehoge/foo_doc
 * [new branch]      master -> master
original_repo/ (master) $ git remote remove new_origin
  • developは、ソースは消してドキュメントは改変せずに残しておきたい。
    • filter-branchを使ってsrcを削除してからgcでゴミ掃除して、新しいリポジトリにpushする。
      • git filter-branch --tree-filter 'rm -rf src'で削除。
      • git gcリポジトリをきれいにしてる。
    • 作業用のローカルリポジトリ~/projects/foo_repo_developとします。
original_repo/ (master) $ git checkout develop
Switched to branch 'develop'
Your branch is up-to-date with 'origin/develop'.
original_repo/ (develop) $ cd ..
projects/ $ git clone process_service foo_repo_develop
Cloning into 'foo_repo_develop'...
done.
projects/ $ cd foo_repo_develop
foo_repo_develop/ (develop) $ ls
README.md docs      src
foo_repo_develop/ (develop) $ git filter-branch --tree-filter 'rm -rf src' --prune-empty
Rewrite cf15eff0fdf6e3a8622a93956f624ab152a4ac8d (28/28)
Ref 'refs/heads/develop' was rewritten
foo_repo_develop/ (develop) $ git gc
Counting objects: 515, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (337/337), done.
Writing objects: 100% (515/515), done.
Total 515 (delta 251), reused 291 (delta 133)
foo_repo_develop/ (develop) $ ls
README.md docs
foo_repo_develop/ (develop) $ git remote add new_origin ssh://git@git.example.com:7999/hogehoge/foo_doc
foo_repo_develop/ (develop) $ git push new_origin
Counting objects: 76, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (50/50), done.
Writing objects: 100% (74/74), 1.95 MiB | 0 bytes/s, done.
Total 74 (delta 14), reused 65 (delta 11)
To ssh://git@git.example.com:7999/hogehoge/foo_doc
 * [new branch]      develop -> develop
  • featureブランチは、developと同じようにする。
    • 書きかけのfeatureブランチはfeature/spec_updateで、コミットはしてある(original_repoにPushはしてない)
    • 作業用のローカルリポジトリ~/projects/foo_repo_featureとします。
projects/ $ cd original_repo
original_repo/ (develop) $ git checkout feature/spec_update
Switched to branch 'feature/spec_update'
Your branch is up-to-date with 'origin/feature/spec_update'.
original_repo/ (feature/spec_update) $ cd ..
projects/ $ git clone original_repo foo_repo_feature
Cloning into 'foo_repo_feature'...
done.
projects/ $ cd foo_repo_feature
foo_repo_feature/ (feature/spec_update) $ ls
README.md docs      src
foo_repo_feature/ (feature/spec_update) $ git filter-branch --tree-filter 'rm -rf src' --prune-empty
Rewrite 7b45ae1601a3db09d3bfadcc6107cffc2df08d9e (30/30)
Ref 'refs/heads/feature/spec_update' was rewritten
foo_repo_feature/ (feature/spec_update) $ ls
README.md docs
foo_repo_feature/ (feature/spec_update) $ git gc
Counting objects: 519, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (341/341), done.
Writing objects: 100% (519/519), done.
Total 519 (delta 257), reused 292 (delta 133)
foo_repo_feature/ (feature/spec_update) $ ls
README.md docs
foo_repo_feature/ (feature/spec_update) $ git remote add new_origin ssh://git@git.example.com:7999/hogehoge/foo_doc
foo_repo_feature/ (feature/spec_update) $ git push new_origin
Counting objects: 19, done.
Delta compression using up to 8 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (13/13), 50.55 KiB | 0 bytes/s, done.
Total 13 (delta 5), reused 11 (delta 3)
To ssh://git@git.example.com:7999/hogehoge/foo_doc
 * [new branch]      feature/spec_update -> feature/spec_update
  • 後始末
    • 作業用のfoo_repo_developfoo_repo_featureを削除。
    • 新たにリモートから新しいドキュメント用リポジトリをclone。
projects/ $ rm -rf foo_repo_develop foo_repo_feature
projects/ $ git clone ssh://git@git.example.com:7999/hogehoge/foo_doc foo_doc
Cloning into 'foo_doc'...
remote: Counting objects: 91, done.
remote: Compressing objects: 100% (76/76), done.
remote: Total 91 (delta 23), reused 0 (delta 0)
Receiving objects: 100% (91/91), 2.00 MiB | 0 bytes/s, done.
Resolving deltas: 100% (23/23), done.
Checking connectivity... done.

これで新しいリポジトリで作業を再開出来ました。

元のリポジトリにあるドキュメントは普通に削除してコミットすればよいかと。(履歴を改変するほどのことはないかな)