章 ▾ 第2版

5.3 分散Git - プロジェクトのメンテナンス

プロジェクトのメンテナンス

プロジェクトに効果的に貢献する方法を知っていることに加えて、プロジェクトをメンテナンスする方法を知っている必要もあるでしょう。これには、format-patchで生成されメールで送られてきたパッチを受け入れて適用することや、自分のプロジェクトにリモートとして追加したリポジトリのリモートブランチの変更を統合することが含まれます。あなたが正統なリポジトリをメンテナンスしているか、パッチの検証や承認を手伝っているかにかかわらず、他の貢献者にとって最も明確で、長期的にあなたが維持できる方法で作業を受け入れる方法を知る必要があります。

トピックブランチでの作業

新しい作業を統合することを検討している場合、通常は「トピックブランチ」で試すのが良いアイデアです。トピックブランチとは、その新しい作業を試すために特別に作成された一時的なブランチです。これにより、個別にパッチを微調整したり、うまくいかない場合は、後で戻る時間ができるまでそれをそのままにしておくことが容易になります。これから試す作業のテーマに基づいて、ruby_clientや同様に分かりやすい名前の簡単なブランチ名を作成すれば、しばらく作業を中断して後で戻ってきた場合でも、簡単に思い出せます。Gitプロジェクトのメンテナーは、これらのブランチにも名前空間を設定する傾向があります。たとえば、sc/ruby_clientのように、scは作業を貢献した人の略です。ご記憶の通り、masterブランチをベースにブランチを作成するには、次のようにします。

$ git branch sc/ruby_client master

あるいは、すぐにそのブランチに切り替えたい場合は、checkout -bオプションを使用できます。

$ git checkout -b sc/ruby_client master

これで、受け取った貢献された作業をこのトピックブランチに追加し、長期的なブランチにマージするかどうかを決定する準備が整いました。

メールからのパッチ適用

プロジェクトに統合する必要があるパッチをメールで受け取った場合、それを評価するために、トピックブランチでそのパッチを適用する必要があります。メールされたパッチを適用する方法は、git applygit amの2つあります。

applyを使ったパッチ適用

パッチがgit diffまたはUnixのdiffコマンドの派生(推奨されません; 次のセクションを参照)で生成されたものを受け取った場合、git applyコマンドで適用できます。パッチを/tmp/patch-ruby-client.patchに保存したと仮定すると、パッチは次のように適用できます。

$ git apply /tmp/patch-ruby-client.patch

これは作業ディレクトリのファイルを変更します。patch -p1コマンドを実行してパッチを適用するのとほぼ同じですが、より厳格で、patchよりも曖昧なマッチングを受け入れません。また、git diff形式で記述されていれば、ファイルの追加、削除、名前変更も処理しますが、patchではできません。最後に、git applyは「すべて適用するか、すべて中止するか」のモデルであり、すべてが適用されるか、何も適用されないかのどちらかですが、patchは部分的にパッチファイルを適用することができ、作業ディレクトリを奇妙な状態にしてしまう可能性があります。git applyは全体的にpatchよりもはるかに保守的です。コミットは自動的に作成されません。実行後、導入された変更を手動でステージングしてコミットする必要があります。

実際に適用する前に、パッチがクリーンに適用されるかどうかを確認するためにgit applyを使用することもできます。パッチを使ってgit apply --checkを実行します。

$ git apply --check 0001-see-if-this-helps-the-gem.patch
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply

出力がない場合、パッチはきれいに適用されるはずです。このコマンドは、チェックが失敗した場合にゼロ以外のステータスで終了するため、スクリプトで利用することもできます。

amを使ったパッチ適用

貢献者がGitユーザーで、format-patchコマンドを使ってパッチを生成してくれた場合、パッチには作成者情報とコミットメッセージが含まれているため、あなたの作業は楽になります。可能であれば、貢献者にはdiffの代わりにformat-patchを使ってパッチを生成するように促しましょう。git applyは、古いパッチなどに対してのみ使用すればよいでしょう。

format-patchで生成されたパッチを適用するには、git am(このコマンドは「メールボックスから一連のパッチを適用する」ために使用されるため、amと名付けられています)を使用します。技術的には、git amはmboxファイルを読み込むように構築されています。mboxファイルとは、1つ以上のメールメッセージを1つのテキストファイルに保存するための、シンプルでプレーンテキスト形式のものです。それは次のように見えます。

From 330090432754092d704da8e76ca5c05c198e71a8 Mon Sep 17 00:00:00 2001
From: Jessica Smith <jessica@example.com>
Date: Sun, 6 Apr 2008 10:17:23 -0700
Subject: [PATCH 1/2] Add limit to log function

Limit log functionality to the first 20

これは、前のセクションで見たgit format-patchコマンドの出力の始まりであり、有効なmboxメール形式も表しています。誰かがgit send-emailを使って適切にパッチをメールで送ってきて、それをmbox形式でダウンロードした場合、そのmboxファイルをgit amに指定すれば、見つかったすべてのパッチを適用し始めます。複数のメールをmbox形式で保存できるメールクライアントを使用している場合、パッチシリーズ全体をファイルに保存し、その後git amを使用してそれらを1つずつ適用できます。

ただし、誰かがgit format-patchで生成されたパッチファイルをチケットシステムなどにアップロードした場合、そのファイルをローカルに保存し、そのディスクに保存されたファイルをgit amに渡して適用することができます。

$ git am 0001-limit-log-function.patch
Applying: Add limit to log function

きれいに適用され、新しいコミットが自動的に作成されたことがわかります。作成者情報はメールのFromDateヘッダーから取得され、コミットメッセージはメールのSubjectと本文(パッチの前)から取得されます。例えば、このパッチが上記のmboxの例から適用された場合、生成されたコミットは次のようになります。

$ git log --pretty=fuller -1
commit 6c5e70b984a60b3cecd395edd5b48a7575bf58e0
Author:     Jessica Smith <jessica@example.com>
AuthorDate: Sun Apr 6 10:17:23 2008 -0700
Commit:     Scott Chacon <schacon@gmail.com>
CommitDate: Thu Apr 9 09:19:06 2009 -0700

   Add limit to log function

   Limit log functionality to the first 20

Commit情報はパッチを適用した人物と適用時刻を示しています。Author情報は、パッチを最初に作成した個人とその作成時刻です。

しかし、パッチがきれいに適用されないこともあります。おそらく、メインブランチがパッチの元になったブランチから大きく分岐しているか、パッチがまだ適用されていない別のパッチに依存しているかのどちらかでしょう。その場合、git amプロセスは失敗し、何をしたいか尋ねてきます。

$ git am 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Patch failed at 0001.
When you have resolved this problem run "git am --resolved".
If you would prefer to skip this patch, instead run "git am --skip".
To restore the original branch and stop patching run "git am --abort".

このコマンドは、競合するマージやリベース操作と非常に似ており、問題のあるファイルに競合マーカーを配置します。この問題の解決方法は非常に似ています。ファイルを編集して競合を解決し、新しいファイルをステージングし、その後git am --resolvedを実行して次のパッチに進みます。

$ (fix the file)
$ git add ticgit.gemspec
$ git am --resolved
Applying: See if this helps the gem

Gitに競合をもう少し賢く解決させたい場合は、-3オプションを渡すことができます。これにより、Gitは3-wayマージを試みます。このオプションはデフォルトではオンになっていません。なぜなら、パッチが基づいているとされているコミットがリポジトリにない場合、機能しないからです。そのコミットを持っている場合、つまりパッチがパブリックなコミットに基づいていた場合、-3オプションは競合するパッチの適用に関して通常はるかに賢明です。

$ git am -3 0001-see-if-this-helps-the-gem.patch
Applying: See if this helps the gem
error: patch failed: ticgit.gemspec:1
error: ticgit.gemspec: patch does not apply
Using index info to reconstruct a base tree...
Falling back to patching base and 3-way merge...
No changes -- Patch already applied.

この場合、-3オプションがなければパッチは競合と見なされていただろう。-3オプションが使われたため、パッチはきれいに適用された。

mboxから多数のパッチを適用している場合、amコマンドをインタラクティブモードで実行することもできます。これは、見つけた各パッチで停止し、適用するかどうかを尋ねます。

$ git am -3 -i mbox
Commit Body is:
--------------------------
See if this helps the gem
--------------------------
Apply? [y]es/[n]o/[e]dit/[v]iew patch/[a]ccept all

これにより、多数のパッチを保存している場合に便利です。そのパッチが何だったか覚えていない場合はまずパッチを表示できますし、すでに適用済みの場合は適用しないという選択もできます。

トピックのすべてのパッチがブランチに適用されコミットされたら、それらを長期実行ブランチに統合するかどうか、どのように統合するかを選択できます。

リモートブランチをチェックアウトする

貢献が、自身のリポジトリを設定し、多数の変更をプッシュした後、そのリポジトリのURLと変更が含まれているリモートブランチ名をあなたに送ってきたGitユーザーからのものであった場合、彼らをリモートとして追加し、ローカルでマージを行うことができます。

たとえば、ジェシカが彼女のリポジトリのruby-clientブランチに素晴らしい新機能があるというメールを送ってきた場合、リモートを追加してそのブランチをローカルでチェックアウトすることでテストできます。

$ git remote add jessica https://github.com/jessica/myproject.git
$ git fetch jessica
$ git checkout -b rubyclient jessica/ruby-client

彼女が後日、別の素晴らしい機能を含む別のブランチで再びメールを送ってきた場合、すでにリモートが設定されているため、直接fetchしてcheckoutすることができます。

これは、一貫して協力している人と作業する場合に最も役立ちます。誰かがたまにしかパッチを1つだけ貢献しない場合、メールで受け入れる方が、全員に自分のサーバーを実行させ、少数のパッチを取得するために継続的にリモートを追加および削除するよりも時間がかからないかもしれません。また、パッチを1つか2つしか貢献しない人ごとに何百ものリモートを持つことはあまり望ましくないでしょう。ただし、スクリプトやホスト型サービスによってこれが容易になる場合もあります。これは主に、あなたの開発方法と貢献者の開発方法に依存します。

このアプローチのもう一つの利点は、コミットの履歴も得られることです。正当なマージ問題が発生する可能性はありますが、彼らの作業があなたの履歴のどこに基づいているかを知っています。適切な3者間マージがデフォルトとなり、-3を指定して、パッチがアクセス可能なパブリックコミットから生成されたことを期待する必要はありません。

一貫して特定の人物と作業しているわけではないが、それでもこの方法で彼らからプルしたい場合は、リモートリポジトリのURLをgit pullコマンドに提供できます。これは一度限りのプルを行い、URLをリモート参照として保存しません。

$ git pull https://github.com/onetimeguy/project
From https://github.com/onetimeguy/project
 * branch            HEAD       -> FETCH_HEAD
Merge made by the 'recursive' strategy.

導入される内容の特定

これで、貢献された作業を含むトピックブランチができました。この時点で、それらをどうするか決めることができます。このセクションでは、いくつかのコマンドを再確認し、これらをどのように使用して、この作業をメインブランチにマージした場合に正確に何が導入されるかを確認できるかを見ていきます。

このブランチに含まれているが、masterブランチには含まれていないすべてのコミットをレビューすることは、しばしば役立ちます。ブランチ名の前に--notオプションを追加することで、masterブランチのコミットを除外できます。これは、以前使用したmaster..contrib形式と同じことを行います。たとえば、貢献者が2つのパッチを送ってきて、あなたがcontribというブランチを作成し、そこにそれらのパッチを適用した場合、次のように実行できます。

$ git log contrib --not master
commit 5b6235bd297351589efc4d73316f0a68d484f118
Author: Scott Chacon <schacon@gmail.com>
Date:   Fri Oct 24 09:53:59 2008 -0700

    See if this helps the gem

commit 7482e0d16d04bea79d0dba8988cc78df655f16a0
Author: Scott Chacon <schacon@gmail.com>
Date:   Mon Oct 22 19:38:36 2008 -0700

    Update gemspec to hopefully work better

各コミットがどのような変更を導入するかを確認するには、-pオプションをgit logに渡すと、各コミットに導入された差分が追加されることを思い出してください。

このトピックブランチを別のブランチとマージした場合に何が起こるか、その完全な差分を見るには、正しい結果を得るために奇妙なトリックを使用する必要があるかもしれません。次のように実行しようと考えるかもしれません。

$ git diff master

このコマンドは差分を表示しますが、誤解を招く可能性があります。トピックブランチを作成してからmasterブランチが進んだ場合、一見奇妙な結果が得られます。これは、Gitが、現在いるトピックブランチの最後のコミットのスナップショットと、masterブランチの最後のコミットのスナップショットを直接比較するためです。たとえば、masterブランチのファイルに行を追加した場合、スナップショットの直接比較では、トピックブランチがその行を削除するように見えます。

masterがトピックブランチの直接の祖先である場合、これは問題になりません。しかし、2つの履歴が分岐している場合、差分はトピックブランチの新しいものをすべて追加し、masterブランチに固有のものをすべて削除するように見えます。

本当に見たいのは、トピックブランチに追加された変更、つまりこのブランチをmasterとマージした場合に導入される作業です。それを行うには、Gitにトピックブランチの最後のコミットと、masterブランチとの最初の共通祖先を比較させます。

技術的には、共通祖先を明示的に特定し、それに対して差分を実行することでこれを行うことができます。

$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db

または、より簡潔に

$ git diff $(git merge-base contrib master)

ただし、どちらも特に便利ではないため、Gitは同じことを行う別の省略記法としてトリプルドット構文を提供しています。git diffコマンドのコンテキストでは、別のブランチの後に3つのピリオドを置くと、現在のブランチの最後のコミットとそのブランチの共通祖先との間のdiffを実行できます。

$ git diff master...contrib

このコマンドは、現在のトピックブランチがmasterとの共通祖先以降に導入した作業のみを表示します。これは覚えておくと非常に便利な構文です。

貢献された作業の統合

トピックブランチのすべての作業がよりメインのブランチに統合される準備ができたとき、問題となるのはその方法です。さらに、プロジェクトを維持するためにどのような全体的なワークフローを使用したいですか?いくつかの選択肢があるので、そのうちのいくつかを説明します。

マージワークフロー

基本的なワークフローの一つは、すべての作業を単純にmasterブランチに直接マージすることです。このシナリオでは、基本的に安定したコードを含むmasterブランチがあります。完了したと思われるトピックブランチの作業、または他の誰かが貢献し、あなたが検証した作業がある場合、それをマスターブランチにマージし、マージしたばかりのトピックブランチを削除し、繰り返します。

たとえば、複数のトピックブランチを持つ履歴のように、ruby_clientphp_clientという2つのブランチに作業があるリポジトリがあり、ruby_client、次にphp_clientをマージすると、履歴はトピックブランチのマージ後のようになります。

History with several topic branches
図72. 複数のトピックブランチを持つ履歴
After a topic branch merge
図73. トピックブランチのマージ後

それはおそらく最も単純なワークフローですが、より大規模または安定したプロジェクトを扱っていて、何を導入するかについて非常に注意したい場合には、問題になる可能性があります。

より重要なプロジェクトがある場合は、2段階のマージサイクルを使用したいかもしれません。このシナリオでは、masterdevelopという2つの長期実行ブランチがあり、masterは非常に安定したリリースがカットされた場合にのみ更新され、すべての新しいコードはdevelopブランチに統合されると判断します。あなたはこれらの両方のブランチを定期的に公開リポジトリにプッシュします。新しいトピックブランチをマージするたびに(トピックブランチのマージ前)、それをdevelopにマージし(トピックブランチのマージ後)、その後、リリースにタグを付ける際に、安定したdevelopブランチがある場所までmasterをファストフォワードします(プロジェクトリリース後)。

Before a topic branch merge
図74. トピックブランチのマージ前
After a topic branch merge
図75. トピックブランチのマージ後
After a project release
図76. プロジェクトリリース後

これにより、人々があなたのプロジェクトのリポジトリをクローンする際、masterをチェックアウトして最新の安定版をビルドし、それを簡単に最新の状態に保つこともできますし、developをチェックアウトしてより最先端のコンテンツを利用することもできます。また、すべての作業を統合するintegrateブランチを持つことで、この概念を拡張することもできます。そして、そのブランチのコードベースが安定し、テストに合格したら、それをdevelopブランチにマージし、それがしばらく安定していることが証明されたら、masterブランチをファストフォワードします。

大規模なマージワークフロー

Gitプロジェクトには、新しい作業のためのmasternextseen(以前は「pu」 — 提案された更新)、そしてメンテナンスのバックポートのためのmaintという4つの長期実行ブランチがあります。貢献者によって新しい作業が導入されると、メンテナーのリポジトリで、これまで説明したのと同様の方法でトピックブランチに集められます(並行して貢献された複雑な一連のトピックブランチの管理を参照)。この時点で、トピックは安全で消費可能かどうか、あるいはさらなる作業が必要かどうかを判断するために評価されます。安全であれば、nextにマージされ、そのブランチはプッシュアップされて、誰もが統合されたトピックを試せるようになります。

Managing a complex series of parallel contributed topic branches
図77. 並行して貢献された複雑な一連のトピックブランチの管理

まだ作業が必要なトピックは、代わりにseenにマージされます。完全に安定したと判断されると、トピックはmasterに再マージされます。その後、nextseenブランチはmasterから再構築されます。これは、masterがほぼ常に前進し、nextは時々リベースされ、seenはさらに頻繁にリベースされることを意味します。

Merging contributed topic branches into long-term integration branches
図78. 貢献されたトピックブランチを長期統合ブランチにマージする

トピックブランチが最終的にmasterにマージされると、リポジトリから削除されます。Gitプロジェクトには、メンテナンスリリースが必要な場合に備えて、最後のリリースからフォークされたバックポートパッチを提供するmaintブランチもあります。したがって、Gitリポジトリをクローンすると、開発の異なる段階でプロジェクトを評価するためにチェックアウトできる4つのブランチがあります。これは、あなたがどれだけ最先端を追求したいか、またはどのように貢献したいかによって異なります。また、メンテナーは新しい貢献を精査するための構造化されたワークフローを持っています。Gitプロジェクトのワークフローは特殊です。これを明確に理解するには、Gitメンテナーズガイドを確認するとよいでしょう。

リベースとチェリーピックのワークフロー

他のメンテナーは、ほとんど線形な履歴を保つために、貢献された作業をmasterブランチにマージするのではなく、その上にリベースまたはチェリーピックすることを好みます。トピックブランチに作業があり、それを統合したいと判断したら、そのブランチに移動し、リベースコマンドを実行して、現在のmaster(またはdevelopなど)ブランチの上に変更を再構築します。それがうまく機能すれば、masterブランチをファストフォワードでき、線形なプロジェクト履歴が得られます。

導入された作業をあるブランチから別のブランチへ移動させるもう一つの方法は、チェリーピックです。Gitでのチェリーピックは、単一のコミットに対するリベースのようなものです。コミットで導入されたパッチを取り出し、現在いるブランチに再適用しようとします。これは、トピックブランチに複数のコミットがあり、そのうちの1つだけを統合したい場合、またはトピックブランチに1つのコミットしかなく、リベースを実行するよりもチェリーピックしたい場合に役立ちます。例えば、次のようなプロジェクトがあるとします。

Example history before a cherry-pick
図79. チェリーピック前の履歴例

コミットe43a6masterブランチにプルしたい場合、次のように実行できます。

$ git cherry-pick e43a6
Finished one cherry-pick.
[master]: created a0a41a9: "More friendly message when locking the index fails."
 3 files changed, 17 insertions(+), 3 deletions(-)

これはe43a6で導入されたものと同じ変更をプルしますが、適用日が異なるため、新しいコミットSHA-1値を取得します。これで履歴は次のようになります。

History after cherry-picking a commit on a topic branch
図80. トピックブランチでのコミットをチェリーピックした後の履歴

これで、トピックブランチを削除し、取り込みたくなかったコミットを破棄できます。

Rerere

たくさんのマージやリベースを行う場合、あるいは長期間存続するトピックブランチを管理している場合、Gitには「rerere」と呼ばれる便利な機能があります。

Rerereは「reuse recorded resolution」(記録された解決の再利用)の略で、手動での競合解決をショートカットする手段です。rerereが有効になっている場合、Gitは成功したマージからの前後のイメージのセットを保持し、以前に解決したものと全く同じ競合に気づくと、前回からの解決策をそのまま使用し、あなたに煩わせることはありません。

この機能は、設定とコマンドの2つの部分から構成されます。設定はrerere.enabledで、グローバル設定に入れておくと非常に便利です。

$ git config --global rerere.enabled true

これで、競合を解決するマージを行うたびに、将来必要になった場合に備えて、解決策がキャッシュに記録されます。

必要であれば、git rerereコマンドを使用してrerereキャッシュとやり取りできます。単独で呼び出された場合、Gitはその解決策のデータベースをチェックし、現在のマージ競合と一致するものを見つけて解決しようとします(ただし、rerere.enabledtrueに設定されている場合、これは自動的に行われます)。また、何を記録するかを確認したり、特定の解決策をキャッシュから消去したり、キャッシュ全体をクリアしたりするためのサブコマンドもあります。rerereについては、Rerereでさらに詳しく説明します。

リリースのタグ付け

リリースをカットすることを決定したら、将来どの時点でもそのリリースを再作成できるように、タグを割り当てたいと思うでしょう。Gitの基本で説明したように、新しいタグを作成できます。あなたがメンテナーとしてタグに署名することにした場合、タグ付けは次のようになるでしょう。

$ git tag -s v1.5 -m 'my signed 1.5 tag'
You need a passphrase to unlock the secret key for
user: "Scott Chacon <schacon@gmail.com>"
1024-bit DSA key, ID F721C45A, created 2009-02-09

タグに署名する場合、タグの署名に使用した公開PGPキーを配布する問題が発生する可能性があります。Gitプロジェクトのメンテナーは、公開キーをリポジトリにブロブとして含め、そのコンテンツを直接指すタグを追加することでこの問題を解決しました。これを行うには、gpg --list-keysを実行して、どのキーが必要かを確認します。

$ gpg --list-keys
/Users/schacon/.gnupg/pubring.gpg
---------------------------------
pub   1024D/F721C45A 2009-02-09 [expires: 2010-02-09]
uid                  Scott Chacon <schacon@gmail.com>
sub   2048g/45D02282 2009-02-09 [expires: 2010-02-09]

次に、キーをエクスポートし、それをgit hash-objectにパイプすることで、Gitデータベースに直接インポートできます。これにより、そのコンテンツを持つ新しいブロブがGitに書き込まれ、そのブロブのSHA-1値が返されます。

$ gpg -a --export F721C45A | git hash-object -w --stdin
659ef797d181633c87ec71ac3f9ba29fe5775b92

これでキーのコンテンツがGitに入ったので、hash-objectコマンドが提供した新しいSHA-1値を指定して、それを直接指すタグを作成できます。

$ git tag -a maintainer-pgp-pub 659ef797d181633c87ec71ac3f9ba29fe5775b92

git push --tagsを実行すると、maintainer-pgp-pubタグは全員と共有されます。誰かがタグを検証したい場合、データベースから直接ブロブをプルし、それをGPGにインポートすることで、あなたのPGPキーを直接インポートできます。

$ git show maintainer-pgp-pub | gpg --import

彼らはその鍵を使ってあなたの署名されたすべてのタグを検証できます。また、タグメッセージに指示を含めておけば、git show <tag>を実行することで、エンドユーザーにタグ検証に関するより具体的な指示を与えることができます。

ビルド番号の生成

Gitには、各コミットに付随する「v123」のような単調に増加する番号やそれと同等のものがないため、コミットに人間が読める名前を付けたい場合は、そのコミットに対してgit describeを実行できます。その応答として、Gitは、そのコミットよりも前の最新のタグの名前、そのタグ以降のコミット数、そして最後に記述されるコミットの部分的なSHA-1値(Gitを意味する「g」の文字がプレフィックスとして付く)からなる文字列を生成します。

$ git describe master
v1.6.2-rc1-20-g8c5b85c

このようにして、スナップショットをエクスポートしたりビルドしたりして、人々が理解できる名前を付けることができます。実際、GitリポジトリからクローンされたソースコードからGitをビルドすると、git --versionはこのようなものを表示します。もし直接タグ付けしたコミットを記述している場合は、単にタグ名を表示します。

デフォルトでは、git describeコマンドは注釈付きタグ(-aまたは-sフラグで作成されたタグ)を必要とします。軽量(非注釈付き)タグも利用したい場合は、コマンドに--tagsオプションを追加します。この文字列はgit checkoutまたはgit showコマンドのターゲットとしても使用できますが、末尾の短縮されたSHA-1値に依存しているため、永久に有効であるとは限りません。例えば、Linuxカーネルは最近、SHA-1オブジェクトの一意性を確保するために、8文字から10文字にジャンプしたため、古いgit describeの出力名は無効になりました。

リリースの準備

さあ、ビルドをリリースしましょう。Gitを使わないかわいそうな人たちのために、コードの最新のスナップショットをアーカイブにしたいと思うでしょう。これを行うコマンドはgit archiveです。

$ git archive master --prefix='project/' | gzip > `git describe master`.tar.gz
$ ls *.tar.gz
v1.6.2-rc1-20-g8c5b85c.tar.gz

誰かがそのtarballを開くと、projectディレクトリの下にプロジェクトの最新のスナップショットが得られます。同様に、--format=zipオプションをgit archiveに渡すことで、zipアーカイブを作成することもできます。

$ git archive master --prefix='project/' --format=zip > `git describe master`.zip

これで、Webサイトにアップロードしたり、人々にメールで送ったりできる、プロジェクトリリースの素晴らしいtarballとzipアーカイブが手に入りました。

ショートログ

プロジェクトで何が起こっているか知りたい人々のメーリングリストにメールを送る時が来ました。前回のリリースやメール以降にプロジェクトに追加されたものの変更履歴を素早く得る良い方法は、git shortlogコマンドを使用することです。これは、指定した範囲内のすべてのコミットを要約します。たとえば、前回のリリースがv1.0.1だった場合、以下のコマンドは前回のリリース以降のすべてのコミットの要約を提供します。

$ git shortlog --no-merges master --not v1.0.1
Chris Wanstrath (6):
      Add support for annotated tags to Grit::Tag
      Add packed-refs annotated tag support.
      Add Grit::Commit#to_patch
      Update version and History.txt
      Remove stray `puts`
      Make ls_tree ignore nils

Tom Preston-Werner (4):
      fix dates in history
      dynamic version method
      Version bump to 1.0.2
      Regenerated gemspec for version 1.0.2

v1.0.1以降のすべてのコミットが作成者ごとにグループ化された簡潔な要約が得られ、それをリストにメールで送信できます。

scroll-to-top