-
1. Gitを始めるにあたって
- 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のブランチ機能
- 3.1 ブランチの基本
- 3.2 基本的なブランチとマージ
- 3.3 ブランチ管理
- 3.4 ブランチワークフロー
- 3.5 リモートブランチ
- 3.6 リベース
- 3.7 まとめ
-
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の内側
- 10.1 PlumbingとPorcelain
- 10.2 Gitオブジェクト
- 10.3 Gitリファレンス
- 10.4 Packfile
- 10.5 Refspec
- 10.6 転送プロトコル
- 10.7 メンテナンスとデータ復旧
- 10.8 環境変数
- 10.9 まとめ
-
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 Plumbingコマンド
5.2 分散 Git - プロジェクトへの貢献
プロジェクトへの貢献
プロジェクトに貢献する方法を説明する際の主な難しさは、その方法のバリエーションが多数あることです。Gitは非常に柔軟であるため、人々はさまざまな方法で共同作業を行うことができ、どのように貢献すべきかを説明するのは難しいです。どのプロジェクトも少しずつ異なります。関連する変数には、アクティブな貢献者の数、選択されたワークフロー、コミットアクセス、そしておそらく外部の貢献方法があります。
最初の変数はアクティブな貢献者の数です。このプロジェクトに積極的にコードを貢献しているユーザーは何人いて、どれくらいの頻度で貢献していますか?多くの場合、1日に数コミット、またはやや停滞しているプロジェクトであればそれ以下の数の開発者が2、3人いるでしょう。大規模な企業やプロジェクトでは、開発者の数は数千人になることもあり、毎日数百、数千のコミットが発生します。これは、開発者が増えれば増えるほど、コードがきちんと適用されるか、簡単にマージできるかを確認する上で問題が生じるため、重要です。提出した変更は、作業中または変更が承認または適用されるのを待っている間にマージされた作業によって、時代遅れになったり、深刻に破損したりする可能性があります。コードを常に最新の状態に保ち、コミットを有効にするにはどうすればよいでしょうか?
次の変数は、プロジェクトで使用されているワークフローです。各開発者がメインコードラインに等しく書き込みアクセス権を持つ集中型ですか?プロジェクトにすべてのパッチをチェックするメンテナーまたは統合マネージャーがいますか?すべてのパッチはピアレビューされ、承認されていますか?あなたはそのプロセスに関与していますか?副官システムが導入されており、最初に彼らに作業を提出する必要がありますか?
次の変数は、あなたのコミットアクセスです。プロジェクトに貢献するために必要なワークフローは、プロジェクトに書き込みアクセス権がある場合とない場合とでは大きく異なります。書き込みアクセス権がない場合、プロジェクトは貢献された作業をどのように受け入れることを好みますか?ポリシーさえありますか?一度にどれくらいの作業を貢献していますか?どれくらいの頻度で貢献していますか?
これらすべての質問は、プロジェクトに効果的に貢献する方法や、どのようなワークフローが好ましいか、または利用可能であるかに影響を与える可能性があります。これらの各側面を、単純なものから複雑なものへと続く一連のユースケースで説明します。これらの例から、実際に必要な特定のワークフローを構築できるはずです。
コミットガイドライン
具体的なユースケースを見る前に、コミットメッセージについて簡単に説明します。コミットを作成するための良いガイドラインを持ち、それに従うことで、Gitでの作業や他者との共同作業がはるかに容易になります。Gitプロジェクトは、パッチを提出するためのコミットを作成するための多くの良いヒントをまとめたドキュメントを提供しています。これはGitのソースコードのDocumentation/SubmittingPatches
ファイルで読むことができます。
まず、提出するコードには空白エラーが含まれていてはなりません。Gitはこれを簡単にチェックする方法を提供しています。コミットする前にgit diff --check
を実行すると、潜在的な空白エラーを特定してリスト表示してくれます。

git diff --check
の出力コミットする前にこのコマンドを実行すると、他の開発者を悩ませる可能性のある空白の問題をコミットしようとしているかどうかを判断できます。
次に、各コミットを論理的に独立した変更セットにしてみてください。可能であれば、変更を理解しやすいものにしてください。週末に5つの異なる問題についてコーディングし、それらをすべて月曜日に1つの巨大なコミットとして提出しないでください。週末にコミットしなくても、月曜日にステージングエリアを使用して、作業を少なくとも問題ごとに1つのコミットに分割し、コミットごとに有用なメッセージを付けてください。変更の一部が同じファイルを変更する場合は、git add --patch
を使用してファイルを部分的にステージングしてみてください(詳細は対話的なステージングで説明されています)。すべての変更がどこかの時点で追加される限り、ブランチの先端にあるプロジェクトスナップショットは1つのコミットでも5つのコミットでも同じなので、同僚の開発者が変更をレビューする際に作業を容易にするように努めてください。
このアプローチにより、後で変更セットのいずれかを抽出したり、元に戻したりするのが容易になります。履歴の書き換えでは、履歴を書き換えたり、ファイルを対話的にステージングしたりするための多くの有用なGitのトリックが説明されています。これらのツールを使用して、作業を他の誰かに送信する前に、クリーンで理解しやすい履歴を作成してください。
最後に覚えておくべきことはコミットメッセージです。高品質なコミットメッセージを作成する習慣をつけることで、Gitの使用と共同作業がはるかに容易になります。一般的に、メッセージは50文字以内の単一行で簡潔に変更セットを説明し、その後に空行、そしてより詳細な説明を続けるべきです。Gitプロジェクトでは、より詳細な説明に、変更の動機と、その実装が以前の動作とどのように異なるかを含めることを要求しています。これは従うべき良いガイドラインです。コミットメッセージは命令形で書いてください。例えば、「バグを修正」であり、「バグを修正した」や「バグを修正する」ではありません。これは、Tim Popeが元々書いたものを少し改変したテンプレートです。
Capitalized, short (50 chars or less) summary
More detailed explanatory text, if necessary. Wrap it to about 72
characters or so. In some contexts, the first line is treated as the
subject of an email and the rest of the text as the body. The blank
line separating the summary from the body is critical (unless you omit
the body entirely); tools like rebase will confuse you if you run the
two together.
Write your commit message in the imperative: "Fix bug" and not "Fixed bug"
or "Fixes bug." This convention matches up with commit messages generated
by commands like git merge and git revert.
Further paragraphs come after blank lines.
- Bullet points are okay, too
- Typically a hyphen or asterisk is used for the bullet, followed by a
single space, with blank lines in between, but conventions vary here
- Use a hanging indent
すべてのコミットメッセージがこのモデルに従っていれば、あなたと共同作業する開発者にとって、物事ははるかに簡単になるでしょう。Gitプロジェクトには適切にフォーマットされたコミットメッセージがあります。git log --no-merges
を実行して、適切にフォーマットされたプロジェクトのコミット履歴がどのように見えるかを確認してみてください。
注
|
私たちが行うことではなく、私たちが言うことを実行してください。
簡潔にするため、この本の例の多くは、このような適切にフォーマットされたコミットメッセージを持っていません。代わりに、単に 要するに、私たちが行うことではなく、私たちが言うことを実行してください。 |
プライベートな小規模チーム
おそらく遭遇する最も単純な設定は、他の1、2人の開発者と行うプライベートプロジェクトです。この文脈での「プライベート」とは、クローズドソース、つまり外部にアクセスできないことを意味します。あなたと他の開発者は皆、リポジトリへのプッシュアクセスを持っています。
この環境では、Subversionや他の集中型システムを使用しているときに行うのと同様のワークフローに従うことができます。オフラインコミットや、はるかに単純なブランチングとマージなどの利点は依然として得られますが、ワークフローは非常に似ています。主な違いは、マージがコミット時にサーバーではなくクライアント側で行われることです。2人の開発者が共有リポジトリで共同作業を開始する様子を見てみましょう。最初の開発者であるJohnは、リポジトリをクローンし、変更を加え、ローカルでコミットします。これらの例では、プロトコルメッセージを...
に置き換えて、少し短くしています。
# John's Machine
$ git clone john@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim lib/simplegit.rb
$ git commit -am 'Remove invalid default value'
[master 738ee87] Remove invalid default value
1 files changed, 1 insertions(+), 1 deletions(-)
2番目の開発者、ジェシカも同じことをします。リポジトリをクローンし、変更をコミットします。
# Jessica's Machine
$ git clone jessica@githost:simplegit.git
Cloning into 'simplegit'...
...
$ cd simplegit/
$ vim TODO
$ git commit -am 'Add reset task'
[master fbff5bc] Add reset task
1 files changed, 1 insertions(+), 0 deletions(-)
さて、ジェシカは自分の作業をサーバーにプッシュします。これは問題なく機能します。
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
上記の出力の最後の行は、プッシュ操作からの有用な戻りメッセージを示しています。基本形式は
で、oldref
は古い参照を意味し、newref
は新しい参照を意味し、fromref
はプッシュされるローカル参照の名前、toref
は更新されるリモート参照の名前です。以下の議論でも同様の出力が表示されるので、その意味を基本的な理解していると、リポジトリのさまざまな状態を理解するのに役立ちます。詳細については、git-pushのドキュメントを参照してください。
この例を続けると、その直後、ジョンはいくつかの変更を加え、それらをローカルリポジトリにコミットし、同じサーバーにプッシュしようとします。
# John's Machine
$ git push origin master
To john@githost:simplegit.git
! [rejected] master -> master (non-fast forward)
error: failed to push some refs to 'john@githost:simplegit.git'
この場合、ジョンがプッシュする作業は、ジェシカが以前プッシュした作業と競合するため失敗します。Subversion に慣れている人にとっては、2人の開発者が同じファイルを編集したわけではないことに気づくでしょうから、これは特に重要です。Subversion は異なるファイルが編集された場合でも、サーバー上で自動的にそのようなマージを行います。しかし、Git の場合、まずローカルでコミットをマージする必要があります。言い換えれば、ジョンはジェシカの上流の変更をまずフェッチし、それらをローカルリポジトリにマージしてからでなければ、プッシュすることはできません。
まず、ジョンはジェシカの作業をフェッチします(これはジェシカの上流の作業を _フェッチ_ するだけであり、まだジョンの作業にマージはしません)。
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
この時点でのジョンのローカルリポジトリは、次のようになります。

これでジョンは、フェッチしたジェシカの作業を自分のローカル作業にマージできるようになります。
$ git merge origin/master
Merge made by the 'recursive' strategy.
TODO | 1 +
1 files changed, 1 insertions(+), 0 deletions(-)
そのローカルマージがスムーズに行われる限り、ジョンの更新された履歴は次のようになります。

origin/master
をマージした後のジョンのリポジトリこの時点で、ジョンはジェシカの作業が自分の作業に影響を与えないことを確認するために、この新しいコードをテストしたいと思うかもしれません。すべて問題ないようであれば、彼は最終的に新しいマージされた作業をサーバーにプッシュできます。
$ git push origin master
...
To john@githost:simplegit.git
fbff5bc..72bbc59 master -> master
最終的に、ジョンのコミット履歴は次のようになります。

origin
サーバーにプッシュした後のジョンの履歴その間、ジェシカはissue54
という新しいトピックブランチを作成し、そのブランチに3つのコミットを行いました。彼女はまだジョンの変更をフェッチしていないので、彼女のコミット履歴は次のようになります。

突然、ジェシカはジョンがサーバーに新しい作業をプッシュしたことを知り、それを見たいと思ったので、まだ持っていないサーバーからのすべての新しいコンテンツを次のようにフェッチできます。
# Jessica's Machine
$ git fetch origin
...
From jessica@githost:simplegit
fbff5bc..72bbc59 master -> origin/master
それはその間にジョンがプッシュした作業を引き出します。ジェシカの履歴は次のようになります。

ジェシカは自分のトピックブランチが準備できたと考えていますが、プッシュするためにジョンのフェッチした作業のどの部分を自分の作業にマージする必要があるかを知りたいと思っています。彼女はgit log
を実行してそれを確認します。
$ git log --no-merges issue54..origin/master
commit 738ee872852dfaa9d6634e0dea7a324040193016
Author: John Smith <jsmith@example.com>
Date: Fri May 29 16:01:27 2009 -0700
Remove invalid default value
issue54..origin/master
という構文は、Gitに、後者のブランチ(この場合はorigin/master
)にあり、最初のブランチ(この場合はissue54
)にないコミットのみを表示するように指示するログフィルターです。この構文については、コミット範囲で詳しく説明します。
上記の出力から、ジョンが作成したコミットのうち、ジェシカがまだ自分のローカル作業にマージしていないコミットが1つだけあることがわかります。彼女がorigin/master
をマージすると、その1つのコミットが彼女のローカル作業を変更することになります。
これで、ジェシカは自分のトピックの作業をmaster
ブランチにマージし、ジョンの作業(origin/master
)を自分のmaster
ブランチにマージし、そして再びサーバーにプッシュバックできます。
まず(issue54
トピックブランチ上のすべての作業をコミットした後)、ジェシカはすべての作業を統合する準備としてmaster
ブランチに戻ります。
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
ジェシカは、origin/master
とissue54
のどちらを先にマージしても構いません。両方とも上流なので、順序は関係ありません。どちらの順序を選択しても、最終的なスナップショットは同じになるはずです。履歴のみが異なります。彼女は最初にissue54
ブランチをマージすることを選択します。
$ git merge issue54
Updating fbff5bc..4af4298
Fast forward
README | 1 +
lib/simplegit.rb | 6 +++++-
2 files changed, 6 insertions(+), 1 deletions(-)
問題は発生しません。ご覧のとおり、単純な早送りマージでした。ジェシカは、origin/master
ブランチにあるジョンが以前フェッチした作業をマージすることで、ローカルマージプロセスを完了します。
$ git merge origin/master
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
すべてがスムーズにマージされ、ジェシカの履歴は次のようになります。

これでorigin/master
はジェシカのmaster
ブランチから到達可能になったので、彼女は(ジョンがその間にさらに変更をプッシュしていないと仮定して)正常にプッシュできるはずです。
$ git push origin master
...
To jessica@githost:simplegit.git
72bbc59..8059c15 master -> master
各開発者は数回コミットし、お互いの作業を正常にマージしました。

これは最も単純なワークフローの一つです。しばらく作業を行い(通常はトピックブランチで)、準備ができたらその作業をmaster
ブランチにマージします。その作業を共有したい場合は、origin/master
が変更されていればそこからmaster
をフェッチしてマージし、最後にサーバーのmaster
ブランチにプッシュします。一般的なシーケンスは次のようになります。

プライベート管理チーム
次のシナリオでは、大規模なプライベートグループにおける貢献者の役割について見ていきます。小規模なグループが機能で共同作業を行い、その後、それらのチームベースの貢献が別の当事者によって統合される環境で作業する方法を学びます。
JohnとJessicaが「featureA」という機能で共同作業をしているとしましょう。一方、Jessicaと3番目の開発者Josieは「featureB」という2番目の機能で作業しています。この場合、会社は統合マネージャーワークフローの一種を使用しており、個々のグループの作業は特定のエンジニアのみが統合し、メインリポジトリのmaster
ブランチはそれらのエンジニアのみが更新できます。このシナリオでは、すべての作業はチームベースのブランチで行われ、後でインテグレーターによってまとめられます。
ジェシカがこの環境で2つの異なる開発者と並行して共同作業を行いながら、彼女の2つの機能に取り組むワークフローを見てみましょう。彼女がすでにリポジトリをクローンしていると仮定して、彼女はまずfeatureA
に取り組むことにします。彼女はその機能のために新しいブランチを作成し、そこでいくつかの作業を行います。
# Jessica's Machine
$ git checkout -b featureA
Switched to a new branch 'featureA'
$ vim lib/simplegit.rb
$ git commit -am 'Add limit to log function'
[featureA 3300904] Add limit to log function
1 files changed, 1 insertions(+), 1 deletions(-)
この時点で、彼女は自分の作業をジョンと共有する必要があるため、featureA
ブランチのコミットをサーバーにプッシュします。ジェシカはmaster
ブランチへのプッシュアクセス権を持っていません。インテグレーターだけが持っています。そのため、ジョンと共同作業するために別のブランチにプッシュする必要があります。
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA
ジェシカはジョンにメールを送り、featureA
というブランチに作業をプッシュしたことを伝え、今見てもらえると伝えます。ジョンからのフィードバックを待つ間、ジェシカはジョージーとfeatureB
の作業を始めることにします。まず、サーバーのmaster
ブランチをベースにして新しい機能ブランチを開始します。
# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
次に、ジェシカはfeatureB
ブランチでいくつかのコミットを行います。
$ vim lib/simplegit.rb
$ git commit -am 'Make ls-tree function recursive'
[featureB e5b0fdc] Make ls-tree function recursive
1 files changed, 1 insertions(+), 1 deletions(-)
$ vim lib/simplegit.rb
$ git commit -am 'Add ls-files'
[featureB 8512791] Add ls-files
1 files changed, 5 insertions(+), 0 deletions(-)
ジェシカのリポジトリは現在このようになっています。

彼女は自分の作業をプッシュする準備ができていますが、Josieから、一部の初期「featureB」作業を含むブランチがすでにfeatureBee
ブランチとしてサーバーにプッシュされているというメールを受け取ります。Jessicaは自分の作業をサーバーにプッシュする前に、それらの変更を自分の作業とマージする必要があります。Jessicaはまずgit fetch
でJosieの変更をフェッチします。
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee
ジェシカがまだチェックアウトされているfeatureB
ブランチにいると仮定して、彼女はgit merge
でジョージーの作業をそのブランチにマージできます。
$ git merge origin/featureBee
Auto-merging lib/simplegit.rb
Merge made by the 'recursive' strategy.
lib/simplegit.rb | 4 ++++
1 files changed, 4 insertions(+), 0 deletions(-)
この時点で、ジェシカはこれらマージされた「featureB」の作業をすべてサーバーに戻したいと思っていますが、自分のfeatureB
ブランチを単純にプッシュしたくありません。むしろ、ジョージーがすでに上流のfeatureBee
ブランチを開始しているので、ジェシカは**その**ブランチにプッシュしたいと思っており、次のように行います。
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
これは*refspec*と呼ばれます。RefspecでGitのrefspecとその利用方法について詳しく説明しています。また、-u
フラグにも注目してください。これは--set-upstream
の短縮形であり、後で簡単にプッシュおよびプルできるようにブランチを設定します。
突然、ジェシカはジョンからメールを受け取ります。ジョンは、彼らが共同作業しているfeatureA
ブランチにいくつかの変更をプッシュしたと彼女に伝え、ジェシカにそれらを見るように頼みます。再び、ジェシカは簡単なgit fetch
を実行して、ジョンからの最新の作業を含む、サーバーからの*すべて*の新しいコンテンツをフェッチします。
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
ジェシカは、新しくフェッチしたfeatureA
ブランチのコンテンツを、同じブランチのローカルコピーと比較することで、ジョンの新しい作業のログを表示できます。
$ git log featureA..origin/featureA
commit aad881d154acdaeb2b6b18ea0e827ed8a6d671e6
Author: John Smith <jsmith@example.com>
Date: Fri May 29 19:57:33 2009 -0700
Increase log output to 30 from 25
ジェシカは気に入ったものを見つけた場合、ジョンの新しい作業を自分のローカルのfeatureA
ブランチにマージできます。
$ git checkout featureA
Switched to branch 'featureA'
$ git merge origin/featureA
Updating 3300904..aad881d
Fast forward
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
最後に、ジェシカはマージされたすべてのコンテンツにいくつかの軽微な変更を加えたいと思うかもしれません。そこで彼女は自由にそれらの変更を加え、ローカルのfeatureA
ブランチにコミットし、最終結果をサーバーにプッシュします。
$ git commit -am 'Add small tweak to merged content'
[featureA 774b3ed] Add small tweak to merged content
1 files changed, 1 insertions(+), 1 deletions(-)
$ git push
...
To jessica@githost:simplegit.git
3300904..774b3ed featureA -> featureA
ジェシカのコミット履歴は現在このようになっています。

ある時点で、ジェシカ、ジョージー、そしてジョンは、サーバー上のfeatureA
とfeatureBee
ブランチが本線への統合の準備ができたことをインテグレータに通知します。インテグレータがこれらのブランチを本線にマージした後、フェッチによって新しいマージコミットが取得され、履歴は次のようになります。

多くのグループがGitに切り替えるのは、複数のチームが並行して作業し、プロセスの後半で異なる作業ラインをマージできるというこの機能のためです。チーム内の小さなサブグループが、チーム全体を巻き込んだり妨げたりすることなく、リモートブランチを介して共同作業できる能力は、Gitの大きな利点です。ここで見たワークフローのシーケンスは次のようになります。

フォークされた公開プロジェクト
公開プロジェクトへの貢献は少し異なります。プロジェクトのブランチを直接更新する権限がないため、別の方法でメンテナーに作業を渡す必要があります。この最初の例では、簡単なフォークをサポートするGitホスト上でのフォークによる貢献について説明します。多くのホスティングサイト(GitHub、BitBucket、repo.or.czなど)がこれをサポートしており、多くのプロジェクトメンテナーがこの貢献スタイルを期待しています。次のセクションでは、電子メールによるパッチの受け入れを好むプロジェクトについて説明します。
まず、メインリポジトリをクローンし、貢献しようとしているパッチまたはパッチシリーズ用のトピックブランチを作成し、そこで作業を行うのが一般的です。シーケンスは基本的に次のようになります。
$ git clone <url>
$ cd project
$ git checkout -b featureA
... work ...
$ git commit
... work ...
$ git commit
注
|
メンテナーがパッチをレビューしやすくするために、 |
ブランチでの作業が完了し、メンテナーに貢献する準備ができたら、元のプロジェクトページに移動し、「フォーク」ボタンをクリックして、プロジェクトの書き込み可能な独自のフォークを作成します。次に、このリポジトリURLをローカルリポジトリの新しいリモートとして追加する必要があります。この例では、それをmyfork
と呼びましょう。
$ git remote add myfork <url>
次に、このリポジトリに新しい作業をプッシュする必要があります。その作業をmaster
ブランチにマージしてプッシュするよりも、作業しているトピックブランチをフォークされたリポジトリにプッシュする方が簡単です。その理由は、作業が受け入れられなかったり、チェリーピックされたりした場合に、master
ブランチを巻き戻す必要がないためです(Gitのcherry-pick
操作については、リベースとチェリーピックのワークフローで詳しく説明されています)。メンテナーがあなたの作業をmerge
、rebase
、またはcherry-pick
した場合でも、最終的には彼らのリポジトリからプルすることによって、あなたの作業は戻ってきます。
いずれにせよ、作業を次のようにプッシュできます。
$ git push -u myfork featureA
作業をリポジトリのフォークにプッシュしたら、元のプロジェクトのメンテナーに、マージしてもらいたい作業があることを通知する必要があります。これはしばしば*プルリクエスト*と呼ばれ、通常、ウェブサイト(GitHubにはGitHubで説明する独自の「プルリクエスト」メカニズムがあります)を介してそのようなリクエストを生成するか、git request-pull
コマンドを実行してその後の出力をプロジェクトメンテナーに手動でメールで送信できます。
git request-pull
コマンドは、トピックブランチをプルしたいベースブランチと、そこからプルしてもらいたいGitリポジトリURLを受け取り、プルを要求しているすべての変更の概要を生成します。たとえば、ジェシカがジョンにプルリクエストを送信したい場合、彼女がプッシュしたばかりのトピックブランチで2つのコミットを行った場合、彼女はこれを実行できます。
$ git request-pull origin/master myfork
The following changes since commit 1edee6b1d61823a2de3b09c160d7080b8d1b3a40:
Jessica Smith (1):
Create new function
are available in the git repository at:
https://githost/simplegit.git featureA
Jessica Smith (2):
Add limit to log function
Increase log output to 30 from 25
lib/simplegit.rb | 10 +++++++++-
1 files changed, 9 insertions(+), 1 deletions(-)
この出力はメンテナーに送信できます。作業がどこから分岐したか、コミットの概要、そして新しい作業をどこからプルするかを伝えます。
メンテナーではないプロジェクトでは、一般的にmaster
のようなブランチが常にorigin/master
を追跡し、拒否された場合に簡単に破棄できるトピックブランチで作業を行う方が簡単です。作業テーマをトピックブランチに隔離することで、メインリポジトリの先端がその間に移動し、コミットがきれいには適用されなくなった場合に、作業をリベースするのも容易になります。たとえば、プロジェクトに2番目の作業トピックを送信したい場合、プッシュしたばかりのトピックブランチで作業を続けるのではなく、メインリポジトリのmaster
ブランチからやり直します。
$ git checkout -b featureB origin/master
... work ...
$ git commit
$ git push myfork featureB
$ git request-pull origin/master myfork
... email generated request pull to maintainer ...
$ git fetch origin
これで、各トピックはサイロ(パッチキューに似たもの)内に収められ、トピックが互いに干渉したり依存したりすることなく、次のように書き換え、リベース、変更できます。

featureB
作業を含む最初のコミット履歴プロジェクトのメンテナーが他のパッチをいくつか取り込み、あなたの最初のブランチを試しましたが、もはやきれいにマージされないとしましょう。この場合、そのブランチをorigin/master
の上にリベースし、メンテナーのために競合を解決し、それから変更を再提出しようとすることができます。
$ git checkout featureA
$ git rebase origin/master
$ git push -f myfork featureA
これにより、履歴はfeatureA
作業後のコミット履歴のように書き換えられます。

featureA
作業後のコミット履歴ブランチをリベースしたため、サーバー上のfeatureA
ブランチを、その子孫ではないコミットで置き換えるためには、プッシュコマンドに-f
を指定する必要があります。代替案として、この新しい作業をサーバー上の別のブランチ(おそらくfeatureAv2
など)にプッシュすることもできます。
もう一つの可能なシナリオを見てみましょう。メンテナーがあなたの2番目のブランチの作業を見て、コンセプトは気に入ったものの、実装の詳細を変更してほしいと言ってきました。この機会に、作業をプロジェクトの現在のmaster
ブランチに基づいたものに移動します。現在のorigin/master
ブランチをベースにして新しいブランチを開始し、そこにfeatureB
の変更をスカッシュし、競合を解決し、実装の変更を行い、そしてそれを新しいブランチとしてプッシュします。
$ git checkout -b featureBv2 origin/master
$ git merge --squash featureB
... change implementation ...
$ git commit
$ git push myfork featureBv2
--squash
オプションは、マージされたブランチ上のすべての作業を1つの変更セットに圧縮し、実際のマージコミットを行わずに、あたかも実際のマージが行われたかのようなリポジトリの状態を生成します。これは、将来のコミットが1つの親のみを持ち、別のブランチからのすべての変更を導入し、新しいコミットを記録する前により多くの変更を行うことができることを意味します。また、デフォルトのマージプロセスの場合にマージコミットを遅延させるために、--no-commit
オプションも役立ちます。
この時点で、メンテナーに要求された変更を行ったこと、そしてそれらの変更がfeatureBv2
ブランチにあることを通知できます。

featureBv2
作業後のコミット履歴メールによる公開プロジェクトへの貢献
多くのプロジェクトでは、パッチの受け入れに関する確立された手順があります。プロジェクトごとに異なるため、それぞれの特定のルールを確認する必要があります。開発者メーリングリストを通じてパッチを受け入れる古い大規模なプロジェクトがいくつかあるため、その例を今から説明します。
ワークフローは前のユースケースと似ています。作業するパッチシリーズごとにトピックブランチを作成します。違いは、プロジェクトに提出する方法です。プロジェクトをフォークして自分の書き込み可能なバージョンにプッシュする代わりに、各コミットシリーズのメールバージョンを生成し、開発者メーリングリストにメールで送信します。
$ git checkout -b topicA
... work ...
$ git commit
... work ...
$ git commit
これで、メーリングリストに送りたいコミットが2つあります。git format-patch
を使って、mbox形式のファイルを生成し、それをリストにメールで送ることができます。このコマンドは、各コミットをメールメッセージに変換し、コミットメッセージの最初の行を件名に、残りのメッセージとコミットが導入するパッチを本文にします。このコマンドの素晴らしい点は、format-patch
で生成されたメールからのパッチを適用すると、すべてのコミット情報が適切に保存されることです。
$ git format-patch -M origin/master
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
format-patch
コマンドは、作成するパッチファイルの名前を出力します。-M
スイッチは、Gitに名前変更を探すように指示します。ファイルは次のようになります。
$ cat 0001-add-limit-to-log-function.patch
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
---
lib/simplegit.rb | 2 +-
1 files changed, 1 insertions(+), 1 deletions(-)
diff --git a/lib/simplegit.rb b/lib/simplegit.rb
index 76f47bc..f9815f1 100644
--- a/lib/simplegit.rb
+++ b/lib/simplegit.rb
@@ -14,7 +14,7 @@ class SimpleGit
end
def log(treeish = 'master')
- command("git log #{treeish}")
+ command("git log -n 20 #{treeish}")
end
def ls_tree(treeish = 'master')
--
2.1.0
これらのパッチファイルを編集して、コミットメッセージには表示させたくないがメールリストには表示させたい情報を追加することもできます。---
行とパッチの開始(diff --git
行)の間にテキストを追加すると、開発者はそれを読むことができますが、その内容はパッチ適用プロセスでは無視されます。
これをメーリングリストにメールで送るには、ファイルをメールプログラムに貼り付けるか、コマンドラインプログラムで送信できます。テキストを貼り付けると、特に改行やその他の空白を適切に保持しない「賢い」クライアントの場合、書式設定の問題が発生することがよくあります。幸いなことに、GitはIMAP経由で適切にフォーマットされたパッチを送信するのに役立つツールを提供しており、これはあなたにとって簡単かもしれません。Gmail経由でパッチを送信する方法を示します。これは私たちが最もよく知っているメールエージェントです。Gitのソースコードにある前述のDocumentation/SubmittingPatches
ファイルの最後に、多くのメールプログラムの詳細な手順が記載されています。
まず、~/.gitconfig
ファイルにimapセクションを設定する必要があります。git config
コマンドを連続して実行して各値を個別に設定することも、手動で追加することもできますが、最終的に設定ファイルは次のようになるはずです。
[imap]
folder = "[Gmail]/Drafts"
host = imaps://imap.gmail.com
user = user@gmail.com
pass = YX]8g76G_2^sFbd
port = 993
sslverify = false
IMAPサーバーがSSLを使用しない場合、最後の2行は必要ないかもしれません。ホストの値はimaps://
ではなくimap://
になります。設定が完了したら、git imap-send
を使用して、指定されたIMAPサーバーの「下書き」フォルダにパッチシリーズを配置できます。
$ cat *.patch |git imap-send
Resolving imap.gmail.com... ok
Connecting to [74.125.142.109]:993... ok
Logging in...
sending 2 messages
100% (2/2) done
この時点で、下書きフォルダに移動し、宛先フィールドをパッチを送信するメーリングリストに変更し、必要に応じてメンテナーまたはそのセクションの担当者をCCに入れ、送信できるようになります。
SMTPサーバーを介してパッチを送信することもできます。以前と同様に、一連のgit config
コマンドを使用して各値を個別に設定するか、~/.gitconfig
ファイルのsendemailセクションに手動で追加できます。
[sendemail]
smtpencryption = tls
smtpserver = smtp.gmail.com
smtpuser = user@gmail.com
smtpserverport = 587
これが完了したら、git send-email
を使ってパッチを送信できます。
$ git send-email *.patch
0001-add-limit-to-log-function.patch
0002-increase-log-output-to-30-from-25.patch
Who should the emails appear to be from? [Jessica Smith <jessica@example.com>]
Emails will be sent from: Jessica Smith <jessica@example.com>
Who should the emails be sent to? jessica@example.com
Message-ID to be used as In-Reply-To for the first email? y
すると、Gitは送信する各パッチに対して、次のような大量のログ情報を出力します。
(mbox) Adding cc: Jessica Smith <jessica@example.com> from
\line 'From: Jessica Smith <jessica@example.com>'
OK. Log says:
Sendmail: /usr/sbin/sendmail -i jessica@example.com
From: Jessica Smith <jessica@example.com>
To: jessica@example.com
Subject: [PATCH 1/2] Add limit to log function
Date: Sat, 30 May 2009 13:29:15 -0700
Message-Id: <1243715356-61726-1-git-send-email-jessica@example.com>
X-Mailer: git-send-email 1.6.2.rc1.20.g8c5b.dirty
In-Reply-To: <y>
References: <y>
Result: OK
ヒント
|
システムの構成と電子メールに関するヘルプ、その他のヒントやコツ、および電子メールで試用パッチを送信するためのサンドボックスについては、git-send-email.ioを参照してください。 |
まとめ
このセクションでは、複数のワークフローについて取り上げ、クローズドソースプロジェクトでの小規模チームの一員としての作業と、大規模な公開プロジェクトへの貢献の違いについて話しました。コミットする前に空白エラーをチェックすること、そして素晴らしいコミットメッセージを書くことができるようになりました。パッチのフォーマット方法、および開発者メーリングリストへのメール送信方法を学びました。マージの扱いについても、さまざまなワークフローの文脈で説明しました。これで、あらゆるプロジェクトで共同作業を行う準備ができました。
次に、その逆の側面、つまりGitプロジェクトのメンテナンス方法を見ていきます。慈悲深い独裁者や統合マネージャーになる方法を学びます。