-
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 リセットの解明
- 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 プラミングとポーセリン
- 10.2 Gitオブジェクト
- 10.3 Git参照
- 10.4 パックファイル
- 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 プラミングコマンド
5.2 分散Git - プロジェクトへの貢献
プロジェクトへの貢献
プロジェクトに貢献する方法を説明する上での主な困難は、その方法が多数存在することです。Gitは非常に柔軟であるため、人々はさまざまな方法で協力でき、どのように貢献すべきかを記述するのは困難です — 各プロジェクトは少しずつ異なります。関連する変数には、アクティブな貢献者の数、選択されたワークフロー、コミットアクセス、そして外部からの貢献方法などが含まれます。
最初の変数は、アクティブな貢献者の数です。このプロジェクトに積極的にコードを貢献しているユーザーが何人いて、どれくらいの頻度で貢献しているか?多くの場合、1日に数回のコミットを行う開発者が2〜3人、あるいは多少休止状態のプロジェクトではそれ以下ということもあります。より大きな企業やプロジェクトでは、開発者の数が数千人に上り、毎日数百または数千のコミットが発生することもあります。これは、開発者が増えるにつれて、コードがクリーンに適用されるか、簡単にマージできることを確認する上での問題が増えるため重要です。あなたが作業している間、または変更が承認または適用されるのを待っている間にマージされた作業によって、提出した変更が古くなったり、ひどく壊れたりする可能性があります。コードを常に最新の状態に保ち、コミットを有効に保つにはどうすればよいでしょうか?
次の変数は、プロジェクトで使用されているワークフローです。各開発者がメインコードラインへの書き込みアクセス権を平等に持つ、集中型ですか?プロジェクトには、すべてのパッチをチェックするメンテナまたは統合マネージャーがいますか?すべてのパッチはピアレビューされ、承認されていますか?あなたはそのプロセスに関与していますか?中尉システムが導入されており、まず彼らに作業を提出する必要がありますか?
次の変数は、あなたのコミットアクセスです。プロジェクトへの書き込みアクセス権がある場合とない場合では、プロジェクトに貢献するために必要なワークフローは大きく異なります。書き込みアクセス権がない場合、プロジェクトは貢献された作業をどのように受け入れることを好みますか?そもそもポリシーがありますか?一度にどれくらいの作業を貢献しますか?どれくらいの頻度で貢献しますか?
これらすべての質問は、あなたがプロジェクトに効果的に貢献する方法、そしてどのようなワークフローが推奨されるか、または利用可能であるかに影響を与えます。これらの各側面を、単純なものから複雑なものへと移行する一連のユースケースで説明します。これらの例から、実践で必要な特定のワークフローを構築できるようになるでしょう。
コミットのガイドライン
特定のユースケースを見る前に、コミットメッセージについて簡単に説明します。コミットを作成するための良いガイドラインを持ち、それに従うことで、Gitでの作業や他者との共同作業がはるかに簡単になります。Gitプロジェクトは、パッチを提出するためのコミットを作成するためのいくつかの良いヒントをまとめたドキュメントを提供しています。それはGitのソースコードのDocumentation/SubmittingPatches
ファイルで読むことができます。
まず、あなたの提出物には空白のエラーが含まれていてはなりません。Gitにはこれをチェックする簡単な方法があります。コミットする前にgit diff --check
を実行すると、可能性のある空白のエラーを特定し、それらをリストアップしてくれます。

git diff --check
の出力コミットする前にそのコマンドを実行すれば、他の開発者を悩ませる可能性のある空白の問題をコミットしようとしているかどうかを知ることができます。
次に、各コミットを論理的に独立した変更セットにするように努めてください。可能であれば、変更を消化しやすいものにしてください — 週末を通して5つの異なる問題についてコーディングし、月曜日にそれらすべてを1つの巨大なコミットとして提出しないでください。週末中にコミットしなくても、月曜日にステージングエリアを使用して、作業を少なくとも1つの問題につき1つのコミットに分割し、各コミットに役立つメッセージを付けてください。一部の変更が同じファイルを変更する場合は、git add --patch
を使用してファイルを部分的にステージングしてみてください(詳細はインタラクティブステージングで詳しく説明されています)。ブランチの先端にあるプロジェクトのスナップショットは、すべての変更がどこかの時点で追加されていれば、1つのコミットであっても5つのコミットであっても同じなので、他の開発者があなたの変更をレビューする際に楽になるように努めてください。
このアプローチは、後で必要になった場合に、変更セットの1つを取り出したり元に戻したりするのを容易にもします。履歴の書き換えでは、履歴を書き換えたりファイルをインタラクティブにステージングしたりするための便利なGitトリックが多数説明されています — これらのツールを使用して、作業を他の人に送る前に、クリーンで理解しやすい履歴を作成するのに役立ててください。
最後に、コミットメッセージについて心に留めておいてください。質の高いコミットメッセージを作成する習慣を身につけることは、Gitの使用と共同作業をはるかに容易にします。一般的なルールとして、メッセージは50文字程度の簡潔な変更セットの説明から始まり、空白行を挟んで、より詳細な説明が続くべきです。Gitプロジェクトは、より詳細な説明に、変更の動機と、その実装が以前の振る舞いとどのように異なるかを記述することを要求しています — これは従うべき良いガイドラインです。コミットメッセージは命令形で書いてください:「バグを修正」のように「Fix bug」とし、「Fixed bug」や「Fixes bug」とはしないでください。これは、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も同じことをします — リポジトリをクローンし、変更をコミットします
# 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は自分の作業をサーバーにプッシュします。これは問題なく機能します。
# Jessica's Machine
$ git push origin master
...
To jessica@githost:simplegit.git
1edee6b..fbff5bc master -> master
上記の出力の最後の行は、プッシュ操作からの有用な返信メッセージを示しています。基本フォーマットは<oldref>..<newref> fromref → toref
で、oldref
は古い参照を意味し、newref
は新しい参照を意味し、fromref
はプッシュされるローカル参照の名前、toref
は更新されるリモート参照の名前です。以下の議論でも同様の出力が表示されるため、その意味の基本的な理解はリポジトリの様々な状態を理解するのに役立ちます。詳細については、git-pushのドキュメントを参照してください。
この例を続行すると、その直後、Johnはいくつかの変更を加え、それらをローカルリポジトリにコミットし、同じサーバーにプッシュしようとします。
# 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'
この場合、Johnのプッシュは、Jessicaが以前に彼女の変更をプッシュしたために失敗します。これは、Subversionに慣れている場合に特に理解することが重要です。なぜなら、2人の開発者が同じファイルを編集していないことに気づくからです。Subversionは異なるファイルが編集された場合、サーバー上でそのようなマージを自動的に行いますが、Gitでは、まずローカルでコミットをマージする必要があります。言い換えれば、JohnはまずJessicaのアップストリームの変更をフェッチし、それらを自分のローカルリポジトリにマージしてからでないとプッシュが許可されません。
最初のステップとして、JohnはJessicaの作業をフェッチします(これはJessicaのアップストリームの作業をフェッチするだけで、まだJohnの作業にマージするわけではありません)
$ git fetch origin
...
From john@githost:simplegit
+ 049d078...fbff5bc master -> origin/master
この時点で、Johnのローカルリポジトリは次のようになります

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

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

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

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

Jessicaは自分のトピックブランチが準備できたと考えていますが、プッシュできるようにJohnがフェッチした作業のどの部分を自分の作業にマージする必要があるかを知りたいと思っています。彼女は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
という構文は、後者のブランチ(この場合はorigin/master
)にあり、最初のブランチ(この場合はissue54
)にはないコミットのみを表示するようGitに要求するログフィルターです。この構文については、コミット範囲で詳しく説明します。
上記の出力から、Johnが作成したコミットが1つあり、Jessicaがまだ自分のローカル作業にマージしていないことがわかります。彼女がorigin/master
をマージした場合、それが彼女のローカル作業を変更する唯一のコミットになります。
これで、Jessicaは自分のトピック作業をmaster
ブランチにマージし、Johnの作業(origin/master
)を自分のmaster
ブランチにマージし、そして再びサーバーにプッシュすることができます。
まず(issue54
トピックブランチでのすべての作業をコミットした後)、Jessicaはこれらすべての作業を統合する準備として、自分のmaster
ブランチに切り替えます。
$ git checkout master
Switched to branch 'master'
Your branch is behind 'origin/master' by 2 commits, and can be fast-forwarded.
Jessicaは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(-)
問題は発生しませんでした。ご覧のように、単純なfast-forwardマージでした。Jessicaは、origin/master
ブランチにあるJohnの以前にフェッチされた作業をマージすることで、ローカルマージプロセスを完了します。
$ 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(-)
すべてがクリーンにマージされ、Jessicaの履歴は次のようになります

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

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

管理されたプライベートチーム
次のシナリオでは、大規模なプライベートグループにおける貢献者の役割を見ていきます。ここでは、小規模なグループが機能について共同作業を行い、その後、チームベースの貢献が別の当事者によって統合される環境で作業する方法を学びます。
JohnとJessicaが1つの機能(「featureA」と呼びましょう)に共同で取り組んでおり、一方、Jessicaと3人目の開発者Josieが2つ目の機能(例えば「featureB」)に取り組んでいるとします。この場合、会社は統合マネージャー型のワークフローを使用しており、個々のグループの作業は特定のエンジニアによってのみ統合され、メインリポジトリのmaster
ブランチはそれらのエンジニアによってのみ更新できます。このシナリオでは、すべての作業はチームベースのブランチで行われ、後でインテグレーターによってまとめられます。
この環境で、2人の異なる開発者と並行して共同作業を行いながら、Jessicaが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(-)
この時点で、彼女はJohnと作業を共有する必要があるため、自分のfeatureA
ブランチのコミットをサーバーにプッシュします。Jessicaはmaster
ブランチへのプッシュアクセス権を持っていません — 統合担当者のみが持っています — したがって、Johnと共同作業するために別のブランチにプッシュする必要があります。
$ git push -u origin featureA
...
To jessica@githost:simplegit.git
* [new branch] featureA -> featureA
JessicaはJohnに、featureA
という名前のブランチに作業をプッシュしたので、今すぐ見ることができるとメールで伝えます。Johnからのフィードバックを待つ間、JessicaはJosieとfeatureB
の作業を開始することにしました。まず、彼女はサーバーのmaster
ブランチをベースとして新しい機能ブランチを開始します。
# Jessica's Machine
$ git fetch origin
$ git checkout -b featureB origin/master
Switched to a new branch 'featureB'
さて、Jessicaは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(-)
Jessicaのリポジトリは次のようになります

彼女は作業をプッシュする準備ができていますが、Josieから、いくつかの初期の「featureB」作業を含むブランチがすでにfeatureBee
ブランチとしてサーバーにプッシュされたというメールを受け取ります。Jessicaは、自分の作業をサーバーにプッシュする前に、それらの変更を自分のものとマージする必要があります。Jessicaはまずgit fetch
でJosieの変更をフェッチします。
$ git fetch origin
...
From jessica@githost:simplegit
* [new branch] featureBee -> origin/featureBee
Jessicaがまだチェックアウト済みのfeatureB
ブランチにいると仮定すると、彼女はgit merge
を使ってJosieの作業をそのブランチにマージできるようになります。
$ 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(-)
この時点で、Jessicaはマージされた「featureB」の作業すべてをサーバーにプッシュしたいと考えていますが、単に自分のfeatureB
ブランチをプッシュするのではなく、JosieがすでにアップストリームのfeatureBee
ブランチを開始しているため、Jessicaはそのブランチにプッシュしたいと考えています。彼女は次のようにしてそれを行います。
$ git push -u origin featureB:featureBee
...
To jessica@githost:simplegit.git
fba9af8..cd685d1 featureB -> featureBee
これはrefspecと呼ばれます。Refspecで、Gitのrefspecとその様々な使い方について詳しく説明しています。また、-u
フラグにも注目してください。これは--set-upstream
の短縮形で、後のプッシュとプルを容易にするようにブランチを設定します。
突然、JessicaはJohnからメールを受け取ります。Johnは共同作業しているfeatureA
ブランチにいくつかの変更をプッシュしたと伝え、Jessicaにそれらを確認するよう依頼します。再び、Jessicaはシンプルなgit fetch
を実行して、Johnの最新の作業を含む(もちろん)サーバーからのすべての新しいコンテンツをフェッチします。
$ git fetch origin
...
From jessica@githost:simplegit
3300904..aad881d featureA -> origin/featureA
Jessicaは、新しくフェッチしたfeatureA
ブランチの内容を自分のローカルコピーの同じブランチと比較することで、Johnの新しい作業のログを表示できます。
$ 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
もしJessicaが気に入れば、彼女はJohnの新しい作業を自分のローカル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(-)
最後に、Jessicaはマージされたコンテンツすべてにいくつかの小さな変更を加えたいと思うかもしれません。そこで、彼女は自由にそれらの変更を行い、ローカルの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
Jessicaのコミット履歴は次のようになります

ある時点で、Jessica、Josie、Johnは、サーバー上の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
注
|
|
ブランチでの作業が完了し、メンテナに貢献する準備ができたら、元のプロジェクトページにアクセスし、「Fork」ボタンをクリックして、プロジェクトの書き込み可能な独自のフォークを作成します。次に、このリポジトリ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を受け取り、プルを要求しているすべての変更の要約を生成します。たとえば、JessicaがJohnにプルリクエストを送信したい場合で、彼女がちょうどプッシュしたトピックブランチに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
作業後のコミット履歴ブランチをリベースしたため、-f
をプッシュコマンドに指定して、サーバー上のfeatureA
ブランチを、その子孫ではないコミットで置き換える必要があります。別の方法としては、この新しい作業をサーバー上の異なるブランチ(おそらくfeatureAv2
と呼ばれるもの)にプッシュすることが挙げられます。
もう1つの可能性のあるシナリオを見てみましょう。メンテナがあなたの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プロジェクトの管理方法を見ていきます。あなたは寛大な独裁者や統合マネージャーになる方法を学ぶでしょう。