社内のサーバーにホスティングしていた巨大な Git リポジトリを GitHub に移行する機会があったので、そのときにやったことを備忘録代わりとして書きます。
リポジトリの概要
ちょっと、巨大というと誇張しすぎですが、対象のリポジトリは以下のようになっています。
- Unity プロジェクト
- コミットの数は、9,000 弱
- リポジトリのサイズは 5 GB 程度
- プロジェクトの期間は 3 年ほど
- 所々 で 100 MB 超えのファイルがコミットされている
- 一部のアセットは既に GitHub のリポジトリとなっていて、 LFS で管理されている
要望
- このリポジトリを、ひとつの GitHub リポジトリとして統合したい
- また、そのときにアセット類は LFS で管理したい
この要望に対応するため、スクリプトを書いてゴニョゴニョします。
やるべきことは、以下の 2 点です。
- 社内サーバーにホスティングされている Git リポジトリを GitHub に移行する
- アセット類のリポジトリ統合する
社内サーバーにホスティングされている Git リポジトリを GitHub に移行する
通常であれば、社内サーバーのリポジトリをローカルにクローンして、リモートリポジトリとして GitHub を追加し、 Push で OK なのですが、100 MB 超えのファイルのコミットがあると GitHub が受け付けてくれず、 Push が失敗してしまいます。
そこで、伝家の宝刀 git filter-branch
コマンドを使ってリポジトリの履歴を書き換えてしまいます。
すべてのブランチをチェックアウトする
master
と develop
ブランチのみ、であれば単純なのですが、 master-XXX
など、大量のブランチが残っていて、どのブランチが必要で、どのブランチが不要なのかが分からない。という状況だったので、とにかく全てのブランチをチェックアウトすることにしました。
以下のようなスクリプトを書いて実行します。
#!/bin/bash
branches=$(git branch -r --no-merged | grep -v HEAD | sed -e "s/origin\///")
for branch in ${branches}; do
echo ${branch}
git checkout -b ${branch} origin/${branch}
done
過去のコミットに Git LFS を対応させる
過去のコミットのバイナリファイルなどを LFS で管理するために git filter-branch
で履歴を書き換えていきます。
LFS 化するファイルの拡張子は予め決めておきます。
#!/bin/bash
# LFS 化するファイル拡張子を列挙しておく
extensions='
png
jpg
mp3
'
# 列挙した拡張子からコマンドを作成する
tree_filter_command="git lfs track "
for extension in ${extensions}; do
tree_filter_command=${tree_filter_command}\"*.${extension}\"" "
done
# git filter-branch を適用する
git filter-branch \
--tree-filter "${tree_filter_command} >/dev/null" \
--index-filter "rm .gitattributes" \
--tag-name-filter cat -- \
--all
このスクリプトを実行して、全てのブランチを書き換えていきます。
この処理に一晩くらいかかりました。
あとは、通常どおり、リモートブランチを切り替えて、プッシュすれば移行は完了です。
リモートブランチを切り替える
$ git remote remove origin
$ git remote add origin git@github.com:foo/bar.git
プッシュ
$ git push -u origin master
$ git push --all origin
掃除
最後にローカルリポジトリを掃除します。
$ rm -rf .git/refs/original/
$ git reflog expire --expire=now --all
$ git gc --aggressive --prune=now
アセット類のリポジトリを統合する
まずは、リソースのリモートリポジトリを登録します。
$ git remote add resource git@github.com:foo/bar-resource.git
リソースの master
ブランチをチェックアウト(名前は本流のブランチとは別にします)
$ git fetch resource
$ git checkout -b feature/resource-master resource/master
リソースのブランチを本流のブランチにマージします。
この時、 --allow-unrelated-histories
を付けることで別のリポジトリを統合することができます。
いきなり master
にマージするのは怖いので feature/merge-resource
というマージ用のブランチを用意して作業しました。
$ git checkout master
$ git checkout -b feature/merge-resource
$ git merge --allow-unrelated-histories feature/resource-master
最後にリソースディレクトリの移動などを行って完了です。
まとめ
- 履歴の書き換えには、
git filter-branch
を使う - 別のリポジトリを統合するには、
--allow-unrelated-histories
を使う
Git はローカルで作業して失敗してもリモートには影響がないので、こういった作業も比較的気軽に行えて便利ですね。