新しいプロジェクトインテグレーターは、他のプロジェクト開発者が期待するものから「後ろの方」に見えるプロジェクト履歴で終わることがあります。 このハウツーは、中央リポジトリを維持するための推奨される統合ワークフローを示しています。
ある中央リポジトリに以下の履歴があるとします:
---o---o---A
これはコミット A
で終了します(時間は左から右に流れ、フラフの各ノードはコミットであり、それらの間の線は親子関係を示します)。
次に、あなたはこれをクローンして、あなた独自のコミット作業を行います。これにより、「あなたのリポジトリ」に以下の履歴が作成されます:
---o---o---A---B---C
あなたの同僚が同様に、「彼のリポジトリ」の A
上に構築し、そしてそれから、それを中央リポジトリにプッシュしたと想像してみてください:
---o---o---A---X---Y---Z
ここで あなたが git push
した場合、 C
に至るあなたの履歴には X
,Y
,Z
が欠けているため、失敗してしまいます。 どうにかして、履歴の先端を Z
の子孫にする必要があります。
この問題の解決への提案の一つは、「フェッチしてからマージする」、別名「git pull」です。 フェッチすると、あなたのリポジトリは以下のような履歴になります。
---o---o---A---B---C
\
X---Y---Z
その後あなたがマージを実行すると、「あなたのブランチ」、つまり C
にいる間に、マージ M
を作成し、履歴を以下のようにします:
---o---o---A---B---C---M
\ /
X---Y---Z
M
は Z
の子孫であるため、あなたはプッシュして中央リポジトリを更新できます。 このようなマージ M
は、両方の履歴でコミットを失うことはないので、その意味では間違いではないかもしれませんが、人々が「プロジェクト参加者間で共有される権威ある正規の履歴」、つまり「the trunk」について話したい場合は、 しばしばそれを 「最初の親からのチェーンをたどることによって表示されるコミット」 と見なし、それを表示するために以下コマンドを使用します:
$ git log --first-parent
同僚が Z
をプッシュした後、 あなたが M
をプッシュする前に中央リポジトリを観察した他のすべての人にとって、 the trunk のコミットは以前は o-o-A-X-Y-Z
でした。 しかし、 あなたが C
にいる間に M
を作成したため、 M
の最初の親は C
になります。したがって、 M
をプッシュして中央リポジトリを進めることにより、あなたは X-Y-Z
を、 the trunk 上ではなくサイドブランチにしてしまいました。
あなたはむしろ以下の形の履歴を持ちたいはずです:
---o---o---A---X---Y---Z---M'
\ /
B-----------C
これは、最初の親チェーンで、プロジェクトが最初に X
その次に Y
その次に Z
のコミットを行い、2つのコミット B
と C
で構成される変更をマージして単一のゴールを達成することが明らかです。 この2つのパッチで バグ #12345 の修正に取り組み、親が入れ替わったマージ M'
はログメッセージで「merge fix-bug-12345」と言うかもしれません。 git pull
でマージを作成するが、親を逆順に記録するように指示する方法があるかもしれません。
注意: 上記で「単一の目標を達成する」と言ったことは重要です。 「マージ順序の入れ替え」は、プロジェクトが無関係なものを一度のマージで処理することはあまり気にしないが、最初の親へのチェーンは気にするという特殊なケースにのみ対応します。
「the trunk」の管理については、複数の考え方があります。
-
あるプロジェクトでは、マージせずに完全に線形の履歴を保持する方針です。 明らかに、マージ順序を入れ替えても、彼らの好みとは一致しません。 代わりに、この形状の履歴を生成するには、更新されたアップストリーム上のあなたの履歴をフラット化する必要があります:
---o---o---A---X---Y---Z---B---C
こうするには
git pull --rebase
とか使います。 -
あるプロジェクトでは、履歴内のマージを許容しますが、最初の親の順序についてあまり心配せず、早送りマージを許可する方針です。 彼らにとって、マージ順序を入れ替えても問題はありませんが、マージ順序の入れ替えは不要です。
-
あるプロジェクトでは、「the trunk」の各コミット毎に1つのことを行う方針です。このようなプロジェクトでの
git log --first-parent
の出力は、単一のテーマを完了するサイドブランチのマージ、または単一のテーマを単独で完了する単一のコミットのいずれかを示します。 もし二つのコミットB
とC
(あるいは二つのコミットグループ) が独立した二つの問題を解決していたとしたら、先ほどの例でマージ順を入れ替えて作ったマージM'
はまだプロジェクトの標準に達していないことになります。 これは、2つの無関係な作業B
とC
を同時にマージします。
最後のカテゴリのプロジェクト(Git自体もその1つです)の場合、個々の開発者は以下のような履歴を準備する必要があります:
C0--C1--C2 topic-c
/
---o---o---A master
\
B0--B1--B2 topic-b
つまり、たぶん以下のように、別々のトピックを別々のブランチに保持します:
$ git clone $URL work && cd work
$ git checkout -b topic-b master
$ ... work to create B0, B1 and B2 to complete one theme
$ git checkout -b topic-c master
$ ... same for the theme of topic-c
そしてそれから
$ git checkout master
$ git pull --ff-only
これは、アップストリームから X
と Y
と Z
を掴んできて、あなたのmasterブランチを進めます:
C0--C1--C2 topic-c
/
---o---o---A---X---Y---Z master
\
B0--B1--B2 topic-b
そしてそれから、これら2つのブランチを別々にマージします:
$ git merge topic-b
$ git merge topic-c
これは以下の結果になります
C0--C1---------C2
/ \
---o---o---A---X---Y---Z---M---N
\ /
B0--B1-----B2
そして、中央リポジトリにプッシュバックします。
topic-bとtopic-cをマージしているときに、誰かが再び中央リポジトリの履歴を進めて、 Z
の先に W
を置き、 git push
を失敗させる可能性があります。
このような場合は、巻き戻して M
と N
を破棄し、 master
の先端を再度更新して、2つのマージをやり直します:
$ git reset --hard origin/master
$ git pull --ff-only
$ git merge topic-b
$ git merge topic-c
この手順により、以下のような履歴が作成されます:
C0--C1--------------C2
/ \
---o---o---A---X---Y---Z---W---M'--N'
\ /
B0--B1---------B2