branch -d: base the "already-merged" safety on the branch it merges with
[gitweb.git] / builtin-branch.c
index 9f57992062314a3d8d168b5cfb6b3c6737605033..d2a35fe00d97bd95dc7a18740599e1165ecf46f1 100644 (file)
@@ -65,7 +65,7 @@ static int parse_branch_color_slot(const char *var, int ofs)
                return BRANCH_COLOR_LOCAL;
        if (!strcasecmp(var+ofs, "current"))
                return BRANCH_COLOR_CURRENT;
-       die("bad config variable '%s'", var);
+       return -1;
 }
 
 static int git_branch_config(const char *var, const char *value, void *cb)
@@ -76,6 +76,8 @@ static int git_branch_config(const char *var, const char *value, void *cb)
        }
        if (!prefixcmp(var, "color.branch.")) {
                int slot = parse_branch_color_slot(var, 13);
+               if (slot < 0)
+                       return 0;
                if (!value)
                        return config_error_nonbool(var);
                color_parse(value, var, branch_colors[slot]);
@@ -91,9 +93,60 @@ static const char *branch_get_color(enum color_branch ix)
        return "";
 }
 
+static int branch_merged(int kind, const char *name,
+                        struct commit *rev, struct commit *head_rev)
+{
+       /*
+        * This checks whether the merge bases of branch and HEAD (or
+        * the other branch this branch builds upon) contains the
+        * branch, which means that the branch has already been merged
+        * safely to HEAD (or the other branch).
+        */
+       struct commit *reference_rev = NULL;
+       const char *reference_name = NULL;
+       int merged;
+
+       if (kind == REF_LOCAL_BRANCH) {
+               struct branch *branch = branch_get(name);
+               unsigned char sha1[20];
+
+               if (branch &&
+                   branch->merge &&
+                   branch->merge[0] &&
+                   branch->merge[0]->dst &&
+                   (reference_name =
+                    resolve_ref(branch->merge[0]->dst, sha1, 1, NULL)) != NULL)
+                       reference_rev = lookup_commit_reference(sha1);
+       }
+       if (!reference_rev)
+               reference_rev = head_rev;
+
+       merged = in_merge_bases(rev, &reference_rev, 1);
+
+       /*
+        * After the safety valve is fully redefined to "check with
+        * upstream, if any, otherwise with HEAD", we should just
+        * return the result of the in_merge_bases() above without
+        * any of the following code, but during the transition period,
+        * a gentle reminder is in order.
+        */
+       if ((head_rev != reference_rev) &&
+           in_merge_bases(rev, &head_rev, 1) != merged) {
+               if (merged)
+                       warning("deleting branch '%s' that has been merged to\n"
+                               "         '%s', but it is not yet merged to HEAD.",
+                               name, reference_name);
+               else
+                       warning("not deleting branch '%s' that is not yet merged to\n"
+                               "         '%s', even though it is merged to HEAD.",
+                               name, reference_name);
+       }
+       return merged;
+}
+
 static int delete_branches(int argc, const char **argv, int force, int kinds)
 {
-       struct commit *rev, *head_rev = head_rev;
+       struct commit *rev, *head_rev = NULL;
        unsigned char sha1[20];
        char *name = NULL;
        const char *fmt, *remote;
@@ -146,15 +199,8 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
                        continue;
                }
 
-               /* This checks whether the merge bases of branch and
-                * HEAD contains branch -- which means that the HEAD
-                * contains everything in both.
-                */
-
-               if (!force &&
-                   !in_merge_bases(rev, &head_rev, 1)) {
-                       error("The branch '%s' is not an ancestor of "
-                             "your current HEAD.\n"
+               if (!force && !branch_merged(kinds, bname.buf, rev, head_rev)) {
+                       error("The branch '%s' is not fully merged.\n"
                              "If you are sure you want to delete it, "
                              "run 'git branch -D %s'.", bname.buf, bname.buf);
                        ret = 1;
@@ -387,8 +433,9 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
 
                commit = item->commit;
                if (commit && !parse_commit(commit)) {
+                       struct pretty_print_context ctx = {0};
                        pretty_print_commit(CMIT_FMT_ONELINE, commit,
-                                           &subject, 0, NULL, NULL, 0, 0);
+                                           &subject, &ctx);
                        sub = subject.buf;
                }