summaryrefslogtreecommitdiff
path: root/builtin-branch.c
diff options
context:
space:
mode:
Diffstat (limited to 'builtin-branch.c')
-rw-r--r--builtin-branch.c81
1 files changed, 66 insertions, 15 deletions
diff --git a/builtin-branch.c b/builtin-branch.c
index 1a03d5f356..a28a13986d 100644
--- a/builtin-branch.c
+++ b/builtin-branch.c
@@ -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;
}
@@ -561,6 +608,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT__VERBOSE(&verbose),
OPT_SET_INT('t', "track", &track, "set up tracking mode (see git-pull(1))",
BRANCH_TRACK_EXPLICIT),
+ OPT_SET_INT( 0, "set-upstream", &track, "change upstream info",
+ BRANCH_TRACK_OVERRIDE),
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
REF_REMOTE_BRANCH),
@@ -586,7 +635,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
OPT_BIT('m', NULL, &rename, "move/rename a branch and its reflog", 1),
OPT_BIT('M', NULL, &rename, "move/rename a branch, even if target exists", 2),
OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
- OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
+ OPT_BOOLEAN('f', "force", &force_create, "force creation (when already exists)"),
{
OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref,
"commit", "print only not merged branches",
@@ -635,10 +684,12 @@ int cmd_branch(int argc, const char **argv, const char *prefix)
rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
rename_branch(argv[0], argv[1], rename > 1);
- else if (argc <= 2)
+ else if (argc <= 2) {
+ if (kinds != REF_LOCAL_BRANCH)
+ die("-a and -r options to 'git branch' do not make sense with a branch name");
create_branch(head, argv[0], (argc == 2) ? argv[1] : head,
force_create, reflog, track);
- else
+ } else
usage_with_options(builtin_branch_usage, options);
return 0;