GitHub に100MB超のファイルを置く -- git push に失敗してからの対処方法

GitHub には大きなファイルを直接置けない．具体的にはリポジトリ内のファイルのサイズが1個あたり100MBを超えるとプッシュ (push) が出来なくなる．（その前に50MBで警告が出る．）これではちょっと大きな画像とか音声ファイルを置くときに困ることになる．

そこで，大きなファイルをローカルリポジトリに追加してしまってプッシュ出来なくなった状況からの対処方法をメモしておこう．

# 0. 問題発生

想定するのはこんな状況だ．

まずGitHubに `{REPO}.git` なるリポジトリがあった（あるいは新規に作った）として，それをローカルにクローン (clone) したとしよう．

```sh
$ git clone https://github.com/{USER}/{REPO}.git
Cloning into '{REPO}'...
```

以下，ワーキングディレクトリは `{REPO}` とする．

ここで100MBを超える `{LARGE_FILE}` をディレクトリ `{REPO}` の直下に作ったとしよう．仮に `{LARGE_FILE}` は150MBとする．Gitそのものにはファイルサイズの制限が無いので，ローカルリポジトリへのコミット (commit) は問題なく成功する．

```sh
$ cd {REPO}
$ git add {LARGE_FILE}                  # 成功
$ git commit -m 'I added {LARGE_FILE}.' # 成功
[master #######] I added {LARGE_FILE}.
1 file changed, 0 insertions(+), 0 deletions(-)
create mode 100644 LARGE_FILE
```

問題が起こるのはこのリポジトリを GitHub へプッシュする時で，ここでファイルサイズ超過のエラーが起こる．

```sh
$ git push origin master                # 失敗
Counting objects: 3, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
remote: error: GH001: Large files detected. You may want to try Git Large File Storage - https://git-lfs.github.com.
remote: error: Trace: #######
remote: error: See https://help.github.com/articles/working-with-large-files/ for more information.
remote: error: File {LARGE_FILE} is 150.00 MB; this exceeds GitHubs file size limit of 100.00 MB
To https://github.com/{USER}/{REPO}.git
! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://github.com/{USER}/{REPO}.git'
```

見ての通り100MBを超えるファイルをローカルリポジトリにコミットした状態で GitHub にプッシュしようとすると

> remote: error: GH001: Large files detected. You may want to try Git Large File Storage — https://git-lfs.github.com.

と言われて，終了してしまう．

# 1. Git LFS のインストール

ここで絶望せずに [Git Large File Storage (LFS)](https://git-lfs.github.com) をインストールしよう．Macで [Homebrew](http://brew.sh) を使っていれば `brew install git-lfs` で一発だ．

```sh
$ brew install git-lfs
==> Downloading https://homebrew.bintray.com/bottles/git-lfs-1.5.3.sierra.bottle
==> Pouring git-lfs-1.5.3.sierra.bottle.tar.gz
==> Caveats
Update your git config to finish installation:
  # Update global git config
  $ git lfs install
  # Update system git config
  $ git lfs install --system
==> Summary
🍺  /usr/local/Cellar/git-lfs/1.5.3: 6 files, 9.4M
```

もちろん Homebrew を使っていなくてもソースからインストール出来るのだが，この際なので Homebrew ごとインストールしてしまおう．そのほうがきっと早い．

次にリポジトリを Git LFS に対応させよう．ワーキングディレクトリで次のコマンドを打とう．

```sh
$ cd {REPO}
$ git lfs install
Updated pre-push hook.
Git LFS initialized.
```

# 2. リポジトリの巻き戻し

次に先程のコミットを無かったことにしよう．さもないと，またプッシュが失敗する．そこでリポジトリをリセット (reset) する必要があるのだが，リセットに関しては次の記事が大変参考になる．

> [git reset についてもまとめてみる - murankの日記](http://d.hatena.ne.jp/murank/20110327/1301224770)

結論を言うと

```sh
$ cd {REPO}
$ git reset --soft HEAD^
```

で，元ファイルを残したままコミットだけを取り消す事ができる．

# 3. 再びコミットする

次に Git LFS を使ったコミットをしよう．と言っても，最初の手続き以外は普通のコミットと大差ない．最初の手続きとは次のコマンドのことだ．

```sh
$ cd {REPO}
$ git lfs track {LARGE_FILE}            # {LARGE_FILE} を登録
Tracking {LARGE_FILE}
$ git add .gitattributes                # 登録済みファイルリストをadd
```

この `git lfs track {LARGE_FILE}` と `git add .gitattributes` は一度やっておけば，次からは省略できる．後は普通にコミットしよう．

```sh
$ git add {LARGE_FILE}                  # 通常のadd
$ git commit -m 'I added {LARGE_FILE}.' # 通常のcommit
[master #######] I added {LARGE_FILE}.
2 files changed, 4 insertions(+)
create mode 100644 .gitattributes
create mode 100644 {LARGE_FILE}
```

# 4. GitHub にプッシュしよう

これでもうプッシュに失敗することはない（はず）なので，遠慮なくプッシュしよう．

```sh
$ cd {REPO}
$ git push origin master                # 成功
Git LFS: (1 of 1 files) 150.00 MB / 150.00 MB
Counting objects: 4, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (4/4), done.
Writing objects: 100% (4/4), 505 bytes | 0 bytes/s, done.
Total 4 (delta 0), reused 0 (delta 0)
To https://github.com/{USER}/{REPO}.git
#######..#######  master -> master
```

これでプッシュは成功するはずなのだが，実はもうひとつのハードルがある．GitHub LFS は月1GBを超える帯域使用に別途課金しているため，アップロードするファイルのサイズによってはアカウントがロックされてしまう．

もしプッシュに失敗するようなら GitHub からメールが来ていないか確認しよう．帯域を使い切っているようだとGitHubから次のような警告メールが来ているはずだ．

> GitHub to {ME}
> Git LFS has been disabled on your personal account {ME} because you’ve exceeded your data plan by at least 150%. Please purchase additional data packs to cover your bandwidth and storage usage:
https://github.com/account/billing/data/upgrade

# 5. 帯域をオーバーしたら

GitHub LFS の無料帯域 (1GB/mo.) を使い切っていたら，次の GitHub のページから帯域を購入しよう．

[https://github.com/account/billing/data/upgrade?packs=1](https://github.com/account/billing/data/upgrade?packs=1)

本稿執筆時点では1ヶ月あたり5.00USDで50GB/mo.の帯域が購入できる．ここで帯域を購入したら，また4のステップを繰り返そう．今度はうまくいくはずだ．

帯域購入は自動更新されるので，次回請求日前に次のリンクから Downgrade to free をクリックしておこう．

[https://github.com/settings/billing](https://github.com/settings/billing)

なお GitHub ではリポジトリのサイズが1GBまでに制限されているのだが，[この Git LFS の方は1アカウントにつき1GBまでと制限されている](http://webapps.stackexchange.com/questions/45254/file-size-and-storage-limits-on-github)．帯域を購入するとストレージも一緒に50GBまでアップグレードされるのだが，ダウングレードすると当然ストレージも失う．大きなファイルは可能な限り Google Drive のような大容量低価格ストレージに逃がしておくのもありかもしれない．