if (!force &&
!in_merge_bases(rev, &head_rev, 1)) {
- error("The branch '%s' is not a strict subset of "
+ error("The branch '%s' is not an ancestor of "
"your current HEAD.\n"
"If you are sure you want to delete it, "
"run 'git branch -D %s'.", argv[i], argv[i]);
struct ref_list {
int index, alloc, maxwidth;
struct ref_item *list;
+ struct commit_list *with_commit;
int kinds;
};
+static int has_commit(const unsigned char *sha1, struct commit_list *with_commit)
+{
+ struct commit *commit;
+
+ if (!with_commit)
+ return 1;
+ commit = lookup_commit_reference_gently(sha1, 1);
+ if (!commit)
+ return 0;
+ while (with_commit) {
+ struct commit *other;
+
+ other = with_commit->item;
+ with_commit = with_commit->next;
+ if (in_merge_bases(other, &commit, 1))
+ return 1;
+ }
+ return 0;
+}
+
static int append_ref(const char *refname, const unsigned char *sha1, int flags, void *cb_data)
{
struct ref_list *ref_list = (struct ref_list*)(cb_data);
refname += 10;
}
+ /* Filter with with_commit if specified */
+ if (!has_commit(sha1, ref_list->with_commit))
+ return 0;
+
/* Don't add types the caller doesn't want */
if ((kind & ref_list->kinds) == 0)
return 0;
commit = lookup_commit(item->sha1);
if (commit && !parse_commit(commit)) {
pretty_print_commit(CMIT_FMT_ONELINE, commit,
- &subject, 0, NULL, NULL, 0);
+ &subject, 0, NULL, NULL, 0, 0);
sub = subject.buf;
}
printf("%c %s%-*s%s %s %s\n", c, branch_get_color(color),
}
}
-static void print_ref_list(int kinds, int detached, int verbose, int abbrev)
+static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit)
{
int i;
struct ref_list ref_list;
memset(&ref_list, 0, sizeof(ref_list));
ref_list.kinds = kinds;
+ ref_list.with_commit = with_commit;
for_each_ref(append_ref, &ref_list);
qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
detached = (detached && (kinds & REF_LOCAL_BRANCH));
- if (detached) {
+ if (detached && has_commit(head_sha1, with_commit)) {
struct ref_item item;
item.name = xstrdup("(no branch)");
item.kind = REF_LOCAL_BRANCH;
die("Branch is renamed, but update of config-file failed");
}
+static int opt_parse_with_commit(const struct option *opt, const char *arg, int unset)
+{
+ unsigned char sha1[20];
+ struct commit *commit;
+
+ if (!arg)
+ return -1;
+ if (get_sha1(arg, sha1))
+ die("malformed object name %s", arg);
+ commit = lookup_commit_reference(sha1);
+ if (!commit)
+ die("no such commit %s", arg);
+ commit_list_insert(commit, opt->value);
+ return 0;
+}
+
int cmd_branch(int argc, const char **argv, const char *prefix)
{
- int delete = 0, force_delete = 0, force_create = 0;
- int rename = 0, force_rename = 0;
+ int delete = 0, rename = 0, force_create = 0;
int verbose = 0, abbrev = DEFAULT_ABBREV, detached = 0;
int reflog = 0, track;
- int kinds = REF_LOCAL_BRANCH, kind_remote = 0, kind_any = 0;
+ int kinds = REF_LOCAL_BRANCH;
+ struct commit_list *with_commit = NULL;
struct option options[] = {
OPT_GROUP("Generic options"),
OPT__VERBOSE(&verbose),
OPT_BOOLEAN( 0 , "track", &track, "set up tracking mode (see git-pull(1))"),
OPT_BOOLEAN( 0 , "color", &branch_use_color, "use colored output"),
- OPT_BOOLEAN('r', NULL, &kind_remote, "act on remote-tracking branches"),
+ OPT_SET_INT('r', NULL, &kinds, "act on remote-tracking branches",
+ REF_REMOTE_BRANCH),
+ OPT_CALLBACK(0, "contains", &with_commit, "commit",
+ "print only branches that contain the commit",
+ opt_parse_with_commit),
+ {
+ OPTION_CALLBACK, 0, "with", &with_commit, "commit",
+ "print only branches that contain the commit",
+ PARSE_OPT_HIDDEN, opt_parse_with_commit,
+ },
OPT__ABBREV(&abbrev),
OPT_GROUP("Specific git-branch actions:"),
- OPT_BOOLEAN('a', NULL, &kind_any, "list both remote-tracking and local branches"),
- OPT_BOOLEAN('d', NULL, &delete, "delete fully merged branch"),
- OPT_BOOLEAN('D', NULL, &force_delete, "delete branch (even if not merged)"),
- OPT_BOOLEAN('l', NULL, &reflog, "create the branch's reflog"),
- OPT_BOOLEAN('f', NULL, &force_create, "force creation (when already exists)"),
- OPT_BOOLEAN('m', NULL, &rename, "move/rename a branch and its reflog"),
- OPT_BOOLEAN('M', NULL, &force_rename, "move/rename a branch, even if target exists"),
+ OPT_SET_INT('a', NULL, &kinds, "list both remote-tracking and local branches",
+ REF_REMOTE_BRANCH | REF_LOCAL_BRANCH),
+ OPT_BIT('d', NULL, &delete, "delete fully merged branch", 1),
+ OPT_BIT('D', NULL, &delete, "delete branch (even if not merged)", 2),
+ 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_END(),
};
git_config(git_branch_config);
track = branch_track;
argc = parse_options(argc, argv, options, builtin_branch_usage, 0);
-
- delete |= force_delete;
- rename |= force_rename;
- if (kind_remote)
- kinds = REF_REMOTE_BRANCH;
- if (kind_any)
- kinds = REF_REMOTE_BRANCH | REF_LOCAL_BRANCH;
- if (abbrev && abbrev < MINIMUM_ABBREV)
- abbrev = MINIMUM_ABBREV;
- else if (abbrev > 40)
- abbrev = 40;
-
- if ((delete && rename) || (delete && force_create) ||
- (rename && force_create))
+ if (!!delete + !!rename + !!force_create > 1)
usage_with_options(builtin_branch_usage, options);
head = resolve_ref("HEAD", head_sha1, 0, NULL);
}
if (delete)
- return delete_branches(argc, argv, force_delete, kinds);
+ return delete_branches(argc, argv, delete > 1, kinds);
else if (argc == 0)
- print_ref_list(kinds, detached, verbose, abbrev);
+ print_ref_list(kinds, detached, verbose, abbrev, with_commit);
else if (rename && (argc == 1))
- rename_branch(head, argv[0], force_rename);
+ rename_branch(head, argv[0], rename > 1);
else if (rename && (argc == 2))
- rename_branch(argv[0], argv[1], force_rename);
+ rename_branch(argv[0], argv[1], rename > 1);
else if (argc <= 2)
create_branch(argv[0], (argc == 2) ? argv[1] : head,
force_create, reflog, track);