誰かがリベースまたはリセットを公開されたブランチにプッシュした後、どのように回復/再同期しますか?


88

公開された作品をリベースしてはいけない、危険だなどと聞いたことはありますが、リベース公開された場合の対処法のレシピは見たことがありません。

さて、これはリポジトリが既知の(そしてできれば小さな)人々のグループによってのみ複製される場合にのみ実際に実行可能であることに注意してください。リベースまたはリセットをプッシュする人は誰でも、次に注意を払う必要があることを他の人に通知できますフェッチ(!)。

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

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

これはfoo、リモートリポジトリに従って、その履歴を優先してローカル状態を破棄するだけです。

しかし、そのブランチで大幅なローカル変更を行った場合、どのように状況に対処するのでしょうか。


シンプルケースレシピは+1。特にOSが異なる場合は、マシン間の個人的な同期に最適です。それはマニュアルで言及されるべきものです。
Philip Oakley

個人同期の理想的なレシピはgit pull --rebase && git pushです。あなたが上で作業している場合masterにのみ、これは非常に近くに確実にあなたがリベースともう一方の端にプッシュした場合でも、あなたのために正しいことを行います。
アリストテレスパガルツィス2013

PCとLinuxマシン間で同期および開発しているので、リベース/更新ごとに新しいブランチを使用するとうまくいくことがわかります。私もバリアントを使用するgit reset --hard @{upstream}私はのための魔法の呪文refspec知っていることを、今までの私の最後のコメントを参照してください。「私が持っているもの忘れを/、私はリモートから取得したものを使用していた」stackoverflow.com/a/15284176/717355
フィリップ・オーキー

Git2.0を使用すると、ブランチの古いオリジンを見つけることができます(アップストリームブランチがで書き換えられる前push -f):以下の私の答えを
VonC 2013

回答:


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

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

リベースは暴力のようなものです。それでも問題が解決しない場合は、さらに多くの問題が必要になります。☺

もちろん、リベース前の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

11
クイックノート:かなり直感的だと思いますが、awkがよくわからない場合は、そのワンライナーがgit reflog show origin/foo「fetch:forced-update」という最初の行の出力を調べているだけです。これは、フェッチによってリモートブランチが早送り以外のことを実行したときにgitが記録するものです。(手動で行うこともできます。強制更新はおそらく最新のものです。)
Cascabel 2010年

2
それは暴力のようなものではありません。暴力は時折楽しい
Iolo 2013

5
@iolo確かに、リベースはいつも楽しいです。
Dan Bechard 2013年

1
暴力のように、ほとんどの場合、リベースを避けます。しかし、その方法の手がかりがあります。
ボブスタイン

2
まあ、他の人が影響を受ける場所にリベースをプッシュすることは避けてください。
アリストテレスパガルツィス2015年

11

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

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


4
ああ、そうです。しかし、今ではそれが何を言っているのか理解できましたが、これを自分で理解する前は、以前は理解できなかったでしょう。そして、料理本のレシピはありません(おそらくそのようなドキュメントではそうです)。また、「ハードケース」をハードと呼ぶのはFUDであると述べます。書き直された履歴は、ほとんどの社内開発の規模で簡単に管理できることを提出します。この主題が常に扱われる迷信的な方法は私を悩ませます。
アリストテレスパガルツィス2010年

4
@Aristotle:すべての開発者がgitの使用方法を知っていて、すべての開発者と効果的にコミュニケーションできることを考えると、非常に管理しやすいというのは正しいことです。完璧な世界では、それで話は終わりです。しかし、そこにある多くのプロジェクトは十分に大きいので、上流のリベースは本当に怖いものです。(そして、私の職場のように、ほとんどの開発者がリベースについて聞いたことがない場所があります。)「迷信」は、可能な限り最も安全で最も一般的なアドバイスを提供する方法にすぎないと思います。他人のレポで災害を引き起こす人になりたいと思う人は誰もいません。
Cascabel 2010年

2
はい、その動機はわかります。そして、私はそれに完全に同意します。しかし、「結果がわからない場合は試さないでください」と「悪だから絶対にやらない」という世界には違いがあり、これだけで問題になります。恐れを植え付けるよりも、指示する方が常に良いです。
アリストテレスパガルツィス2010年

@アリストテレス:同意しました。私は「自分が何をしているのかを確認する」という終わりに向かう傾向がありますが、特にオンラインでは、グーグルからのカジュアルな訪問者が注意を払うように十分な重みを与えるようにしています。そうです、おそらくその多くはトーンダウンする必要があります。
Cascabel 2010年

11

gitの1.9 / 2.0 2014年第1四半期以降では、で説明したように、書き換え上流の枝にそれをリベースする前に、あなたの前のブランチの起源をマークする必要はありませんアリストテレス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コミット点まで使用B3B2B1今ではで指しBて、あなたのtopicとき分岐が戻ってそれの上に開始されたorigin/master時でしたB3

このモードでは、のreflogを使用してフォークポイントとしてorigin/master検索するため、次の方法で更新さB3topicorigin/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は、 "ベース"の場合に対処するために、ブランチの作業の基となった " "ブランチ(通常はリモート追跡ブランチ)のreflogエントリを使用して、リベースされるブランチのフォークポイントを計算します。ブランチが巻き戻され、再構築されました。

たとえば、履歴が次のようになっている場合:

  • 「現在の先端base」ブランチはであるBが、先にその先端がために使用することが観察フェッチB3し、その後B2、その後、B1 現在に入る前にコミットし、
  • 最新の「ベース」の上にリベースされているブランチは、コミットB3に基づいています。

それが見つけようとするB3「の出力を経てgit rev-list --reflog base」(すなわちBB1B2B3それはそれは、現在の先端の祖先であるコミット見つけるまで)「Derived (topic)」。

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


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

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

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

どこ:

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

正しい結果git rebase master devISBの分岐点として識別されるdevと、masterその結果、CDE必要に再生することをコミットしていますmaster。しかしCDしてパッチ同一であるC*D*し、従って最終的な結果であるように、ドロップすることができます。

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

フォークポイントが識別されない場合、Bを含むブランチを選択B'すると競合が発生し、パッチが同一のコミットが正しく識別されない場合、Cを含むD(または同等にD*)ブランチを選択すると競合が発生します。


--fork-point」の「」モードはgit rebase、コマンドが2.20時代にCで書き直されたときに後退しました。これは、Git 2.27(2020年第2四半期)で修正されました。

Junio C Hamano()によるcommit f08132f(2019年12月9日)を参照してください。(合併によりJunio C浜野- -fb4175bをコミットする、2020年3月27日)gitster
gitster

rebase--fork-point回帰修正

サインオフ:Alex Torok
[jc:修正を刷新し、Alexのテストを使用]
サインオフ:Junio C Hamano

git rebase --fork-point master」それは内部的に呼び出されるよう、作業OKに使用される「git merge-base --fork-point短いもしrefnameを処理し、それが根底にある呼び出す前に、フルもしrefnameにDWIMする方法を知っていた」get_fork_point()の機能を。

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

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


1
git push --force(git 1.8.5)をより慎重に実行できるようになったことに注意してください:stackoverflow.com/a/18505634/6309
VonC 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.