1. ホーム
  2. git

[解決済み] Git リベースされたブランチをプルする

2023-06-01 15:02:23

質問

私の状況を説明させてください。

ブロンドさんとオレンジさんは、コミット M1 で master ブランチから分岐したブランチ A で作業しています。ブランチ A には 2 つのコミットがあります。A1 と A2 です。

M1  
   \
    \
     A1 - A2

一方、Orange 氏は master ブランチにさらに 2 つのコミット、M2 と M3 をコミットしてプッシュしました。

M1  - M2 - M3
   \
    \
     A1 - A2

ブロンド氏はリモートからプルし、しばらくしてから master ブランチにリベースすることにしました。

M1  - M2 - M3
   \         \
    \         \
     A1 - A2   A1` - A2`

A1` と A2` はリベースされたコミットで、ブロンドさんのローカルに存在し、A1 と A2 はリモートに存在します。ブロンドさんは自分のコミットをプッシュします。 -f を使って変更を強制し、履歴を書き換えます。これで、リモートリポジトリは以下のようになります。

M1  - M2 - M3
             \
              \
               A1` - A2`

しかし、オレンジ氏は A ブランチでも作業をしていました。彼のローカルリポジトリはまだこのような状態です。

M1  - M2 - M3
   \
    \
     A1 - A2

オレンジさんがリモートリポジトリのAブランチと同期するために必要なことは何ですか?

通常のpullではうまくいきません。ウィル プル -f はリモートからの変更をローカルに強制するのでしょうか?Aのローカルバージョンを削除し、リモートリポジトリから再び持ってくることでトリックを行うことを知っていますが、それはそれを達成するための良い方法ではないように思えます。

どのように解決すればよいでしょうか。

私のお勧めは(私がオレンジさんだったらどうするか、ですが)、次のように始めることです。 git fetch . これはブロンド氏がリベース後に "git push -f" を実行する直前まで持っていたものです。

M1  - M2 - M3
   \         \
    \         \
     A1 - A2   A1' - A2'

一つ重要な違いは、自分のローカルラベルを A が rev A2 を指していることと、リモートラベル remotes/origin/A が A2' を指しています (ブロンド氏はその逆で、ローカルラベル A がA2'を指していて remotes/origin/A はA2を指している)。

もし私が "A" という名前のブランチのコピーで作業していた場合、代わりにこのようになります。

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2'

(私のローカルラベルは、A2 ではなく A3 を指しています。) 今私がしなければならないことは、A3 (と必要なら A4 など) を A2' にリベースすることです。 1 つの明白な直接の方法です。

$ git branch -a
  master
* A
  remotes/origin/master
  remotes/origin/A
$ git branch new_A remotes/origin/A
$ git rebase -i new_A

を実行し、修正されたものが new_A に A1' と A2' として入っているので、rev A1 と A2 を完全に削除します。 あるいは

$ git checkout -b new_A remotes/origin/A
$ git format-patch -k --stdout A3..A | git am -3 -k

(この git am -3 -k メソッドに記述されています。 git-format-patch のマニュアルページで説明されています)。

これらは、ブロンド氏が自分の rebase つまり、A1, A2, A3, などを特定する必要があります。

2番目のアプローチが成功した場合、私は以下のように終わります。

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2' - A3'

私のブランチ名 new_A は A3' を指します (私の既存の A ブランチはまだ古い A3 を指しています)。 最初の方法で成功しても、結局は同じことなのですが、既存のブランチ名 A は A3' を指すようになります (そして、A1-A2-A3 の古いブランチの名前は、私のレポに残っているにもかかわらず、ありません; それを見つけるには reflogs または同様のものを通過する必要があります)。

(私の A3 が A3' になるように修正する必要がある場合、対話型リベースと "git am" の両方の方法が、もちろん私による作業を必要とします)。

もちろん、単に git merge (を使用することもできますが、その場合マージコミット ("M" 番号なし、以下同様) が作成され、rev A1 と A2 は表示したままになります。

M1  ---- M2 ---- M3
   \               \
    \               \
     A1 - A2 - A3    A1' - A2' -- M
                 \_______________/

もしあなたが元のA1とA2を保存したいのであれば、これは良いことで、もしあなたがそれらを取り除きたいのであれば、それは悪いことです。 つまり、"何をすべきか"は、"結果をどうしたいのか"によります。

編集から追加: format-patch 方式は、すべてがうまくいくことを確認する間、古い A ブランチ名を残しておけるので、より気に入っています。 すべてがうまくいっていると仮定して が良好であると仮定して、最後の数ステップを説明します。

$ git branch -m A old_A
$ git branch -m new_A A

で、old_Aを完全に捨てられるかどうか。

$ git branch -D old_A

または、同等に、ブランチ削除から始めて、new_A を A にリネームします。

(編集: 以下も参照してください。 git rebase --onto ドキュメントを参照してください。)