1. ホーム
  2. git

[解決済み] Git Rebaseの衝突。誰がHEADなのか?

2022-03-09 22:11:25

質問

私はこのプロジェクトを持っており、リモートリポにはメインの開発ブランチがあり、私は実験的なブランチを含むフォークを持っています。私は以下のことを要求されています。 rebase は、フォークにプッシュする前に開発ブランチから実験ブランチに変更します。つまり、次のようになります。

git checkout experimentalbranch
git fetch remoterepo
git rebase remoterepo/developmentbranch

この頃になると、コンフリクトにぶつかる。しかし 私はこれらの変更についてよく知らないのですが (私の変更をすぐにマージしてくれなかったので、数週間分の変更をリベースしているのです)。また、私は初めて rebase . 私はどちらかというと merge .

meldでは、通常、次のようになります。 <<LOCAL||REMOTE>> に対して merge というのは、とても直感的な響きです。しかし rebase である。 <<HEAD||COMMIT MESSAGE>> . 誰が HEAD ? のことでしょうか? HEAD 開発ブランチの?開発ブランチの最新コードなのか、それとも別の場所なのか?

解決方法は?

TL;DR(2018年5月追記)

Gitはその内部構造をあなたに見せるので、全体は基本的に少なくとも少しは混乱します。

ここで懸念しているケースは、実行時に発生することに注意してください。

git checkout somebranch; git rebase origin/their-branch

などがあります。 リベースはマージの競合を解決するために一時的に停止しています。 git add を実行すると、コンフリクトが解消され git rebase --continue . (何らかのマージツールを使って git mergetool や派手なGUIインターフェースでは、そのインターフェースがこの一部または全部を他の方法でやってくれるかもしれませんが、その根底にあるのは git add を実行し、解決したファイルを git rebase --continue .)

一番最初に HEAD コミットは その ブランチを使用すると git checkout --ours または git checkout --theirs , --ours というのは その -の最終コミットです。 origin/their-branch -一方 --theirs というのは あなたの は、リベースの対象となる最初のコミットです。 これは、通常の日常的な Git の混乱です ( git における "ours" と "theirs" の正確な意味は何ですか? )であり、本来の質問とは異なるものです。

しかし、その後 HEAD のコミットは、実は 一種の混合物 . それは 自分のコミットを最新のコミットの上に何回かコピーした結果 . あなたは今、自分自身の一部が構築された 新しい の一連のコミット、そして自分の オリジナル のコミットです。 この衝突の原因は 通常 何か "彼ら"がしたこと(の途中で変更された何か)。 origin/their-branch ). このコンフリクトを解決する必要があります。 解決したところで、後のコミットでまったく同じ衝突が繰り返されるかもしれません。

もう一度 HEAD または local または --ours rebase があなたの変更と彼らの変更を組み合わせて作成したコミットです。 であり、もう一方のコミット( remote または >>>>>>> または --theirs ) はあなた自身のコミットで、rebase はそのコミットを atop でコピーしようとしています。 HEAD .

長い

マージ(内部的に "merging"を繰り返す特殊なケースであるリベースも含む)を行う場合、2つの "head"(特定の2つの枝先)が関与します。 これらを your-branchorigin/their-branch :

              G - H --------      <-- HEAD=your-branch
            /               \
... - E - F                   M   <-- desired merge commit [requires manual merge]
            \               /
              I - J - K - L       <-- origin/their-branch

この点は、一般的に(そして当然のことながら)混乱しやすいのですが、このようにラベル付けすると、十分に明確になります。

しかし、さらに悪いことに、git は --ours--theirs は、マージ中のふたつの先頭コミットを指すものです。 H を実行したとき git merge であり、quot;theirs" は、まあ、彼らのものである(コミット L ). しかし、リベースを行うときは、2つの頭が逆になります。つまり、"ours" はあなたがリベースしている頭、つまり彼らの更新したコード、そして "theirs" はあなたが現在リベース中のコミット、つまりあなた自身のコードということになります。

これは、rebaseが実際には一連のチェリーピック操作を使用しているためです。 あなたはほとんど同じ絵から始めます。

              G - H           <-- HEAD=your-branch
            /
... - E - F
            \
              I - J - K - L   <-- origin/their-branch

ここでgitが行うべきことは コピー コミットの効果 GH は、すなわち git cherry-pick コミット G を実行した後、commit で再度実行します。 H . しかし、そのためには、git は スイッチ をコミットする L まず、内部的に("detached HEAD"モードを使って)。

              G - H           <-- your-branch
            /
... - E - F
            \
              I - J - K - L   <-- HEAD, origin/their-branch

これで、コミットのツリーを比較することで、リベース操作を開始できるようになりました。 FG (何を変更したかを確認するため)、次に FL (作品の一部がすでに L にない変更は L を追加します。 これは、内部的には "merge" 操作です。

              G - H           <-- your-branch
            /
... - E - F                   G'   <-- HEAD
            \               /
              I - J - K - L   <-- origin/their-branch

マージがうまくいかなかった場合 HEAD はコミット時にまだ残っています L (コミット G' はまだ存在しない)。 したがって、はい。 HEAD は、その開発ブランチの先頭です。

をコピーしたら G が存在しますが。 HEAD に移動します。 G' からの変更をコピーしようとします。 H は、同じように(diff GH であれば、diff FG' とし、その結果をマージする)。

              G - H           <-- your-branch
            /
... - E - F                   G' - H'   <-- HEAD
            \               /
              I - J - K - L   <-- origin/their-branch

繰り返しになりますが、マージに失敗して助けが必要な場合、残されたのは HEAD を指しています。 G' ではなく H' として H' はまだ存在しない。

マージがすべて成功し、コミットすると G'H' する が存在する場合、git はラベルを削除します。 your-branch コミットから H をコミットすることを意味します。 H' の代わりに

              G - H
            /
... - E - F                   G' - H'   <-- HEAD=your-branch
            \               /
              I - J - K - L   <-- origin/their-branch

で、リベースされ HEAD が再び期待通りになりました。 しかし、リベースの際に HEAD は、そのブランチチップ(コミット L ) 、あるいは新しいコミットのひとつをコピーしてそのブランチチップの後ろに追加したもの、そして --ours の末尾にある成長中のブランチを意味します。 L 一方 --theirs はコピー元のコミットを意味します ( G または H 上記)。

(これは基本的に、gitが生のメカニズムである いかに これはgitではよくあることです)。