実践GitHub Pages運用のユースケースとワークフローの詳細

GitHub Pages Workflow

前回、GitHub Pages 活用の概要を書きましたが、実際に運用していくと、mastergh-pages をどう使い分けるか、また両者の同期をどう行うかなど、いくつかの課題が浮かび上がってくると思います。

そこで今回は、実際に GitHub Pages 上で運用されている Dive Into HTML5 をよく知る立場から書かれた記事 「GitHub Pages Workflow and deleting git’s `master` branch – Oli.jp」 の翻訳を中心に、関連するいくつかの記事から、ユースケースと運用のシナリオ、及びそれに応じたワークフローをまとめてみました。

以下は、その参考記事です。

  1. Git post-commit hook to keep master and gh-pages branch in sync by Paul Irish
  2. 2011年10月13日 Easily keep gh-pages in sync with master | Lea Verou
  3. 2011年10月13日 Quick tip: git checkout specific files from another branch
  4. 2011年10月14日 GitHub Pages Workflow and deleting git’s master branch – Oli.jp
  5. 2011年11月29日 Ryan Fitzer » Easy Syncing of GitHub Pages

これらの記事は、書かれた日付が近いのもそうですが、相互に参照したり、コメント欄で議論したりしていますので、それらをトラッキングしながら私なりの注釈も加えていきたいと思います 😀 。

ちなみに GitHub Pages には、ユーザーページとプロジェクトページの2種類があることは前回説明した通りです。前者は基本的に master ブランチのみを管理すれば良いので、本記事では、mastergh-pages の2つのブランチを管理しなければならない後者を主な対象としています。

1.gh-pages作成のワークフロー(おさらい)

GitHub Pages の作成 は少々特殊で、作業ディレクトリクリーンな状態commit するか、stash で隠す) にした上で、次のようにして gh-pages ブランチを作成します。

cd /path/to/fancypants
git symbolic-ref HEAD refs/heads/gh-pages
rm .git/index
git clean -fdx
echo "My GitHub Page" > index.html
git add .
git commit -a -m "First pages commit"
git push origin gh-pages

通常のブランチ作成 (例えば git checkout -b gh-pages) では、作業ディレクトリの内容が全て引き継がれることになりますが、上記により、親のない空の gh-pages が作成されます。独立した2つの異なるブランチの想定する使い方は以下の通りです。

  • master : プロジェクトのコード
  • gh-pages : プロジェクトの PR サイト

このような特別な作り方をする理由は、次の2つでしょう。

  • mastergh-pages を、マージされることのない独立したブランチとして管理・運用したい
  • リモート・リポジトリがクローンされた時でも、gh-pages はクローンされたくない

試しに gh-pages ブランチのあるリモート・リポジトリをローカルにクローンしてみてください。デフォルトで gh-pages はクローンされません。

$ git clone git@github.com:username/my_repo.git
Cloning into 'my_repo'...
...
$ git branch
* master

gh-pages をクローンするには、次のように 追跡ブランチ として明示的に指示しなければなりません。

$ git checkout --track -b gh-pages origin/gh-pages
Branch gh-pages set up to track remote branch gh-pages from origin.
Switched to a new branch 'gh-pages'
$ git branch
* gh-pages
  master

これを応用すれば、クローンされたくないブランチを作ることもできますネ 😀 。

参考情報

2.GitHub Pages運用のユースケースとワークフロー

GitHubber 達が工夫を凝らしたワークフローを考案していますので、それらを紹介します。

2.1プロジェクトのコードとPRサイトを1つのリポジトリとして運用する

master の最新版から一部のファイルをチェックアウトし、PR サイト側 (gh-pages) でデモを動かすという、まさに典型的な運用のシナリオです。

Lea Verou は 参考記事2 で、master で作業している場合を想定した、次のようなワークフローを提案しています。

git add .
git status            # 変更されたコミットすべきファイルを確認します
git commit -m "..."   # master でのコミットを行います
git push              # 変更を GitHub の master ブランチに push します
git checkout gh-pages # gh-pages ブランチに切り替えます
git rebase master     # master ブランチの更新内容を gh-pages に適用します
git push              # gh-pages ブランチの変更を GitHub に push します
git checkout master   # master ブランチに戻ります

1~4行目までは通常の更新作業です。5行目で gh-pages に切り替え、6行目で master と同期させています。

Nicolas Gallagher は、この提案の6行目に問題があることを指摘し、参考記事3 を書いています。git rebase master は、master の歴史を含めて全てを gh-pages にミラーリングすることになります。元々2つのブランチがマージ可能(あるいは一方が他方の派生)であれば rebase が効果的な局面もありますが、一部の共通なファイルを異なるブランチで同期させる方法としては、次のように必要なファイルだけを更新する git checkout master -- ... の方が適切であるというのが彼の主張です。

git add .
git status            # 変更されたコミットすべきファイルを確認します
git commit -m "..."   # master でコミットします
git push              # 変更を GitHub の master ブランチに push します
git checkout gh-pages # gh-pages ブランチに切り替えます
git checkout master -- files... # master のステージング・エリアから現在の作業ディレクトリにファイルをコピーします
git commit -m "..."   # gh-pages でコミットします
git push              # gh-pages ブランチの変更を GitHub に push します
git checkout master   # master ブランチに戻ります

さて、いずれのワークフローでも master で何かコミットしたら、gh-pages への反映も自動的に行いたくなりますネ。こんな時には post-commit フック が便利です。

Paul Irish は 参考記事1 で、Lea のワークフロー を自動化するための shell スクリプトを Git リポジトリ中のファイル .git/hooks/post-commit に記述することを提案しています。

#!/bin/sh
git checkout gh-pages
git rebase master
git checkout master

このスクリプトにより、Lea のワークフロー の4行目以降を git push --all という1行に置き換えることができます。

git add .
git status            # 変更されたコミットすべきファイルを確認します
git commit -m "..."   # master でのコミットを行います
git push --all        # 全てのブランチの変更をリモートに push します

さらに Ryan Fitzer は、これらの議論をふまえた上で rebasemerge に置き換えた運用を 参考記事5 に記しました。おそらく gh-pages をテストサイトと位置づけ、一部ファイル修正もあり得るという運用シナリオを描いているのではないかと思います。

2.2リモートのgh-pagesmasterのサブモジュールとして運用する

Git の submodule は、「ふたつのプロジェクトはそれぞれ別のものとして管理したい。だけど、一方を他方の一部としても使いたい」 時のコマンドです。ここでの想定は、ローカル上の jekyll で生成したブログを gh-pages に送り込むというシナリオです。

例えば jekyll が _site というディレクトリにサイトを構築する設定だとすると、この _site にリモートの gh-pages と同期するブランチを作成すれば、最もシンプルにプロジェクトのブログを運営することができるようになるでしょう。この辺はちょっとマニアックな内容になりますので、詳しくは以下の参考情報をご覧ください 😉 。

参考情報

@nitoyon さんの 指摘、反映しました。nitoyon さん、ありがとうございました!)

2.3gh-pages専用のリポジトリを作って運用する

Nicolas Gallagher は、GitHub ヘルプ に掲載された方法にヒントを得て、ローカルの master を GitHub の gh-pages[remotename] [localbranch]:[remotebranch] の書式で push しています。

$ git push origin master:gh-pages

これは、Lea のワークフロー の5行目以降を置き換えます。ローカルの master ブランチは、リモートの gh-pages ブランチのミラーかサブセットであることが要求されます。もし gh-pages をローカルに持っているなら、それは GitHub 上のバージョンのコピーとなることを意味しています。これにより、ローカルの gh-pages を無視する (あるいは持たなくても良い) ことになります。もしローカルの gh-pages で何かの push に失敗しても (non-fast-foward updates were rejected)、fetch と merge でローカルの master に取り込んだ後、再び push するだけです。

一方ここまで来ると、ローカルのブランチも gh-pages としてしまった方が便利でしょう。次の例は、新しく gh-pages ブランチを作成し、master ブランチを削除します。

$ git checkout -b gh-pages
Switched to a new branch 'gh-pages'
$ git branch -d master
Deleted branch master (was xxxxxxx).
$ git branch
* gh-pages

参考情報

2.4GitHub上のmasterを削除し、gh-pagesだけで運用する

前章最後の続きとも言えますが、もはや GitHub 上にさえ master が要らないというプロジェクトもあり得るでしょう。例えばドキュメント自体がプロジェクトの対象であるような場合です。

GitHub 上の master ブランチを削除するには、次のようにします。

  1. 「Admin → Options → Settings」 で、Default Branch を master 以外に設定する
  2. git push origin :master」 を実行する (空のローカル・ブランチをリモートの master に push して リモートブランチを削除する

Default Branchの設定

3.CNAME(Canonical Name)で独自ドメインをGitHub Pagesに転送する

オリジナルのドメインを持っている人で、DNS レコードの設定ができる環境にある方(そういうサービスを受けられる方)は、そのドメインあるいはサブドメインの別名を GitHub Pages に設定し、GitHub Pages にはそのドメイン名を記述した CNAME というファイルを設置することで、自分のドメイン内の URL で GitHub Pages にアクセスすることができます。

例えば、最近見つけたサイト nothub.org は、リポジトリ tricknotes/nothub.org (2.4章で述べた gh-pages ブランチしかないリポジトリです!) の GitHub Pages http://tricknotes.github.com/nothub.org (現在は 404 Not Found ですが、この理由と対応は後述します) に対する別名として設定されています。試しに http://nothub.org/CNAME にアクセスしてみましょう。nothub.org と書かれているハズです。

今回はプロジェクトページを主に扱いましたが、ユーザーページでも同様のことができます。CNAME の設定については、Glide Note さんの記事 「GitHub PagesでCustom Domainを利用する – Glide Note – グライドノート」 が参考になると思います。元々は www.glidenote.com で運用されていたブログを、blog.glidenote.com の別名として Octopress を導入した GitHub Pages にマッピングされています。

参考情報

4.GitHub Pagesの寿命と更新タイミングについて

無料アカウントだからでしょうか、GitHub Pages はしばらくアクセスしないと 404 Not Found 状態になります。

Page does not exist

gh-pages を更新し、コミットし直せば再びアクセスできるようになります。おそらく何
らかのキャッシュ機構が働いていて、アクセスのない古いキャッシュは削除され、gh-pages が更新されるまでは再作成されないのではないかと思います。

一方 CNAME を設定した場合、上記が発生していても、設定したオリジナル・ドメインにはちゃんとアクセスできることが 参考記事4 で報告されています。

私の場合、一定時間ごとにアクセスする CGI をサーバーの cron に設定し、GitHub Pages の寿命を延ばしています。もちろんこの方法は未保証ですので、あしからず 😎 。

参考情報

5.まとめ

GitHub Pages の典型的な使用方法以外にも、色々な使い方を 「ユースケースと運用のシナリオ」 という形で整理を試みてみました (厳密な意味 でのツッコミはご勘弁願います 😉 )。結果的にオリジナルの記事をアレンジしてしまうことにはなりましたが、柔軟な運用ができるのは、Git の自由度の高さを活かす設計の良さが GitHub にあるからではないかと改めて知ることができたように思います。GitHub Pages を活用したいと思う方の参考になれば幸いですし、「もっとこういうシナリオがあるゾ」 というご意見を頂ければ、激しく喜びます 🙂 。

最後に、GitHub サポートからのアドバイスを…。

何が起きるか分からない時はいつでも、最初にバックアップとしてローカルにクローンを作成し、全てを fetch してから試してみることです。もし何か問題が起きたら、リポジトリを再度 push すれば良いのです。

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です

海外からの投稿は受け付けていません