チャプター ▾ 第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形式でダウンロードした場合、git amをそのmboxファイルに向けることができ、認識したすべてのパッチを適用し始めます。複数のメールを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を指定して、パッチがアクセスできるパブリックコミットから生成されたことを期待する必要はありません。

常に同じ人と作業しているわけではないけれど、この方法で彼らからプルしたい場合は、git pull コマンドにリモートリポジトリの URL を指定できます。これは一時的なプルを行い、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ブランチには含まれていないすべてのコミットを確認すると役立つことが多いです。masterブランチのコミットを除外するには、ブランチ名の前に--notオプションを追加します。これは、以前使用した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ブランチのファイルに1行追加した場合、スナップショットの直接比較では、トピックブランチがその行を削除するように見えてしまいます。

もし 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との共通祖先から導入した作業のみを表示します。これは覚えておくべき非常に便利な構文です。

貢献された作業の統合

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

マージワークフロー

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

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

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

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

より重要なプロジェクトがある場合、二段階のマージサイクルを使用したいかもしれません。このシナリオでは、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ディレクトリの下にプロジェクトの最新スナップショットが取得されます。git archive--format=zipオプションを渡すことで、同様の方法でzipアーカイブを作成することもできます。

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

これで、ウェブサイトにアップロードしたり、人々にメールで送ったりできる、プロジェクトリリースの素晴らしい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