Ingo Molnar <mingo@xxxxxxx> writes: > * SZEDER Gábor <szeder@xxxxxxxxxx> wrote: > >> you cound use 'git branch --no-merged' to list only those branches >> that have not been merged into your current HEAD. > > hm, it's very slow: Yeah, --no-merged and --merged were done in a quite naïve way. The patch needs to be cleaned up by splitting it into multiple steps: (1) discard everything outside refs/heads and refs/remotes in append_ref(); why do we even have code to deal with refs/tags to begin with??? (2) change ref_item->sha1 to ref_item->commit (and make has_commit() take struct commit); (3) teach merge_filter code not to do has_commit() for each ref, but use revision traversal machinery to compute everything in parallel and in one traversal. but other than that, this seems to pass the tests, and is obviously correct ;-) With an artificial repository that has "master" and 1000 test-$i branches where they were created by "git branch test-$i master~$i": (with patch) $ /usr/bin/time git-branch --no-merged master >/dev/null 0.12user 0.02system 0:00.15elapsed 99%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+1588minor)pagefaults 0swaps $ /usr/bin/time git-branch --no-merged test-200 >/dev/null 0.15user 0.03system 0:00.18elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+1711minor)pagefaults 0swaps (without patch) $ /usr/bin/time git-branch --no-merged master >/dev/null 0.69user 0.03system 0:00.72elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+2229minor)pagefaults 0swaps $ /usr/bin/time git-branch --no-merged test-200 >/dev/null 0.58user 0.03system 0:00.61elapsed 100%CPU (0avgtext+0avgdata 0maxresident)k 0inputs+0outputs (0major+2248minor)pagefaults 0swaps --- builtin-branch.c | 68 ++++++++++++++++++++++++++++++++--------------------- 1 files changed, 41 insertions(+), 27 deletions(-) diff --git a/builtin-branch.c b/builtin-branch.c index b885bd1..788e70a 100644 --- a/builtin-branch.c +++ b/builtin-branch.c @@ -13,6 +13,8 @@ #include "remote.h" #include "parse-options.h" #include "branch.h" +#include "diff.h" +#include "revision.h" static const char * const builtin_branch_usage[] = { "git branch [options] [-r | -a] [--merged | --no-merged]", @@ -181,25 +183,21 @@ static int delete_branches(int argc, const char **argv, int force, int kinds) struct ref_item { char *name; unsigned int kind; - unsigned char sha1[20]; + struct commit *commit; }; struct ref_list { + struct rev_info revs; 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) +static int has_commit(struct commit *commit, 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; @@ -215,6 +213,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, { struct ref_list *ref_list = (struct ref_list*)(cb_data); struct ref_item *newitem; + struct commit *commit; int kind = REF_UNKNOWN_TYPE; int len; static struct commit_list branch; @@ -226,13 +225,15 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, } else if (!prefixcmp(refname, "refs/remotes/")) { kind = REF_REMOTE_BRANCH; refname += 13; - } else if (!prefixcmp(refname, "refs/tags/")) { - kind = REF_TAG; - refname += 10; - } + } else + return 0; + + commit = lookup_commit_reference_gently(sha1, 1); + if (!commit) + return error("branch '%s' does not point at a commit", refname); /* Filter with with_commit if specified */ - if (!has_commit(sha1, ref_list->with_commit)) + if (!has_commit(commit, ref_list->with_commit)) return 0; /* Don't add types the caller doesn't want */ @@ -243,30 +244,25 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, branch.item = lookup_commit_reference_gently(sha1, 1); if (!branch.item) die("Unable to lookup tip of branch %s", refname); - if (merge_filter == SHOW_NOT_MERGED && - has_commit(merge_filter_ref, &branch)) - return 0; - if (merge_filter == SHOW_MERGED && - !has_commit(merge_filter_ref, &branch)) - return 0; + add_pending_object(&ref_list->revs, + (struct object *)branch.item, refname); } /* Resize buffer */ if (ref_list->index >= ref_list->alloc) { ref_list->alloc = alloc_nr(ref_list->alloc); ref_list->list = xrealloc(ref_list->list, - ref_list->alloc * sizeof(struct ref_item)); + ref_list->alloc * sizeof(struct ref_item)); } /* Record the new item */ newitem = &(ref_list->list[ref_list->index++]); newitem->name = xstrdup(refname); newitem->kind = kind; - hashcpy(newitem->sha1, sha1); + newitem->commit = commit; len = strlen(newitem->name); if (len > ref_list->maxwidth) ref_list->maxwidth = len; - return 0; } @@ -309,7 +305,13 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, { char c; int color; - struct commit *commit; + struct commit *commit = item->commit; + + if (merge_filter != NO_FILTER) { + int is_merged = !!(item->commit->object.flags & UNINTERESTING); + if (is_merged != (merge_filter == SHOW_MERGED)) + return; + } switch (item->kind) { case REF_LOCAL_BRANCH: @@ -337,7 +339,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, strbuf_init(&subject, 0); stat[0] = '\0'; - commit = lookup_commit(item->sha1); + commit = item->commit; if (commit && !parse_commit(commit)) { pretty_print_commit(CMIT_FMT_ONELINE, commit, &subject, 0, NULL, NULL, 0, 0); @@ -350,7 +352,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color), maxwidth, item->name, branch_get_color(COLOR_BRANCH_RESET), - find_unique_abbrev(item->sha1, abbrev), + find_unique_abbrev(item->commit->object.sha1, abbrev), stat, sub); strbuf_release(&subject); } else { @@ -363,22 +365,34 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str { int i; struct ref_list ref_list; + struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); memset(&ref_list, 0, sizeof(ref_list)); ref_list.kinds = kinds; ref_list.with_commit = with_commit; + if (merge_filter != NO_FILTER) + init_revisions(&ref_list.revs, NULL); for_each_ref(append_ref, &ref_list); + if (merge_filter != NO_FILTER) { + struct commit *filter; + filter = lookup_commit_reference_gently(merge_filter_ref, 0); + filter->object.flags |= UNINTERESTING; + add_pending_object(&ref_list.revs, + (struct object *) filter, ""); + ref_list.revs.limited = 1; + prepare_revision_walk(&ref_list.revs); + } qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached && has_commit(head_sha1, with_commit)) { + if (detached && head_commit && has_commit(head_commit, with_commit)) { struct ref_item item; item.name = xstrdup("(no branch)"); item.kind = REF_LOCAL_BRANCH; - hashcpy(item.sha1, head_sha1); + item.commit = head_commit; if (strlen(item.name) > ref_list.maxwidth) - ref_list.maxwidth = strlen(item.name); + ref_list.maxwidth = strlen(item.name); print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1); free(item.name); } -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html