Git: why can't I delete my branch after a squash merge?

I have a git repo with mainline (equivalent to master ) and some local function branches. For instance:

 $ git branch * mainline feature1 feature2 feature3 

When I do the following, I can squash merge all my changes into function branches into one commit on mainline :

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

My question is that at this moment, when I try to remove the feature1 branch, it tells me that it is not completely merged:

 $ 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'. 

What causes this error? I thought git merge --squash feature1 merged feature1 into mainline .

+16
source share
1 answer

This is due to the fact that Git does not know that merging squash is equivalent to "different specific branches". You must force remove the branch with git branch -D instead of git branch -D .

(The rest is just why this is).

Draw a commit graph

Let draw (part) a commit graph (this step is suitable for many things in Git ...). Actually, let go one more step so that we start with your git rebase , with something like this:

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

A branch name, such as mainline or feature1 , points to only one specific commit. This commit points back (to the left) to the previous commit, etc., and these are the back pointers that form the actual branches.

The top line of commits, just called o here, is a little boring, so we did not give them letter names. The bottom line of commits, ABC , is only on the feature1 branch. C is the newest such fixation; it goes back to B , which goes back to A , which leads to one of the boring o commits. (Aside: the leftmost o commit, along with all earlier commits in the ... section, is in both branches.)

When you started git rebase , the three ABC commands were copied to the new commits added to the mainline tip, giving us:

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

The new A'-B'-C' commands are basically the same as the original three, but they move on the chart. (Note that all three o drills are on both branches now.) Failure to use the original three means that Git usually does not need to compare copies to the originals. (If the originals were available to a different name - a branch added to the old feature1 , for example, Git, can understand this, at least in most cases. The exact details of how Git shows it is not important here.)

Anyway, now you run git checkout mainline; git merge --squash feature1 git checkout mainline; git merge --squash feature1 . This makes one new commit, which is a squash copy of the three or even many commits that are on feature1 . I will stop drawing old abandoned ones and will call a new squash-commit S for Squash:

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

"Delete security" is completely determined by the commit history

When you ask Git to remove feature1 , it performs a security check: " feature1 merged into mainline ?" This "merges into" test is based solely on graph connectivity. The name mainline indicates a commit S ; commit S points to the first boring o commit, which returns to a more boring o commit. Commit C' , the tip of feature1 not available from S : we are not allowed to move to the right, only to the left.

Compare this to creating a โ€œnormalโ€ merge M :

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

Using the same test: "is feature1 tip feature1 achievable from mainline tip fixation?" - the answer is now yes, because commit M has a link down and left to commit C' . (Commit C' is in Git's internal expression, the second parent of the merge commit is M )

Since squash mergers do not actually merge, but there is no connection from S back to C' .

Again, Git doesn't even try to see if S is the same as A' , B' or C' However, if that were the case, then it would say "not the same thing" because S matches the sum of all three commits. The only way to get S to match crushed commits is to have only one such commit (and in this case there is no need to crush first).

+34
source

Source: https://habr.com/ru/post/1014569/


All Articles