There are several different complex fragments here.
The key question for git branch -d (delete without coercion 1 ) does not mean that the branch is merged? Because this question literally does not answer. Instead, the question is, is the branch merging into <fill something here>?
This test was modified in Git version 1.7 (which means everyone should have one today, but check their version of Git), in commit 99c419c91554e9f60940228006b7d39d42704da7 Junio ââC Hamano:
branch -d: base "already merged" security on the branch with which it merges
When a branch is marked to be combined with another ref (for example, local 'next', which merges and returns to the original "next", with "branch.next.merge" set to "refs / heads / next"), it makes no sense to base a "branch -d ", a security whose goal is not to lose commits that are not merged with other branches on the current branch. It is much more reasonable to check whether it merges with another branch with which it merges.
Therefore, there are two, and sometimes three, if you look at the above commit questions that you (or Git) should answer to see if -d allowed:
If the answer to question 2 is yes (i.e., T ⤠U), deletion is allowed.
What if there is no upstream? Itâs good that where âchanged in 1.7â comes: the original test was not T ⤠U, but rather T ⤠HEAD. This test is still present. Right now, if there is no upstream, the HEAD test is used instead of the up test. Meanwhile, for the âtransition period," when everyone is adapting to the newfangled behavior of Git 1.7, you can also get a warning or additional explanation when there is an upstream. This warning persists today, in Git 2.10 (almost 7 years later: this is a very long transition period!):
if ((head_rev != reference_rev) && in_merge_bases(rev, head_rev) != merged) { if (merged) warning("deleting branch ... not yet merged to HEAD."); else warning("not deleting branch ... even though it is merged to HEAD."); }
(here I cut off a part of the code for display purposes): basically, if HEAD resolves some other commit than the commit we used in the test T â¤, and we would get a different result for T ⤠HEAD than we got for T ⤠U, add an additional warning. (Note that the first part of the test is redundant: if we compare with HEAD due to compatibility with up to 1.7 and missing upstream, we will get the same result if we compare again with HEAD. All we really need is an in_merge_bases test .)
How do you know what happens upstream? Well, there really is an easy command line way:
$ git rev-parse --abbrev-ref master@ {u} origin/master $ git rev-parse --symbolic-full-name master@ {u} refs/remotes/origin/master
The --abbrev-ref option gives you Git a typical shortened version, and --symbolic-full-name gives the full name of the link. Both do not work if they run on a branch without setting upstream. Of course, you can also use git branch -vv to view shortened upstream streams (for all branches).
How do you test this whole thing T ⤠U? The git merge-base --is-ancestor does this for shell scripts, therefore:
$ git merge-base --is-ancestor master origin/master && echo yes || echo no
will tell you whether master ancestor of origin/master (and for this purpose âpoints to the same commitâ it is considered that âis an ancestorâ, i.e. this is the same ⤠test).
Whenever this transition period finally ends, the âuse HEADâ test may disappear completely or may be used for branches that do not have an upstream set. In any case, however, the question is always "is the branch to be deleted combined with ____? (Fill in the gap)" and you should see what fills the gap.
The end in the upstream that corresponds to the commit on the branch that you want to delete must be (or at least have in its history) the same commit, by fixing the hash identifier for which the test merges into so that to succeed. If feature/X was merged into origin/develop using the so-called "squash merge" (which is not a merge, although this was done by merging 2 ), this commit identifier will not match, and the "merge into" test will always fail.
1 By the way, with Git 2.3 you can now add --force to git branch -d instead of using git branch -d , although of course -d still works.
2 The difference here is in the âmergerâ -merge as a noun, which means âcommitting, which is the fixation of the mergerâ (which uses the merger as an adjective), and the âmergeâ -merge as a verb, which means the action combining some sets of changes. The git merge command can:
- performs fast rewind that does not unite-as-a-verb and does not merge as-noun, so there is no merger at all; or
- Perform a real merge that combines (verb) some changes to create a merge (noun); or
- do a "squash merge" that combines (verb) but creates a non-merge (which for some inexplicable reason needs to be done manually).
âSquash mergingâ occurs only if you request it, while âaccelerated unauthorized switchingâ occurs only if it is possible, and you do not prohibit it.