-
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ブランチ
- 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の内部構造
-
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は非常に柔軟であるため、人々はさまざまな方法で協力することができ、どのように貢献すべきかを説明することは問題があります。すべてのプロジェクトは少しずつ異なるためです。関連する変数には、アクティブな貢献者数、選択されたワークフロー、コミット権限、場合によっては外部貢献方法などがあります。
最初の変数はアクティブな貢献者数です。つまり、このプロジェクトに積極的にコードを提供しているユーザーの数と頻度です。多くの場合、2、3人の開発者が1日に数回コミットする場合や、やや休眠中のプロジェクトの場合はそれよりも少ない場合があります。大規模な企業やプロジェクトの場合、開発者の数は数千人に上り、毎日数百件または数千件のコミットが送信される可能性があります。これは、開発者が増えるほど、コードがクリーンに適用されることや、簡単にマージできることを確認する上でより多くの問題が発生するため、重要です。送信した変更が、作業中または変更が承認または適用されるのを待っている間にマージされた作業によって、陳腐化したり、ひどく破損したりする可能性があります。コードを常に最新の状態に保ち、コミットを有効にするにはどうすればよいでしょうか?
次の変数は、プロジェクトで使用されているワークフローです。各開発者がメインコードラインへの書き込み権限を等しく持っている集中型ですか?プロジェクトには、すべてのパッチをチェックするメンテナーまたはインテグレーションマネージャーがいますか?すべてのパッチはピアレビューされ、承認されていますか?あなたはそのプロセスに関与していますか?小隊システムが導入されており、最初にあなたの作業を彼らに提出する必要がありますか?
次の変数は、コミット権限です。プロジェクトへの書き込み権限がある場合とない場合では、プロジェクトに貢献するために必要なワークフローは大きく異なります。書き込み権限がない場合、プロジェクトはどのように貢献された作業を受け入れることを好みますか?それにはポリシーさえありますか?一度にどれだけの作業を貢献していますか?どれくらいの頻度で貢献しますか?
これらの質問はすべて、プロジェクトへの効果的な貢献方法や、どのようなワークフローが推奨または利用可能かに影響を与える可能性があります。ここでは、簡単なものからより複雑なものへと段階的に、これらの各側面をユースケースを通して説明します。これらの例から、実際に必要な具体的なワークフローを構築できるようになるはずです。
コミットのガイドライン
具体的なユースケースを見る前に、コミットメッセージに関する簡単な注意点です。コミットを作成するための適切なガイドラインを持ち、それを守ることで、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 プロジェクトでは、詳細な説明に、変更の動機と、以前の動作との実装の対比を含めることを要求しています。これは従うべき良いガイドラインです。コミットメッセージは命令形で記述してください。「バグを修正する」のように書き、「バグを修正した」または「バグを修正します」のように書かないでください。以下は、もともと 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'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
で、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
という構文はログフィルターで、後のブランチ(この場合は origin/master
)に存在し、最初のブランチ(この場合は issue54
)には存在しないコミットのみを表示するように Git に要求します。この構文については、コミット範囲で詳しく説明します。
上記出力から、ジェシカが自分のローカル作業にマージしていないジョンが行った単一のコミットがあることがわかります。origin/master
をマージすると、ローカル作業が変更されるのはその単一のコミットになります。
これで、ジェシカは自分のトピック作業を自分の 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
各開発者は数回コミットし、お互いの作業を正常にマージしました。

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

プライベートな管理されたチーム
次のシナリオでは、大規模なプライベートグループでの貢献者の役割を見ていきます。小グループが機能について共同作業を行い、その後、それらのチームベースの貢献が別の当事者によって統合される環境での作業方法を学びます。
ジョンとジェシカが 1 つの機能(これを「featureA」と呼びます)で共同作業しており、一方、ジェシカと 3 番目の開発者であるジョージーが 2 番目の機能(「featureB」としましょう)で作業しているとします。この場合、会社は統合マネージャーワークフローの一種を使用しており、個々のグループの作業は特定のエンジニアのみが統合し、メインリポジトリの 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(-)
ジェシカのリポジトリは次のようになります。

彼女は作業をプッシュする準備ができていますが、ジョシーから「featureB」の初期作業を含むブランチがすでにfeatureBee
ブランチとしてサーバーにプッシュされているというメールを受け取ります。ジェシカは、自分の作業をサーバーにプッシュする前に、これらの変更を自分の変更とマージする必要があります。ジェシカはまず、git fetch
でジョシーの変更を取得します。
$ 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と呼ばれます。Git refspecとそのさまざまな使い方についての詳細は、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
ジェシカが内容を気に入れば、git merge
でジョンの新しい作業をローカルの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
作業をリポジトリのフォークにプッシュしたら、マージしてもらいたい作業があることを元のプロジェクトのメンテナーに通知する必要があります。これはプルリクエストと呼ばれることが多く、通常はWebサイトを介してそのようなリクエストを生成します。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
と呼ばれる)にプッシュすることです。
もう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行はおそらく必要なく、hostの値は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プロジェクトの保守方法を説明します。あなたは、慈悲深い独裁者または統合マネージャーになる方法を学びます。