git にブランチを統(tǒng)合するには、git rebase と git merge の 2 つの方法が一般的ですが、実際のチーム開(kāi)発ではどちらの方法が最もよく使用されますか? どちらの方が優(yōu)れていると思いますか?
學(xué)習(xí)是最好的投資!
現(xiàn)実はこんな感じだと思います:
git を使用する人の少なくとも半數(shù)は、(基本的な機(jī)能であっても) リベースの使用方法を知りません (この推定はおそらく非常に保守的です)
殘念ながら、殘りの半分だけがいつリベースを正しく行うべきかを本當(dāng)に理解していません...
マージとリベースは、どちらかを選択するという問(wèn)題ではなく、特定の狀況に応じて選択する必要があります。実際には、ほとんどの開(kāi)発者にとって、この「特定の狀況」を 1 つずつ列挙するのは非常に難しいと言われています。チーム內(nèi)の役割は比較的単一であり、頻繁に遭遇するものではないため、ここでは全員が理解し、遵守すべきガイドラインを 2 つだけ要約します。
リモートから pull
を?qū)g行するときは、常に rebase を使用してください (1 つの例外を除き、これについては後で説明します) pull
的時(shí)候,永遠(yuǎn)使用 rebase(除了一個(gè)例外,后面會(huì)講)
當(dāng)你完成了一個(gè)功能(假定你是單獨(dú)開(kāi)本地分支去做的)后打算合并到主干分支的時(shí)候,永遠(yuǎn)使用 merge
開(kāi)發(fā)者應(yīng)當(dāng)理解:不同的合并策略影響的或許不是最終的代碼結(jié)果,但卻會(huì)影響 git 如何記錄提交的歷史(盡管很多時(shí)候這種理解只對(duì)個(gè)別人受益,而對(duì)自己沒(méi)有什么影響)。其他的人在使用這些歷史的時(shí)候(比如 code review 的時(shí)候,或者 bisect 的時(shí)候等等)會(huì)因?yàn)檫^(guò)去你做過(guò)的事情而感到無(wú)比幸?;蛲纯?。
第二點(diǎn)比較容易理解,把開(kāi)發(fā)一個(gè)功能的具體細(xì)節(jié)隱藏在本地分支中,而把最終的結(jié)果作為一條完整的記錄合并(merge)到主干分支去。
關(guān)鍵是第一點(diǎn)。大多數(shù)情況下大家會(huì)選擇默認(rèn)的 pull
,也就是 fetch + merge
,于是我們會(huì)看到主干上不停的出現(xiàn) Merge commit xxx into xxx 之類(lèi)的干擾信息。如果所有的人都圖方便這么干,那就永遠(yuǎn)都別想有干凈漂亮的歷史樹(shù)了,取而代之是類(lèi)似這樣的東西:
如果你去 clone 一個(gè)正確使用 git 的項(xiàng)目,你絕對(duì)不會(huì)看到上圖這樣亂七八糟的 master。為什么呢?答案就是:git pull --rebase
,這條命令會(huì)使用 rebase
來(lái)代替默認(rèn)的 pull
對(duì)合并的歷史樹(shù)進(jìn)行變基處理,就可以避免因?yàn)闊o(wú)法快進(jìn)(fast forward)而產(chǎn)生的額外合并記錄。
現(xiàn)在絕大多數(shù)的 git 客戶(hù)端都允許設(shè)置 git pull
的默認(rèn)行為是 --rebase
,所以掌握了這個(gè)訣竅可以讓大部分的日常拉取操作都產(chǎn)生清爽漂亮的歷史記錄,但這并不是完美的結(jié)局。有一種例外是這樣的:
在你使用 git merge
完成了一個(gè)功能分支向主干分支的合并之后(當(dāng)然是 --no-ff
pull
(fetch + merge
) を選択するため、Merge commit xxx into xxx がトランクに常に表示されます。 /em> およびその他の不穏な情報(bào)。誰(shuí)もが便宜のためにこれを行うと、クリーンで美しい履歴ツリーは決して得られず、代わりに次のようなものになります。
??git pull --rebase
です。このコマンドは、デフォルトの pull
の代わりに rebase
を使用して、マージされた履歴ツリーをリベースします。早送りできないため、追加のマージされたレコードを回避できます。 ??
??現(xiàn)在、ほとんどの Git クライアントでは、git pull
のデフォルト動(dòng)作を --rebase
に設(shè)定できるため、このトリックをマスターすれば、ほとんどの毎日の pull 操作が可能になります すべてが新鮮で美しい履歴レコードを生成します, しかし、これは完璧な終わりではありません。次のような例外があります: ??
??
???? git merge
を使用して機(jī)能ブランチをトランク ブランチにマージした後 (もちろん --no-ff
) ??
git fetch
を?qū)g行すると、リモート トランクが數(shù)コミット先にあることがわかりますgit fetch
發(fā)現(xiàn)遠(yuǎn)程主干又領(lǐng)先你若干提交
此時(shí)你若 git pull --rebase
,你會(huì)發(fā)現(xiàn)你剛才合并功能分支的記錄沒(méi)了……
這是因?yàn)樽兓鶗?huì)忽略合并記錄,因此變基命令會(huì)有一個(gè)特別的參數(shù)叫做:--preserve-merges
用于重建被忽略的合并記錄。所以比 git pull --rebase
更好的方案就是用 git fetch
+ git rebase -p
代替之。
當(dāng)然這個(gè)僅針對(duì)上面提到的特殊情況,隨著 git 的升級(jí)說(shuō)不好哪一天就不存在這種問(wèn)題了也不一定。我一般都不會(huì)遇到這種情況,因?yàn)槲叶际沁@么做的:
完成功能分支之后先不 merge,而是回到主干分支去 git pull --rebase
如果主干有更新,rebase 更新的內(nèi)容到功能分支來(lái)預(yù)檢一下,看看在加入了最近別人的改動(dòng)之后我的功能是否依然 OK(在這個(gè)過(guò)程中可能會(huì)有沖突處理,別怪我沒(méi)提醒哦)
一切就緒之后再次 git fetch
主干看看有沒(méi)有變動(dòng)(因?yàn)樵诘诙降倪M(jìn)行期間沒(méi)準(zhǔn)又有人 push 了新的變化),有的話(huà)重復(fù)第二步,沒(méi)有則——
合并功能分支到主干然后 push,收工。
我這么做可以避免前面提到的意外,看似復(fù)雜了些但其實(shí)做熟練了之后也沒(méi)什么難度,更重要的原因是 git rebase -p
是有缺陷的:
不能和 git pull
連用,一條命令分成倆,怎么都覺(jué)得“虧”了(雖說(shuō)你可以寫(xiě)腳本,但最好不要因?yàn)榱?xí)慣了之后反而有時(shí)候會(huì)害了你)
由于無(wú)法和 git pull
配合使用,所以你必須指明目標(biāo)分支給 rebase,這一點(diǎn)比較煩人,特別是很多 GUI 客戶(hù)端壓根不支持 git rebase -p
的時(shí)候(我經(jīng)常會(huì)在 CLI/GUI 之間切換環(huán)境使用 git)
ORIG_HEAD
會(huì)被破壞。
ORIG_HEAD
在很多情形下非常有用,比如說(shuō)你可以用 git log -p -reverse ORIG_HEAD
來(lái)回看最近一次合并所產(chǎn)生的所有變化等等。--preserve-merges
git pull --rebase
を?qū)g行すると、マージしたばかりの機(jī)能ブランチのレコードがなくなっていることがわかります...????
??これは、rebase がマージされたレコードを無(wú)視するためです。そのため、rebase コマンドには、無(wú)視されたマージされたレコードを再構(gòu)築するために使用される --preserve-merges
という特別なパラメーターがあります。したがって、git pull --rebase
よりも良い解決策は、代わりに git fetch
+ git rebase -p
を使用することです。 ??
?? もちろん、これは上記の特殊な狀況に限ったものであり、git のアップグレードにより、この問(wèn)題がいつかなくなるとは限りません。私はいつもこうしているので、通常はこのような狀況には遭遇しません: ??
????関數(shù)ブランチの完了後、最初にマージせず、トランクブランチgit pull --rebase
に戻ります????
????トランクが更新された場(chǎng)合は、更新されたコンテンツを関數(shù)ブランチにリベースして、他の人の最近の変更を追加した後も自分の関數(shù)がまだ問(wèn)題ないかどうかを事前チェックします (このプロセスでは競(jìng)合が発生する可能性があります。私を責(zé)めないでください)思い出させてないよ) ああ)????
????すべての準(zhǔn)備ができたら、再度トランクを git fetch
して、変更があるかどうかを確認(rèn)します (第 2 ステップで誰(shuí)かが新しい変更をプッシュした可能性があるため)。変更がある場(chǎng)合は、第 2 のステップを繰り返します。そうでない場(chǎng)合は、——????
????機(jī)能ブランチをトランクにマージしてプッシュしたら、これで終わりです。 ????
??これを行うことで、前述の事故を回避できます。少し複雑に見(jiàn)えますが、実際には、慣れれば難しくありません。より重要な理由は、git rebase -p
に欠陥があることです。 ??
???? git pull
と組み合わせて使用??することはできません。1 つのコマンドが 2 つに分割されているため、とにかく「失われた」ように感じられます (スクリプトを作成することはできますが、そうすることは避けた方がよいでしょう)。慣れると害を及ぼすこともあります) ????
????git pull
では使用できないため、リベースのターゲット ブランチを指定する必要がありますが、特に多くの GUI クライアントが git rebase -p
をサポートしていないため、これは非常に面倒です> (gitを使用するためにCLI/GUI間で環(huán)境を頻繁に切り替えます)????
????ORIG_HEAD
は破棄されます。 ????
??ORIG_HEAD
は、さまざまな狀況で非常に役立ちます。たとえば、git log -p -reverse ORIG_HEAD
を使用して、最新のマージなどによって生じたすべての変更を確認(rèn)できます。 --preserve-merges
を使用すると、それが指すはずの場(chǎng)所が失われるため、最初にそれを置き換える正しいハッシュを見(jiàn)つける必要がありますが、これは少し面倒な場(chǎng)合があります。 ??
?? 上記の答えは、初心者の場(chǎng)合、現(xiàn)実の複雑さは常に想像を超えるものであることを示しています。本當(dāng)に git を使いたい場(chǎng)合は、日々の作業(yè)に基づいて git の動(dòng)作原理を理解する必要があります。公式 Web サイトの電子書(shū)籍 (中國(guó)語(yǔ)版) にアクセスして、その中の git 內(nèi)部セクションを注意深く読むと、どのコマンドを使用するかがわかります。 ??追加のポイント: トランク ブランチ上の多くの重要でない干渉コミット レコードをチームが気にしない場(chǎng)合は、いつでもリベースを使用できます。これにより、少なくとも多くのフォークが発生しなくなります。また、それを適切に処理すれば、クリーンな (しかし冗長(zhǎng)な) ブランチ履歴を取得できます。
必須ではありませんが、プッシュ後にすべてのテストが緑色になる必要があります
(特に長(zhǎng)いブランチはありません。ほとんどは 2 ~ 3 日以?xún)?nèi)にマージされますが、1 週間以上かかることはほとんどありません)
通常はマージの難易度に応じて誰(shuí)もが選択しますが、違いがない場(chǎng)合はリベースが優(yōu)先されます...見(jiàn)た目が良いためです