-
1. はじめに
- 1.1 バージョン管理について
- 1.2 Gitの簡単な歴史
- 1.3 Gitとは?
- 1.4 コマンドライン
- 1.5 Gitのインストール
- 1.6 Gitの初期設定
- 1.7 ヘルプの入手
- 1.8 まとめ
-
2. Gitの基本
- 2.1 Gitリポジトリの取得
- 2.2 リポジトリへの変更の記録
- 2.3 コミット履歴の表示
- 2.4 変更の取り消し
- 2.5 リモートリポジトリの操作
- 2.6 タグ付け
- 2.7 Gitエイリアス
- 2.8 まとめ
-
3. Gitブランチ
-
4. サーバー上のGit
- 4.1 プロトコル
- 4.2 サーバーにGitを導入する
- 4.3 SSH公開鍵の生成
- 4.4 サーバーのセットアップ
- 4.5 Gitデーモン
- 4.6 スマートHTTP
- 4.7 GitWeb
- 4.8 GitLab
- 4.9 サードパーティホスティングのオプション
- 4.10 まとめ
-
5. 分散Git
- 5.1 分散ワークフロー
- 5.2 プロジェクトへの貢献
- 5.3 プロジェクトの保守
- 5.4 まとめ
-
6. GitHub
- 6.1 アカウントのセットアップと設定
- 6.2 プロジェクトへの貢献
- 6.3 プロジェクトの保守
- 6.4 組織の管理
- 6.5 GitHubのスクリプト
- 6.6 まとめ
-
7. Gitツール
- 7.1 リビジョンの選択
- 7.2 インタラクティブなステージング
- 7.3 スタッシュとクリーン
- 7.4 作業への署名
- 7.5 検索
- 7.6 履歴の書き換え
- 7.7 Resetの解説
- 7.8 高度なマージ
- 7.9 Rerere
- 7.10 Gitを使ったデバッグ
- 7.11 サブモジュール
- 7.12 バンドル
- 7.13 置換
- 7.14 認証情報の保存
- 7.15 まとめ
-
8. Gitのカスタマイズ
- 8.1 Gitの設定
- 8.2 Git属性
- 8.3 Gitフック
- 8.4 Git強制ポリシーの例
- 8.5 まとめ
-
9. Gitと他のシステム
- 9.1 クライアントとしてのGit
- 9.2 Gitへの移行
- 9.3 まとめ
-
10. Gitの内部構造
-
A1. 付録A: 他の環境でのGit
- A1.1 グラフィカルインターフェース
- A1.2 Visual StudioでのGit
- A1.3 Visual Studio CodeでのGit
- A1.4 IntelliJ / PyCharm / WebStorm / PhpStorm / RubyMineでのGit
- A1.5 Sublime TextでのGit
- A1.6 BashでのGit
- A1.7 ZshでのGit
- A1.8 PowerShellでのGit
- A1.9 まとめ
-
A2. 付録B: アプリケーションへのGitの組み込み
- A2.1 コマンドラインGit
- A2.2 Libgit2
- A2.3 JGit
- A2.4 go-git
- A2.5 Dulwich
-
A3. 付録C: Gitコマンド
- A3.1 セットアップと設定
- A3.2 プロジェクトの取得と作成
- A3.3 基本的なスナップショット
- A3.4 ブランチとマージ
- A3.5 プロジェクトの共有と更新
- A3.6 検査と比較
- A3.7 デバッグ
- A3.8 パッチ
- A3.9 メール
- A3.10 外部システム
- A3.11 管理
- A3.12 プラミングコマンド
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 apply
を使用する方法とgit 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ファイルを読み込むように構築されています。これは、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
きれいに適用され、自動的に新しいコミットが作成されたことがわかります。作者情報はメールのFrom
ヘッダーとDate
ヘッダーから取得され、コミットのメッセージはメールの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方向マージを試みます。パッチがベースにしていると記述されているコミットがリポジトリにない場合、このオプションは機能しないため、デフォルトではオンになっていません。そのコミットがある場合(パッチが公開コミットに基づいている場合)、-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つのパッチのみをコントリビュートする人のために、数百のリモートを持ちたいとは思わないでしょう。ただし、スクリプトとホストサービスによってこれが簡単になる場合があります。それは主に、どのように開発し、コントリビューターがどのように開発するかに依存します。
このアプローチのもう1つの利点は、コミットの履歴も取得できることです。正当なマージの問題がある可能性はありますが、履歴のどこに彼らの作業が基づいているかを知っています。適切な3方向マージがデフォルトであり、-3
を指定してパッチがアクセスできる公開コミットから生成されることを期待する必要はありません。
一貫して誰かと作業していないが、それでもこの方法でプルしたい場合は、git pull
コマンドにリモートリポジトリのURLを指定できます。これは1回限りのプルを実行し、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
に渡すことができることを覚えておいてください。これにより、各コミットに導入されたdiffが付加されます。
このトピックブランチを別のブランチとマージした場合に何が起こるかの完全なdiffを確認するには、正しい結果を得るために奇妙なトリックを使用する必要がある場合があります。これを実行しようとするかもしれません。
$ git diff master
このコマンドはdiffを提供しますが、誤解を招く可能性があります。トピックブランチを作成してからmaster
ブランチが進んだ場合、奇妙な結果になる可能性があります。これは、Gitが現在いるトピックブランチの最後のコミットのスナップショットと、master
ブランチの最後のコミットのスナップショットを直接比較するためです。たとえば、master
ブランチのファイルに1行を追加した場合、スナップショットを直接比較すると、トピックブランチはその行を削除しようとしているように見えます。
master
がトピックブランチの直接の祖先である場合、これは問題ではありません。ただし、2つの履歴が分岐している場合、diffはトピックブランチにすべての新しいものが追加され、master
ブランチに固有のものがすべて削除されるように見えます。
実際に見たいのは、トピックブランチに追加された変更、つまり、このブランチをmaster
とマージした場合に導入される作業です。これを行うには、Gitにトピックブランチの最後のコミットを、master
ブランチとの最初の共通の祖先と比較させます。
技術的には、共通の祖先を明示的に見つけてから、その上でdiffを実行することでそれを行うことができます。
$ git merge-base contrib master
36c7dba2c95e6bbb78dfa822519ecfec6e1ca649
$ git diff 36c7db
または、より簡潔に
$ git diff $(git merge-base contrib master)
ただし、どちらも特に便利ではないため、Gitは同じことを行うための別の省略形を提供しています。3ドット構文です。git diff
コマンドのコンテキストでは、別のブランチの後に3つのピリオドを置くと、現在いるブランチの最後のコミットと、別のブランチとの共通の祖先の間のdiff
を実行できます。
$ git diff master...contrib
このコマンドは、現在のトピックブランチがmaster
との共通の祖先以降に導入した作業のみを示します。これは非常に便利な構文ですので、覚えておいてください。
コントリビューションされた作業の統合
トピックブランチのすべての作業がよりメインラインブランチに統合する準備ができたら、問題はどのように行うかということです。さらに、プロジェクトを維持するためにどのような全体的なワークフローを使用したいですか?多くの選択肢があるため、そのいくつかを取り上げます。
マージワークフロー
1つの基本的なワークフローは、すべての作業をmaster
ブランチに直接マージすることです。このシナリオでは、基本的に安定したコードを含むmaster
ブランチがあります。完了したと思うトピックブランチでの作業や、他の誰かがコントリビュートし、検証した作業がある場合は、それをmasterブランチにマージし、マージしたばかりのトピックブランチを削除して、繰り返します。
たとえば、複数のトピックブランチを持つ履歴のように見えるruby_client
とphp_client
という名前の2つのブランチで作業を行うリポジトリがあり、ruby_client
の後にphp_client
をマージすると、履歴はトピックブランチのマージ後のようになります。


これはおそらく最も簡単なワークフローですが、導入するものについて非常に注意する必要がある大規模またはより安定したプロジェクトを扱っている場合は、問題になる可能性があります。
より重要なプロジェクトがある場合は、2段階のマージサイクルを使用することをお勧めします。このシナリオでは、master
とdevelop
の2つの長期実行ブランチがあり、master
は非常に安定したリリースがカットされた場合にのみ更新され、すべての新しいコードはdevelop
ブランチに統合されると判断します。これらの両方のブランチを定期的にパブリックリポジトリにプッシュします。マージする新しいトピックブランチがあるたび(トピックブランチのマージ前)、それをdevelop
にマージします(トピックブランチのマージ後)。次に、リリースにタグを付けるときに、安定したdevelop
ブランチがある場所にmaster
を早送りします(プロジェクトリリース後)。



このようにすることで、プロジェクトのリポジトリをクローンした人は、master
をチェックアウトして最新の安定版をビルドし、それを簡単に最新の状態に保つことができます。あるいは、より最先端のコンテンツであるdevelop
をチェックアウトすることもできます。さらに、すべての作業がマージされるintegrate
ブランチを作成することで、この概念を拡張することもできます。そして、そのブランチ上のコードベースが安定し、テストに合格したら、それをdevelop
ブランチにマージします。それがしばらく安定していることが証明されたら、master
ブランチを早送りします。
大規模マージワークフロー
Gitプロジェクトには、master
、next
、および新規作業用のseen
(以前は「pu」 - proposed updates)と、メンテナンスバックポート用のmaint
の4つの長期実行ブランチがあります。コントリビューターによって新しい作業が導入されると、それらは、(並行して投稿されたトピックブランチの複雑なシリーズの管理を参照)で説明した方法と同様に、メンテナーのリポジトリのトピックブランチに収集されます。この時点で、トピックは、安全で消費の準備ができているか、またはさらに作業が必要かを判断するために評価されます。安全な場合は、next
にマージされ、そのブランチがプッシュアップされるため、誰もが統合されたトピックを試すことができます。

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

トピックブランチが最終的にmaster
にマージされたら、リポジトリから削除されます。Gitプロジェクトには、メンテナンスリリースが必要な場合にバックポートされたパッチを提供するために、最後のリリースからフォークされたmaint
ブランチもあります。したがって、Gitリポジトリをクローンすると、開発のさまざまな段階でプロジェクトを評価するためにチェックアウトできる4つのブランチがあり、どれほど最先端にしたいか、またはどのように貢献したいかに応じています。また、メンテナーは新しい貢献を審査するのに役立つ構造化されたワークフローを持っています。Gitプロジェクトのワークフローは特殊化されています。これを明確に理解するには、Gitメンテナーガイドを確認できます。
リベースおよびチェリーピックワークフロー
他のメンテナーは、ほとんど線形の履歴を維持するために、コントリビュートされた作業をマージするのではなく、master
ブランチの上にリベースまたはチェリーピックすることを好みます。トピックブランチに作業があり、それを統合したいと判断した場合は、そのブランチに移動し、リベースコマンドを実行して、現在のmaster
(またはdevelop
など)ブランチの上に変更を再構築します。それがうまくいけば、master
ブランチを早送りすることができ、線形のプロジェクト履歴になります。
導入された作業をあるブランチから別のブランチに移動するもう1つの方法は、チェリーピックすることです。Gitのチェリーピックは、単一のコミットのリベースのようなものです。コミットで導入されたパッチを取得し、現在いるブランチに再適用しようとします。これは、トピックブランチに多数のコミットがあり、そのうちの1つだけを統合したい場合、またはトピックブランチに1つのコミットしかなく、リベースを実行するよりもチェリーピックすることを好む場合に役立ちます。たとえば、次のようなプロジェクトがあるとします。

コミットe43a6
をmaster
ブランチにプルしたい場合は、次を実行できます。
$ 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値を取得します。これで、履歴は次のようになります。

これで、トピックブランチを削除し、プルしたくないコミットを削除できます。
Rerere
多くのマージとリベースを行っている場合、または長期にわたるトピックブランチを維持している場合、Gitには「rerere」と呼ばれる便利な機能があります。
Rerereは「reuse recorded resolution(記録された解決策の再利用)」の略で、手動によるコンフリクト解決をショートカットする方法です。rerereが有効になっている場合、Gitは成功したマージの前後のイメージのセットを保持し、以前に修正したのとまったく同じようなコンフリクトがあることに気付いた場合、それを気にせずに前回の修正を単に使用します。
この機能は、構成設定とコマンドの2つの部分で構成されています。構成設定はrerere.enabled
であり、グローバル構成に含めるのに十分便利です。
$ git config --global rerere.enabled true
これで、コンフリクトを解決するマージを実行するたびに、将来必要になった場合に備えて、解決策がキャッシュに記録されます。
必要に応じて、git rerere
コマンドを使用してrerereキャッシュとやり取りできます。単独で呼び出されると、Gitは解決策のデータベースをチェックし、現在のマージコンフリクトとの一致を見つけようとし、それらを解決しようとします(ただし、rerere.enabled
がtrue
に設定されている場合は、これは自動的に行われます)。また、記録される内容を確認したり、キャッシュから特定の解決策を消去したり、キャッシュ全体をクリアしたりするためのサブコマンドもあります。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プロジェクトのメンテナーは、公開キーをリポジトリ内のblobとして含め、そのコンテンツを直接指すタグを追加することで、この問題を解決しました。これを行うには、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データベースに直接インポートできます。これにより、その内容で新しいblobがGitに書き込まれ、blobの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
タグが全員と共有されます。タグを検証したい場合は、データベースから直接blobをプルしてGPGにインポートすることで、PGPキーを直接インポートできます。
$ git show maintainer-pgp-pub | gpg --import
それらのキーを使用して、署名付きのすべてのタグを検証できます。また、タグメッセージに指示を含める場合、git show <tag>
を実行すると、エンドユーザーにタグ検証に関するより具体的な指示を与えることができます。
ビルド番号の生成
Gitには、「v123」のような単調に増加する番号や、各コミットに対応する同等の番号がないため、コミットに対応する人間が読める名前が必要な場合は、そのコミットに対してgit describe
を実行できます。応答として、Gitは、そのコミットより前の最新のタグの名前、そのタグからのコミット数、最後に説明されているコミットの部分的なSHA-1値(「g」という文字がGitを意味するプレフィックス付き)で構成される文字列を生成します。
$ 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
出力名は無効になりました。
リリースの準備
これで、ビルドをリリースしたいと考えています。最初にやりたいことの1つは、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
ディレクトリの下にプロジェクトの最新のスナップショットを取得します。同様の方法でzipアーカイブを作成することもできますが、git archive
に--format=zip
オプションを渡します。
$ git archive master --prefix='project/' --format=zip > `git describe master`.zip
これで、プロジェクトのリリース用の素敵なtarballとzipアーカイブが作成され、Webサイトにアップロードしたり、人にメールで送信したりできます。
Shortlog
プロジェクトで何が起こっているかを知りたい人のメーリングリストにメールを送信する時が来ました。前回のリリースまたはメール以降にプロジェクトに追加されたものの変更履歴をすばやく取得する良い方法は、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
以降のすべてのコミットのクリーンな要約が作成され、作成者別にグループ化されます。