デフォルトでgitが早送りマージを実行するのはなぜですか?


641

水銀から来て、私は機能を整理するために枝を使います。もちろん、このワークフローも私の歴史の中で見たいです。

私はgitを使用して新しいプロジェクトを開始し、最初の機能を完成させました。機能をマージするとき、gitが早送りを使用していることに気付きました。つまり、可能であれば変更をマスターブランチに直接適用し、ブランチを忘れます。

ですから、将来を考えると、このプロジェクトに取り組んでいるのは私だけです。gitのデフォルトのアプローチ(早送りマージ)を使用すると、私の履歴の結果、1つの巨大なマスターブランチができます。私がすべての機能に個別のブランチを使用したことは誰にもわかりません。結局、その巨大なマスターブランチしか持たないからです。それは専門家ではないように見えませんか?

この推論では、私は早送りマージを望んでおらず、それがデフォルトである理由を理解できません。何がそんなにいいの?


38
注:sandofsky.com/blog/git-workflow.htmlも参照してください。また、「no-ff」を「チェックポイントコミット」で使用しないでください。
VonC、2011

15
一人のプロジェクトでgitを使用して後悔していますか?
HaveAGuess

27
絶対違う!私の作業フォルダーには、gitを使用する7つのワンマンプロジェクトがあります。言い換えると、私はこの質問をして以来、多くのプロジェクトを開始し、それらはすべてgitによってバージョン管理されています。私の知る限りでは、gitとmercurialのみがローカルバージョン管理をサポートしています。これは、慣れてきて以来私にとって不可欠です。設定は簡単で、いつでもすべての歴史を持っています。グループプロジェクトでは、進行中のコードで誰にも干渉することなくコミットできるため、さらに優れています。さらに、githubを使用して、いくつかのプロジェクト(例:micro-optparse)を共有しています。ここで、gitは必須です。
Florian Pilz

7
@Cawas true、-no-ffめったに良いアイデアではありませんが、メインブランチで1つのコミットのみを記録しながら、機能内部の履歴を維持するのに役立ちます。メインブランチでその進行を時々マージする場合、これは長い機能の履歴にとって意味があります。
VonC、2012年

12
ちなみに、「その「線形分岐履歴」は専門外に見えませんか?」というご質問にお答えします。デフォルトのソースコードシステムを使用することに不慣れなことはありません。これはプロ意識についてではありません。これは、あなたをサブスクライブするどの哲学があなたをサブスクライブするかを決定することについてです。たとえば、@ VonCはサンドフスキーの記事にリンクしており、彼は優れたアプローチとして早送りを使用しています。正しいか間違っているかではなく、異なるコンテキストに対する異なる哲学。
Marplesoft 2013年

回答:


718

早送りマージは、存続期間の短いブランチでは理にかなっていますが、より複雑な履歴では、早送りでないマージにより、履歴が理解しやすくなり、コミットのグループを元に戻すことが容易になります。

警告:非早送りには潜在的な副作用もあります。https://sandofsky.com/blog/git-workflow.htmlを確認し、「チェックポイントコミット」による「no-ff」を回避して二分または非難を解除し、それをのデフォルトのアプローチにするかどうかを慎重に検討してくださいmaster

代替テキスト
nvie.comVincent Driessen、「成功したGit分岐モデル」の投稿から)

開発時に完成した機能を組み込む

完成した機能は、次のリリースに追加するために、開発ブランチにマージされる場合があります。

$ git checkout develop
Switched to branch 'develop'
$ git merge --no-ff myfeature
Updating ea1b82a..05e9557
(Summary of changes)
$ git branch -d myfeature
Deleted branch myfeature (was 05e9557).
$ git push origin develop

この--no-ffフラグにより​​、マージが早送りで実行されたとしても、マージは常に新しいコミットオブジェクトを作成します。これにより、機能ブランチの履歴の存在に関する情報が失われるのを防ぎ、機能を一緒に追加したすべてのコミットをグループ化します。

JakubNarębski設定について言及していますmerge.ff

デフォルトでは、Gitは現在のコミットの子孫であるコミットをマージするときに、追加のマージコミットを作成しません。代わりに、現在のブランチの先端が早送りされます。この変数
falseに設定すると、このような場合に追加のマージコミットを作成するようにGitに指示します(--no-ffコマンドラインからオプションを指定するのと同じです)。
' only'に設定すると、そのような早送りマージのみが許可されます(--ff-onlyコマンドラインからオプションを指定することと同等)。


次の理由により、早送りがデフォルトです。

  • 存続期間の短いブランチはGitで非常に簡単に作成して使用できます
  • 短命のブランチは多くの場合、そのブランチ内で自由に再編成できる多くのコミットを分離します
  • これらのコミットは実際にはメインブランチの一部です。再編成されると、メインブランチはそれらを含めるために早送りされます。

ただし、1つのトピック/機能ブランチでの反復ワークフローが予想される場合(つまり、マージしてから、この機能ブランチに戻っていくつかのコミットを追加します)、メインブランチではなく、メインブランチにマージのみを含めると便利です。機能ブランチのすべての中間コミット。

この場合、この種の構成ファイルを設定することになります

[branch "master"]
# This is the list of cmdline options that should be added to git-merge 
# when I merge commits into the master branch.

# The option --no-commit instructs git not to commit the merge
# by default. This allows me to do some final adjustment to the commit log
# message before it gets commited. I often use this to add extra info to
# the merge message or rewrite my local branch names in the commit message
# to branch names that are more understandable to the casual reader of the git log.

# Option --no-ff instructs git to always record a merge commit, even if
# the branch being merged into can be fast-forwarded. This is often the
# case when you create a short-lived topic branch which tracks master, do
# some changes on the topic branch and then merge the changes into the
# master which remained unchanged while you were doing your work on the
# topic branch. In this case the master branch can be fast-forwarded (that
# is the tip of the master branch can be updated to point to the tip of
# the topic branch) and this is what git does by default. With --no-ff
# option set, git creates a real merge commit which records the fact that
# another branch was merged. I find this easier to understand and read in
# the log.

mergeoptions = --no-commit --no-ff

OPはコメントを追加します。

[短命]ブランチの早送りにはある程度の意味があると思いますが、デフォルトのアクションにすることは、gitがあなたを想定していることを意味します...多くの場合[短命]ブランチがあることを意味します。合理的ですか?

Jefromiの回答:

ブランチの寿命はユーザーごとに大きく異なると思います。ただし、経験豊富なユーザーの間では、ブランチの寿命がはるかに長くなる傾向があります。

私にとって、存続期間の短いブランチは、特定の操作(リベース、可能性、または迅速なパッチとテスト)を簡単にするために作成し、完了したらすぐに削除するブランチです。
つまり、分岐したトピックブランチに吸収される可能性が高く、トピックブランチは1つのブランチとしてマージされます。特定の機能を実装する一連のコミットを作成するために、私が内部で何をしたかを知る必要はありません。

より一般的には、私は追加します:

それは本当にあなたの開発ワークフローに依存します:

  • 線形の場合、1つのブランチが意味を成します。
  • 機能を分離して長期間作業し、それらを繰り返しマージする必要がある場合は、いくつかのブランチが理にかなっています。

いつ分岐すべきですか?参照してください

実際、Mercurialブランチモデルを考えると、コアはリポジトリごとに1 つのブランチです匿名のヘッド、ブックマーク、さらには名前付きブランチを作成できます)。「GitとMercurial-比較とコントラスト」を
参照してください。

Mercurialはデフォルトで匿名の軽量コードラインを使用します。このコードラインはその用語では「ヘッド」と呼ばれます。
Gitは、名前付きの軽量ブランチを使用し、リモートリポジトリ内のブランチの名前をリモートトラッキングブランチの名前にマップするために単射マッピングを使用します。
Gitはブランチに名前を付けることを強制します(まあ、名前のない単一のブランチを除いて、これは「切り離されたHEAD」と呼ばれる状況です)が、これはトピックブランチワークフローなどのブランチの多いワークフローでより効果的に機能すると思います単一のリポジトリパラダイムにおける複数のブランチ。


すごい速かった。;)--no-ffオプションについては知っていますが、機能を台無しにして初めて早送りについて知ることができます。短命の枝については早送りすることにある程度の意味があると思いますが、それをデフォルトのアクションにすることは、gitがそのような短命の枝を最も頻繁に持っていると想定していることを意味します。合理的ですか?
Florian Pilz、

@フローリアン:これはプロセスの合理的な見方だと思います。マスターへのマージを管理する方法を設定する構成の例を追加しました。
VonC、

おかげで、設定ファイルはそのような問題を回避するのに役立つはずです。:)この構成をローカルで「git config branch.master.mergeoptions '--no-ff'」を使用してのみ適用
Florian Pilz

2
@BehrangSaeedzadeh:リベースは別のトピックです(このページで一度も言及されていません):featureブランチをパブリックリポジトリにプッシュしていない限り、master何度でもリベースできます。stackoverflow.com/questions/5250817/…を
VonC '28 / 10/28

4
@BehrangSaeedzadeh:リベース自体は履歴を線形にしません。それはどのようにあなたがfeatureブランチの変更をに統合するであり、masterそれは言われた歴史を線形にさせるにさせます。単純な高速順方向マージにより、線形になります。stackoverflow.com/questions/7425541/…で言及されているように、早送りマージのにそのfeatureブランチの履歴をクリーンアップし、重要なコミットのみを残している場合、これは理にかなっています。
VonC、2011年

42

VonC非常に包括的な答えを少し拡張してみましょう:


まず、私が正しく覚えているとすれば、Gitがデフォルトで早送りの場合にマージコミットを作成しないという事実は、同期に相互プルが使用される単一ブランチの「等しいリポジトリ」を検討することから来ています。これら2つのリポジトリする(a 「The Git User's Manual」や「Version Control by Example」など、ほとんどのユーザーのドキュメントで最初の例として見つけることができるワークフロー。この場合、完全に実現されたブランチをマージするためにプルを使用せず、他の作業に追いつくためにそれを使用します。将来のために保存され、リポジトリに保存および保存された同期を実行するときに、一時的で重要ではない事実を持ちたくありません。

機能ブランチの有用性と単一のリポジトリに複数のブランチを持つことの有用性は、VCSのより広範な使用と優れたマージサポート、およびさまざまなマージベースのワークフローの試行によって、後になって初めて得られたものです。そのため、たとえば「Mercurial:The Definitive Guide」の古いリビジョンで見られるように、Mercurialは元々リポジトリごとに1つのブランチ(およびリモートブランチを追跡するための匿名のヒント)のみをサポートしていました。


次に、機能ブランチを使用するベストプラクティスに従う場合、つまり、機能ブランチはすべて安定バージョン(通常は最後のリリースから)から開始する必要があります。マージする機能ブランチを選択することで、含める機能をチェリーピックして選択できるようにするには、通常、早送りの状況ではありません ...このため、この問題は議論の余地があります。最初のブランチをマージするときは、早送りではなく、真のマージを作成することを心配する必要があります( 'マスター'にシングルコミットの変更を直接配置しない場合)。もちろん、他のすべてのマージは早送りではありません。

HTH


安定版リリースからの機能ブランチについて:次のリリースのために開発している機能が、すでに開発してマスターにマージした別の機能の変更に依存している場合はどうなりますか?確かに、マスターからこの2番目の機能ブランチを作成する必要がありますか?
dOxxx 2013年

1
@dOxxx:はい、例外があります。たとえば、1つのブランチが他のブランチ上に構築される場合(直接、または以前のブランチをマスターにマージした後)。
JakubNarębski2013年
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.