LoginSignup
20
7

More than 5 years have passed since last update.

巨大なリポジトリを GitHub に移行した話

Last updated at Posted at 2017-12-05

社内のサーバーにホスティングしていた巨大な Git リポジトリを GitHub に移行する機会があったので、そのときにやったことを備忘録代わりとして書きます。

リポジトリの概要

ちょっと、巨大というと誇張しすぎですが、対象のリポジトリは以下のようになっています。

  • Unity プロジェクト
  • コミットの数は、9,000 弱
  • リポジトリのサイズは 5 GB 程度
  • プロジェクトの期間は 3 年ほど
  • 所々 で 100 MB 超えのファイルがコミットされている
  • 一部のアセットは既に GitHub のリポジトリとなっていて、 LFS で管理されている

要望

  • このリポジトリを、ひとつの GitHub リポジトリとして統合したい
  • また、そのときにアセット類は LFS で管理したい

この要望に対応するため、スクリプトを書いてゴニョゴニョします。

やるべきことは、以下の 2 点です。

  1. 社内サーバーにホスティングされている Git リポジトリを GitHub に移行する
  2. アセット類のリポジトリ統合する

社内サーバーにホスティングされている Git リポジトリを GitHub に移行する

通常であれば、社内サーバーのリポジトリをローカルにクローンして、リモートリポジトリとして GitHub を追加し、 Push で OK なのですが、100 MB 超えのファイルのコミットがあると GitHub が受け付けてくれず、 Push が失敗してしまいます。

そこで、伝家の宝刀 git filter-branch コマンドを使ってリポジトリの履歴を書き換えてしまいます。

すべてのブランチをチェックアウトする

masterdevelop ブランチのみ、であれば単純なのですが、 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 はローカルで作業して失敗してもリモートには影響がないので、こういった作業も比較的気軽に行えて便利ですね。

20
7
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
20
7