web-dev-qa-db-ja.com

git:スカッシュマージ後にブランチを削除できないのはなぜですか?

mainlinemasterと同等)といくつかのローカル機能ブランチを含むgitリポジトリがあります。例えば:

$ git branch
* mainline
  feature1
  feature2
  feature3

次のようにすると、機能ブランチのすべての編集をmainlineへの1つのコミットにスカッシュマージできます。

$ git checkout mainline
$ git pull
$ git checkout feature1
$ git rebase mainline
$ git checkout mainline
$ git merge --squash feature1
$ git commit
$ git Push

私の質問は、現時点でfeature1ブランチを削除しようとすると、完全にマージされていないことを示しています。

$ git branch -d feature1
error: The branch 'feature1' is not fully merged.
If you are sure you want to delete it, run 'git branch -D feature1'.

このエラーの原因は何ですか? git merge --squash feature1feature1mainlineに統合したと思いました。

22
user1002119

これは、スカッシュマージがブランチ固有のさまざまなコミットと「同等」であることをGitが認識していないために発生します。 git branch -Dではなくgit branch -dを使用して、ブランチを強制的に削除する必要があります。

(これの残りは単に約なぜこれが事実です。)

コミットグラフを描く

コミットグラフ(の一部)を描きましょう(このステップはGitの多くのことに適しています...)実際、もう1つステップを戻して、git rebaseの前に次のようなコードを追加します。

...--o--o--o     <-- mainline
      \
       A--B--C   <-- feature1

ブランチnameは、mainlinefeature1のように、1つの特定のコミットのみを指します。そのコミットは前のコミットを指すように(左方向に)戻ります。実際のブランチを形成するのはこれらの後方ポインターです。

上の行のコミットはすべてoと呼ばれていますが、これはちょっと退屈なので、文字名は付けていません。コミットの一番下の行A-B-Cは、ブランチfeature1ではonlyです。 Cはそのような最新のコミットです。 Bに戻り、Aは退屈なoコミットの1つに戻ります。 (余談ですが、左端のoコミットは、...セクション内の以前のすべてのコミットとともに、両方ブランチにあります。)

git rebaseを実行すると、3つのA-B-Cコミットがコピーからnewコミットにmainlineの先端に追加され、 :

...--o--o--o            <-- mainline
      \     \
       \     A'-B'-C'   <-- feature1
        \
         A--B--C       [old feature1, now abandoned]

新しいA'-B'-C'コミットは元の3つとほとんど同じですが、グラフでは移動です。 (現在、3つの退屈なoコミットは両方のブランチにあることに注意してください。)元の3つを放棄すると、通常、Gitはコピーをオリジナルと比較しませんhave。 (オリジナルが他の名前(たとえば、古いfeature1に追加されたブランチ)で到達可能であった場合、Git can少なくともほとんどの場合、これを理解します。正確な詳細Gitがこれをどのように計算するかについては、ここでは特に重要ではありません。)

とにかく、今度はgit checkout mainline; git merge --squash feature1を実行します。これにより、feature1上にある3つのコミットの「スカッシュコピー」である1つの新しいコミット、または多くのコミットが作成されます。私は古い捨てられたものを描くのをやめて、スカッシュのために新しいスカッシュコミットSを呼び出します:

...--o--o--o--S         <-- mainline
            \
             A'-B'-C'   <-- feature1

「安全性の削除」は、完全にコミット履歴によって決定されます

Gitにfeature1の削除を依頼すると、安全性チェックが実行されます:「feature1mainlineにマージされますか?」この「マージされる」テストは、純粋にグラフ接続に基づいています。名前mainlineはコミットを示しますS; commit Sは、最初の退屈なoコミットを指します。これにより、より退屈なoコミットに戻ります。 C'の先端であるfeature1のコミットはnotSから到達可能です:右方向への移動は許可されていません。左方向への移動のみが許可されています。

これを "通常の"マージMと比較してください。

...--o--o--o---------M   <-- mainline
            \       /
             A'-B'-C'    <-- feature1

同じテストを使用して、「mainlineのティップコミットからfeature1のティップコミットに到達可能ですか?」—コミットMには、C'をコミットするための左下のリンクがあります。 (コミットC'は、Gitの内部用語では、マージコミットMの-​​2番目の親です。)

スカッシュマージは実際にはmergesではないため、SからC'への接続はありません。

繰り返しますが、GitはSA'B'、またはC'と「同じ」かどうかを確認するtryさえしません。ただし、そうした場合、Sは3つのコミットすべてのsumと同じであるため、「同じではない」と表示されます。 Sを押しつぶしたコミットに一致させる唯一の方法は、そのようなコミットを1つだけにすることです(この場合、最初に押しつぶす必要はありません)。

41
torek