88

公開された作品を決してリベースしてはならない、危険だ、などと聞いたことはありますが、リベースが公開された場合の状況に対処する方法について投稿されたレシピを見たことがありません。

これは、リポジトリが既知の (できれば少数の) グループによってのみクローンされている場合にのみ実現可能であることに注意してください。フェッチ(!)。

私が見た明らかな解決策の 1 つは、ローカル コミットがなく、fooリベースされた場合に機能します。

git fetch
git checkout foo
git reset --hard origin/foo

これにより、ローカルの状態がfoo破棄され、リモート リポジトリの履歴が優先されます。

しかし、そのブランチで大幅なローカル変更をコミットした場合、どのように状況に対処すればよいでしょうか?

4

3 に答える 3

75

プッシュされたリベースの後に同期を取り戻すことは、ほとんどの場合、それほど複雑ではありません。

git checkout foo
git branch old-foo origin/foo # BEFORE fetching!!
git fetch
git rebase --onto origin/foo old-foo foo
git branch -D old-foo

すなわち。最初に、リモート ブランチが元々あった場所のブックマークを設定し、それを使用して、その時点からリベースされたリモート ブランチにローカル コミットを再生します。

リベースは暴力のようなものです。問題が解決しない場合は、さらに多くのことが必要になるだけです。☺</p>

もちろん、リベース前のorigin/fooコミット ID を調べてそれを使用すれば、ブックマークなしでこれを行うことができます。

これは、フェッチする前にブックマークを作成するのを忘れた状況に対処する方法でもあります。何も失われることはありません。リモート ブランチの reflog を確認するだけです。

git reflog show origin/foo | awk '
    PRINT_NEXT==1 { print $1; exit }
    /fetch: forced-update/ { PRINT_NEXT=1 }'

origin/fooこれにより、履歴を変更した最新のフェッチの前を指していたコミット ID が出力されます。

その後、簡単にできます

git rebase --onto origin/foo $commit foo
于 2010-11-03T07:08:40.390 に答える
11

git 1.9/2.0 Q1 2014 以降では、Aristotle Pagaltzis回答で説明されているように、書き換えられた上流ブランチにリベースする前に、以前のブランチの起点をマークする必要はありません。コミット 07d406bコミット d96855f
を 参照してください。

topicで作成されたブランチで作業した後git checkout -b topic origin/master、リモート トラッキング ブランチの履歴origin/masterが巻き戻されて再構築された可能性があり、次のような履歴になります。

                   o---B1
                  /
  ---o---o---B2--o---o---o---B (origin/master)
          \
           B3
            \
             Derived (topic)

以前origin/masterは commits を指していましたが、B3現在は を指しており、あなたのブランチは が だったときにその上で開始されました。B2B1Btopicorigin/masterB3

このモードでは、 の reflog をorigin/masterフォークB3ポイントとして使用してtopic、更新された の上に をリベースできるようにorigin/masterします。

$ fork_point=$(git merge-base --fork-point origin/master topic)
$ git rebase --onto origin/master $fork_point topic

そのため、git merge-baseコマンドには新しいオプションがあります。

--fork-point::

ブランチ (または につながる履歴<commit>) が別のブランチ (または任意の参照) から分岐したポイントを見つけます<ref>
これは、2 つのコミットの共通の祖先を探すだけでなく、 の reflog も考慮して、以前のブランチの化身からフォークされ<ref>た履歴があるかどうかを確認し<commit><ref>ます。


" git pull --rebase" コマンドは、base" " ベース" が " "ブランチは巻き戻され、再構築されました。

たとえば、履歴が次のようになっているとします。

  • base" " ブランチの現在の先端は にありますBが、以前のフェッチでは、その先端 が現在のコミットに到達する前に、そして次にB3あったことが観察されました。B2B1
  • B3最新の「ベース」の上にリベースされているブランチは commitに基づいています。

現在のヒント " " の祖先であるコミットが見つかるまで、" " の出力 (つまり、、、、)をB3調べgit rev-list --reflog baseB見つけようとします。B1B2B3Derived (topic)

内部的には、get_merge_bases_many()これを一度に計算できます。" " の過去のすべてのヒントをマージすることによって生じる架空のマージ コミットとの
間のマージ ベースが必要です。 そのようなコミットが存在する場合、" " の reflog エントリの 1 つと完全に一致する単一の結果が得られるはずです。Derivedbase (origin/master)
base


Git 2.1 (2014 年第 3 四半期) は、この機能をより堅牢にする機能を追加します。John Keepingによるcommit 1e0dacdを参照してください ( )johnkeeping

次のトポロジがあるシナリオを正しく処理します。

    C --- D --- E  <- dev
   /
  B  <- master@{1}
 /
o --- B' --- C* --- D*  <- master

どこ:

  • B'の修正バージョンでありB、パッチと同一ではありませんB
  • C*およびはそれぞれ およびとD*パッチが同一であり、間違った順序で適用するとテキストが競合します。CD
  • Eにテキスト的に依存しDます。

の正しい結果git rebase master devは、 が とのB分岐点として識別されるため、、、は に対してリプレイする必要があるコミットです。ただし、 andはand and and soとパッチが同一であるため、削除できるため、最終結果は次のようになります。devmasterCDEmasterCDC*D*

o --- B' --- C* --- D* --- E  <- dev

分岐点が識別されない場合、Bを含むブランチを選択B'すると競合が発生し、パッチと同一のコミットが正しく識別されない場合、(または同等の)Cを含むブランチを選択すると競合が発生します。DD*


" " の " --fork-point" モードはgit rebase、2.20 時代にコマンドを C で書き直すと後退しましたが、Git 2.27 (Q2 2020) で修正されました。

Junio C Hamano ( )によるコミット f08132f (2019 年 12 月 9 日)を参照してください。( 2020 年 3 月 27 日コミット fb4175bJunio C Hamanoによってマージされました)gitster
gitster

rebase:--fork-point回帰修正

署名者: Alex Torok
[jc: 修正を改良し、Alex のテストを使用]
署名者: Junio C Hamano gitster@pobox.com

" " は、短い refname を処理し、基になる関数を呼び出す前に完全な refname に dwim する方法を知っているgit rebase --fork-point master" " を内部的に呼び出していたため、問題なく動作していました。git merge-base --fork-pointget_fork_point()

これは、コマンドが C で書き直された後は当てはまりません。これは、直接行われた内部呼び出しがget_fork_point()短い参照を dwim しないためです。

「git merge-base」で使用されている「refname引数を完全なrefnameにdwimする」ロジックを基になるget_fork_point()関数に移動して、「git rebase」の実装で関数の他の呼び出し元が同じように動作するように修正しますこの回帰。


Git 2.31 (2021 年第 1 四半期) では、" git rebase --[no-]fork-point" ( man ) " に構成変数が追加rebase.forkPointされたため、ユーザーはデフォルト以外の設定を指定し続ける必要がなくなりました。

commit 2803d80 (2021 年 2 月 23 日) by Alex Henrie ( alexhenrie)を参照してください。
( 2021 年 2 月 25 日、コミット 682bbadJunio C Hamanoによってマージされました)gitster

rebase: の構成オプションを追加します--no-fork-point

署名者: Alex Henrie

一部のユーザー (私自身を含む) は、コミットをサイレントにドロップできるため、デフォルトでこの機能をオフにすることを好みます。

git configmanページに含まれるようになりました:

rebase.forkPoint

false に設定--no-fork-pointすると、デフォルトでオプションが設定されます。

于 2013-12-06T11:43:04.413 に答える
11

git-rebase の man ページのアップストリーム リベースからの回復セクションでは、このほとんどすべてがカバーされていると思います。

これは、自分のリベースから回復するのとまったく同じです。ブランチを 1 つ移動し、それを履歴に持っていたすべてのブランチを新しい位置にリベースします。

于 2010-11-03T15:39:19.630 に答える