13

OK、私は少し混乱しました。どうやら、私の自宅のマシンでは、開発ブランチが更新されていませんでした。コミットしてプッシュしました。その結果、実際の origin/develop ブランチがローカルの develop ブランチにマージされました。これは、何らかの理由で別のブランチと見なされていました。

一つには、これがどのように起こったのか本当に理解できません。第二に、これを元に戻すことはできますか?

これを説明するために、ネットワークは次のようになります。

 local-develop ---------------------------------- C*--- M -
origin/develop --- C --- C --- C --- C --- C --------- / 

私が本当に望んでいたのは、C* がブランチをマージするのではなく、オリジン/デベロップにコミットすることでした。

私が言ったように、これはすでにプッシュされています。変更を削除して、希望どおりにコミットする方法はありますか?

たとえば、私は:

git reset --hard HEAD~1

これによりマージが取り消されるかどうかはわかりませんが、2 つの異なる開発があり、マージが削除されたなど...?

4

2 に答える 2

27

マージはプッシュ時に発生するのではなく、発生しますgit merge(まあ、pullそれは実際には単なるフェッチ + マージなので、マージ時にマージが発生します)。

次のようなことをした可能性が高いようです。

<get copy from origin/develop>
<wait around for remote's origin/develop to acquire new commits>
git checkout develop      # get onto (local) develop branch
<edit>
git commit -m message     # create some new commit(s)
git push origin develop   # attempt to push -- but this fails!
git pull

Mマージ コミット (上記)を作成するのは最後のステップです。これpullは、 fetch(現在 にあるすべての新しいコミットを取得するorigin/develop)、次にmerge(ローカルdevelopを取得して、フェッチしたばかりの新しいコミットとコミットをマージする) ことを意味するためです。

この新しいgit push結果を編集していない場合リモート リポジトリにはローカル コミット (ラベルを付けたコミットと. この場合、あなたは元気です!(もう一度実行して、ローカルのレポがリモートのレポと一致することを確認してから、その内容を確認してください。)C*Mgit fetchorigin/developgit log origin/develop

ここには 2 つの完全に別個の git リポジトリがあることを覚えておくと役立つかもしれません。そしてあなたがoriginここに呼んでいるもの。(そのマシンを と呼びましょうX。) にログオンする場合Xは、独自のコミット履歴とブランチ名などがあります。そこで、ディレクトリをレポに変更し、 を実行git log -1 developして、そのブランチの先端にあるものを確認します。

ログオフしXて自分のマシンに戻ったら、git log -1 origin/develop. Xそれが で見たものと同じである場合はget fetch、更新するgit fetch必要はありません。実際には (しかしより効率的に) にログオンしてX、そこにあるものを確認するdevelopためです。にないものXはすべてorigin/develop、 にfetch持ち込まれて追加されorigin/developます。これで と同期しましXた。 Xあなたのものはありませんが、あなたは彼らのものを持っています。

merge次に、 (によって暗示されるものを含む)を実行する追加の手順を実行するpullと、必要に応じて、gitはマージコミットを行います...しかし、これはすべて、ブランチの先端にあるリポジトリにあります(まだdevelopこの場合)。このマージ コミットをプッシュしない限りX(または誰かXがあなたのコミットをプルしますが、今は無視しましょう :-))、Xそれはありません。

とにかく、リモート(Xここ)にマージコミットがない限り、あなたはゴールデンです。彼らはそれを持っていないので、他の誰も持っていません。ブランチの を実行して、 commit( ) を の上に置くことができrebaseます。これにより、マージ コミット ( ) が削除され、単純な早送りを にプッシュできます。developC*origin/developMorigin/develop

X マージ コミットがある場合(つまり、編集後にプッシュしてマージを取得した場合) は、おそらく他の人がマージ コミットにpullアクセスして使用しているため、(ある程度) スタックしています。などを使用して独自のレポでこれを行う方法と同様にX、レポをロールバックすることは可能ですが、一般的には悪い考えです。 Xgit resetgit rebase


ここで、実際に (マシン上の) 他のレポにプッシュしたとしますX、他の誰もあなたの変更をまだ見ていないと確信しており、それらを元に戻すのではなく、間違いなくリセットするつもりです (元に戻すと、"これにより、他の誰もが簡単に回復できますが、エラーを確認することもできます :-) )。1

トリックは次のとおりです。最初に、マシン X のリポジトリに「ブランチの先端develはコミット C7 です」と言う必要があります。ここで、C7 は以前の独自の図にあり、各コミットに別の名前を付けることができるように番号が付け直されています。

--------------------------- C*--- M
--- C4 --- C5 --- C6 --- C7 ---- /

それで、どうすればそれができますか?X1 つの方法は、2 でレポにログインしcd(たとえそれが であっても--bare)、そこで使用するgit update-refことです。C7のSHA1が実際にあるとしましょう50db850(「git log」で示されています)。次に、これを行うことができます:

localhost$ ssh X
X$ cd /path/to/repo.git
X$ git update-ref refs/heads/develop 50db850

ただし、 にログインできない場合X、または単にログインしたくない場合でも、 で同じことができますgit push -f3 (これには他の利点があります。特に、正常に完了すると、git リポジトリはorigin/develop巻き戻されたことを認識しpush -fます。) 正しいコミットを指すローカルブランチチップを作成するだけです: 4

localhost$ git branch resetter 50db850
localhost$ git log resetter         # make sure it looks right
...
localhost$ git push -f origin resetter:develop
Total 0 (delta 0), reused 0 (delta 0)
To ssh://[redacted]
 + 21011c9...50db850 resetter -> develop (forced update)
localhost$ git branch -d resetter   # we don't need it anymore

これが完了すると、マシン X は希望の状態に戻り、気に入らなかったマージをプッシュしたことがないかのように続行できます。

push -fを実行すると、他の誰かが の上に新しいコミットを行った場合M、それら非表示になることに注意してください(技術lost+found的には、マージコミットと一緒にそこに残っていますが、git fsck --lost-found、および数か月後、彼らは本当に永遠に消えてしまいます)。5

繰り返しになりますが、非常に重要です。この種の「共有リポジトリのロールバック」は、その共有リポジトリの他のユーザーにとって大きな苦痛なので、実行する前に問題がないことを確認してください。


1この種の単純なマージは元に戻す必要さえありません。そこにマージを残すことには、根本的な問題はありません。しかし、より深刻な誤ったマージを扱っている場合、マージを元に戻すことには、「おっと」の記録以外に、もう 1 つの欠点があります。後で変更を「やり直す」ことが少し難しくなります。わざと」は、以前のマージを見て、「OK、それらの変更を再マージする必要はない」と考えます。次に、代わりに「元に戻す」必要があります。

ここでの正しい教訓は、プッシュする前に( ) を見git logて、プッシュしようとしているものがプッシュしようとしているものであることを確認することだと思います。

2または、より簡単で簡単: git ls-remote. これは、フェッチ プロトコル コードを使用して、リモートの内容を確認します。しかし、それは私がここで目指しているテーマを隠しています。つまり、リモートはあなたのものと同じようにレポです!

3今後の git バージョン 2 リリースには、 で使用できる新しい「安全機能」がありますgit push -f。しかし、それらはまだ出ていないので、役に立ちません。後でこれを再編集して、それらに注意してください。それまでは、本当に気をつけてください: この時点で、新しいものを共有リポジトリにプッシュしようとしている他の誰かと競争しています。

4生の SHA-1 ID でこれを行うこともできます。ただし、ブランチ名は、複数回続けて正確に入力する方が簡単です。

5保持と有効期限は、git の「reflogs」を介して行われます。共有サーバーがすべての参照更新をログに記録していない可能性があります。そうでない場合は、新しいコミットを既に持っているプラ​​イベート リポジトリのみがそれらを保持します。ブランチ チップから到達できなくなったコミットの場合、最短のデフォルトの有効期限は 30 日、つまり約 1 か月です。ただし、共有リポジトリから「失われた作業」を強制的にプッシュした後、全員に「失われた」コミットをリポジトリで検索させるのは面白くない、クレイジーなスクランブルであることに注意してください。

于 2012-04-16T20:56:56.070 に答える
1

Linus Torvals による回答はこちら Linus Torvals による回答はこちらhttp://opensource.apple.com/source/Git/Git-26/src/git-htmldocs/howto/revert-a-faulty-merge.txt

于 2013-10-18T15:36:49.693 に答える