8

私は 1 年間の大半で git を楽しく使用しており、私たちのチームは git-flow モデルを使用しています (ただし、git-flow 自体ではありません)。先週、私はリリース ブランチを作成し、変更を開発/不安定ブランチにマージして戻しました。今日、非常に奇妙なことが起こるまでは。

どういうわけか、リリース ブランチがハイパーマージされた、または何か。正直なところ、「川を渡る」よりも良い表現が思い浮かびません。Git 拡張機能のビジュアルを次に示します。

交差ストリーム GitEx

コミットコメントのほとんどを難読化しています。申し訳ありません。しかし、下から見ると、左側に開発ブランチ、右側にリリース ブランチが定期的にマージされていることがはっきりとわかります。その時点で、リリース ブランチは文字通り開発ブランチとマージされたため、マージで何か間違ったことをしたに違いない途中まで、実際には、開発ブランチにのみあるはずのマージ コミットを共有しているように見えます。

GitHub は、もう少し明確な見解を示しています。

交差ストリーム GitHub

青と黒の線が同じ枝であることを除けば、これはそれほど珍しいことではありません。明らかなことにチェックを入れるだけです:

  • 他の貢献者からのコミットがありますが、問題の特定のコミットは私からのものです。コミットメッセージを信頼できることを知っています。

  • コミット メッセージはすべて同じことを言っています。「ブランチ 'release-1.3' を開発にマージします。」したがって、たとえば、誤って開発ブランチをリリース ブランチにマージしなかったことはわかっています。

  • git pull --rebase新しいブランチを作成したり、git commit、 、以外のコマンドを使用したことはありませんgit merge。私はリポジトリで何かクリエイティブなことを実験したりしようとしていたわけではありません。

元の枝を落として途中の枝から新しく作ったみたいです。しかし、私はそれをしませんでした。ブランチはずっとアクティブです。青と黒のコミットは実際には同じブランチであり、両方を開発 (ピンク) ブランチにマージする以外にマージを行っていないことを誓います。release-1.3developrelease-1.3

だから私は2つの質問があります:

  1. これはどのように可能ですか?gitコミットがグラフノードであるなど、技術的にそれがどのように可能であるかを理解していますが、手続きの観点からこれがどのように起こったかを完全に理解することはできません。これを引き起こす可能性のある間違いは何ですか?また、どうすれば再発を避けることができますか?

  2. これらのブランチのもつれを解くにはどうすればよいのでしょうか?きしみのないクリーンなリリース ブランチに不安定なブランチからの未完成のコードが大量に含まれないようにするにはどうすればよいですか? マージによって時折接続される 2 本の平行線のように見えるはずです。これは共有リポジトリであるため、できれば強制プッシュや破壊的なリベースを行わずにこれを行いたいと考えていますが、これは小さなチームでプライベート リポジトリであるため、絶対に必要な場合に人々に再クローンを作成してもらうことができます。

4

2 に答える 2

4

可能性

これが起こった可能性があるいくつかの方法を考えることができます。

  • 早送りマージ。 これが一番の原因だと思います。これは私が信じていることです:

    1. release-1.3に合併されましたdevelop
    2. 直後に、develop早送りマージされましたrelease-1.3


    それで全部です。 release-1.3にマージされた後、 にマージされた誰かdevelopに対して追加のコミットが行われる前。可能であれば、デフォルトで早送りマージを行います (半分の時間で間違ったことを行う「機能」)。これが、グラフがわかりにくく見える理由です。release-1.3developrelease-1.3git

    早送りマージは、結果のグラフに直接的な証拠を残さないことに注意してください。通常のマージとは異なり、早送りマージは新しいコミット オブジェクトを作成せず、既存のコミットは変更されません。早送りマージは、既存のコミットを指すようにブランチ参照を調整するだけです。

    早送りマージの直接的な証拠はありませんが、各ブランチの最初の親パスをたどることでその 1 つのマージ コミットに到達できるという事実は、これが早送りマージの結果であることを強く示しています。

  • git branchの代わりに臨時記号git checkout私が取り組んでいたプロジェクトで、Git を初めて使用する別の開発者が、git branch foobranch に切り替えようとしてタイプミスを犯しましたfoo。これは完全に自然な間違いであり、専門家でさえ疲れたときに犯す可能性があります. とにかく、この間違いは最終的に、入力されていないにもかかわらず、早送りマージのように見えるものになりましたgit merge。ここでも同様のことが起こった可能性があります。シナリオの展開は次のとおりです。

    1. ユーザーはdevelopブランチをチェックアウトしており、developたまたまこのミステリーの中心にあるマージ コミットを指しています。
    2. release-1.3ユーザーはブランチに切り替えて作業を行いたいと考えていますが、誤って のgit branch release-1.3代わりに入力してしまいgit checkout release-1.3ました。
    3. これは新しいクローンであるため、release-1.3まだローカル ブランチはありません (のみorigin/release-1.3)。したがって、Git はrelease-1.3、同じマージ コミットを指す、という名前の新しいローカル ブランチを問題なく作成します。
    4. ユーザーがいくつかのファイルを編集している間、しばらく時間が経過します。
    5. コミットの準備として、ユーザーはgit status. 現在のブランチはまだdevelopであり、そうではないためrelease-1.3、Git は「ブランチ開発中」と出力します。
    6. ユーザーは驚いて、「ずっと前に release-1.3 に切り替えたんじゃないの? ああ、ブランチを切り替えるのを忘れていたに違いない」と考えます。
    7. ユーザーは を実行しますがgit checkout release-1.3、今度は正しいコマンドを覚えています。
    8. ユーザーはコミットを作成して実行しますgit push
    9. Git のデフォルトのプッシュ動作 ( を参照) はであるため、アップストリーム ブランチが構成されていなくても、push.defaultプッシュ先のアップストリームのブランチを選択します。git help configmatchingrelease-1.3git pushrelease-1.3
    10. の新しいバージョンはrelease-1.3以前の の子孫であるrelease-1.3ため、リモート リポジトリは喜んでプッシュを受け入れます。


    これは、質問で提供したグラフを作成するために必要なすべてです。

develop意図的にマージされたと仮定しますrelease-1.3

developへのマージが意図的なものである場合(誰かが出荷するのに十分なrelease-1.3意識的な決定を行った場合)、これは完全に正常であり、正しいことです。develop視覚的な違いはありますが、青と黒の線は両方ともrelease-1.3枝にあります。青い線はたまたま枝にもあります。develop

唯一間違っているのは、歴史を見直して何が起こっているのかを理解するのが少し面倒だということです (それ以外の場合、この質問はありません)。これが再び起こらないようにするには、次の経験則に従ってください。

  • 2 つの異なる名前のブランチをマージする場合 (たとえば、developrelease-1.3常にgit merge --no-ff.
  • develop同じブランチの 2 つのバージョン (例:とorigin/develop)をマージする場合は、常にgit merge --ff-only. 早送りできないためにそれが失敗した場合は、git rebase.

上記の経験則に従った場合、グラフは次のようになります。

*   (develop)
| * (release-1.3)
* | Merge...
|\|
| * Added...
| * using ...
* | adding...
| * Hide s...
* | Date ...
* | updati...
* | Candi...
| * Locali...
| * <---- merge commit that would have been created by
|/|      'git merge' had you used the '--no-ff' option
* | Merge...
|\|
| * Un-ign...
| * Added...
* | Merge...
|\|
| * Remov...
| * Move...
* | Fixed...

追加のマージ コミットにより、履歴が読みやすくなっていることに注目してください。

developへのマージがrelease-1.3間違っていた場合

どっ!リベースと強制プッシュが必要なようです。これでは、このリポジトリの他のユーザーは満足しません。

これを修正する方法は次のとおりです。

  1. 実行しますgit checkout release-1.3
  2. その中間コミットの sha1 を見つけます (2 つのブランチが一緒になる場所)。と呼びましょうX
  3. 実行しますgit rebase --onto X^2 X。結果のグラフは次のようになります。

    *     (develop)
    |   * (release-1.3)
    *   | Merge...
    |\  |
    | * | Added...
    | | * Added...
    | * | using ...
    | | * using ...
    * | | adding...
    | * | Hide s...
    | | * Hide s...
    * | | Date ...
    * | | updati...
    * | | Candi...
    | * | Locali...
    |/  * Locali...
    *  / Merge...
    |\|
    | * Un-ign...
    | * Added...
    * | Merge...
    |\|
    | * Remov...
    | * Move...
    * | Fixed...
    

    これでブランチは修正されますが、コミットrelease-1.3の 2 つのバージョンがあることに注意してください。次の手順では、これらの重複をブランチrelease-1.3から削除します。develop

  4. 実行しますgit checkout develop
  5. git branch tempこのコミットの一時的なプレースホルダーとして機能するために実行します。
  6. 実行して、ブランチgit reset --hard HEAD^^から 2 つのコミットを削除します。tip コミットと、古いバージョンの を にマージするコミットです。そのヒントのコミットは後で復元します。developdeveloprelease-1.3develop
  7. を実行して、新しいブランチとその祖先git merge --no-ff release-1.3^の 2 番目のコミットを にマージします。release-1.3develop
  8. 実行git cherry-pick tempして、手順 6 で削除されたヒント コミットを元に戻します。
  9. git branch -D temp一時的なプレースホルダー ブランチを削除するために実行します。グラフは次のようになります。

    *   (develop)
    | * (release-1.3)
    * | Merge...
    |\|
    | * Added...
    | * using ...
    * | adding...
    | * Hide s...
    * | Date ...
    * | updati...
    * | Candi...
    | * Locali...
    * | Merge...
    |\|
    | * Un-ign...
    | * Added...
    * | Merge...
    |\|
    | * Remov...
    | * Move...
    * | Fixed...
    
  10. 実行git push -f origin release-1.3 developして、上流のブランチを強制的に更新します。

今後このようなことが起こらないようにする

アップストリーム リポジトリを制御でき、いくつかのフックをインストールできる場合は、ブランチの新しいバージョンから開始して最初の親パスをたどることで、古いバージョンのブランチに到達できないプッシュを拒否するフックを作成できます。 . これには、によって作成された愚かなコミットgit pullを拒否するという利点もあります。

于 2013-07-11T04:20:54.023 に答える
2
  1. 確かではありませんが、純粋な憶測を超えています。git log --decorate --graph --all --onelineaと a (およびおそらくシェルの履歴) を提供していただければ、おgit reflog release-1.3役に立てるかもしれません。

  2. これには強制プッシュが含まれます。これについてチームメイトと話してください。あなたがそれらを提供していないので、私はこれらのコミットのハッシュを知りません。<blah>対応するハッシュに置き換えます。

    git branch -m release-1.3 old-release-1.3
    git checkout -b release-1.3 <Un-ign> # the last good commit on the release branch
    # This will replay those five commits onto our new release-1.3 branch
    git rebase --onto release-1.3 <Merge> old-release-1.3 # the merge just above "Un-ign"
    # Verify that release-1.3 now looks correct
    # You'll need the -f. Be careful with force pushes, run -n first
    git push [-n|-f] origin release-1.3
    
于 2013-07-11T00:15:02.873 に答える