@matheusbsbs / matheus-bagliano / commits / 68067ca1ef
Fork 0

builtin-branch.c: optimize --merged and --no-merged

"git branch --no-merged $commit" used to compute the merge base between the tip of each and every branch with the named $commit, but this was wasteful when you have many branches. Inside append_ref() we literally ran has_commit() between the tip of the branch and the merge_filter_ref. Instead, we can let the revision machinery traverse the history as if we are running: $ git rev-list --branches --not $commit by queueing the tips of branches we encounter as positive refs (this mimicks the "--branches" option in the above command line) and then appending the merge_filter_ref commit as a negative one, and finally calling prepare_revision_walk() to limit the list.. After the traversal is done, branch tips that are reachable from $commit are painted UNINTERESTING; they are already fully contained in $commit (i.e. --merged). Tips that are not painted UNINTERESTING still have commits that are not reachable from $commit, thus "--no-merged" will show them. 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 Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Jul 23, 2008 at 15:13 UTC 68067ca1efcbb0408b931e08654a5e32de975ced
1 file changed +38 -21
builtin-branch.c
+38 -21
@@ -13,6 +13,8 @@
13 #include "remote.h"
14 #include "parse-options.h"
15 #include "branch.h"
16 +#include "diff.h"
17 +#include "revision.h"
18
19 static const char * const builtin_branch_usage[] = {
20 "git branch [options] [-r | -a] [--merged | --no-merged]",
@@ -179,25 +181,21 @@ static int delete_branches(int argc, const char **argv, int force, int kinds)
181 struct ref_item {
182 char *name;
183 unsigned int kind;
182 - unsigned char sha1[20];
184 + struct commit *commit;
185 };
186
187 struct ref_list {
188 + struct rev_info revs;
189 int index, alloc, maxwidth;
190 struct ref_item *list;
191 struct commit_list *with_commit;
192 int kinds;
193 };
194
192 -static int has_commit(const unsigned char *sha1, struct commit_list *with_commit)
195 +static int has_commit(struct commit *commit, struct commit_list *with_commit)
196 {
194 - struct commit *commit;
195 -
197 if (!with_commit)
198 return 1;
198 - commit = lookup_commit_reference_gently(sha1, 1);
199 - if (!commit)
200 - return 0;
199 while (with_commit) {
200 struct commit *other;
201
@@ -213,6 +211,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
211 {
212 struct ref_list *ref_list = (struct ref_list*)(cb_data);
213 struct ref_item *newitem;
214 + struct commit *commit;
215 int kind;
216 int len;
217 static struct commit_list branch;
@@ -227,8 +226,12 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
226 } else
227 return 0;
228
229 + commit = lookup_commit_reference_gently(sha1, 1);
230 + if (!commit)
231 + return error("branch '%s' does not point at a commit", refname);
232 +
233 /* Filter with with_commit if specified */
231 - if (!has_commit(sha1, ref_list->with_commit))
234 + if (!has_commit(commit, ref_list->with_commit))
235 return 0;
236
237 /* Don't add types the caller doesn't want */
@@ -239,12 +242,8 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
242 branch.item = lookup_commit_reference_gently(sha1, 1);
243 if (!branch.item)
244 die("Unable to lookup tip of branch %s", refname);
242 - if (merge_filter == SHOW_NOT_MERGED &&
243 - has_commit(merge_filter_ref, &branch))
244 - return 0;
245 - if (merge_filter == SHOW_MERGED &&
246 - !has_commit(merge_filter_ref, &branch))
247 - return 0;
245 + add_pending_object(&ref_list->revs,
246 + (struct object *)branch.item, refname);
247 }
248
249 /* Resize buffer */
@@ -258,7 +257,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags,
257 newitem = &(ref_list->list[ref_list->index++]);
258 newitem->name = xstrdup(refname);
259 newitem->kind = kind;
261 - hashcpy(newitem->sha1, sha1);
260 + newitem->commit = commit;
261 len = strlen(newitem->name);
262 if (len > ref_list->maxwidth)
263 ref_list->maxwidth = len;
@@ -305,7 +304,13 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
304 {
305 char c;
306 int color;
308 - struct commit *commit;
307 + struct commit *commit = item->commit;
308 +
309 + if (merge_filter != NO_FILTER) {
310 + int is_merged = !!(item->commit->object.flags & UNINTERESTING);
311 + if (is_merged != (merge_filter == SHOW_MERGED))
312 + return;
313 + }
314
315 switch (item->kind) {
316 case REF_LOCAL_BRANCH:
@@ -333,7 +338,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
338 strbuf_init(&subject, 0);
339 stat[0] = '\0';
340
336 - commit = lookup_commit(item->sha1);
341 + commit = item->commit;
342 if (commit && !parse_commit(commit)) {
343 pretty_print_commit(CMIT_FMT_ONELINE, commit,
344 &subject, 0, NULL, NULL, 0, 0);
@@ -346,7 +351,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose,
351 printf("%c %s%-*s%s %s %s%s\n", c, branch_get_color(color),
352 maxwidth, item->name,
353 branch_get_color(COLOR_BRANCH_RESET),
349 - find_unique_abbrev(item->sha1, abbrev),
354 + find_unique_abbrev(item->commit->object.sha1, abbrev),
355 stat, sub);
356 strbuf_release(&subject);
357 } else {
@@ -359,22 +364,34 @@ static void print_ref_list(int kinds, int detached, int verbose, int abbrev, str
364 {
365 int i;
366 struct ref_list ref_list;
367 + struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1);
368
369 memset(&ref_list, 0, sizeof(ref_list));
370 ref_list.kinds = kinds;
371 ref_list.with_commit = with_commit;
372 + if (merge_filter != NO_FILTER)
373 + init_revisions(&ref_list.revs, NULL);
374 for_each_ref(append_ref, &ref_list);
375 + if (merge_filter != NO_FILTER) {
376 + struct commit *filter;
377 + filter = lookup_commit_reference_gently(merge_filter_ref, 0);
378 + filter->object.flags |= UNINTERESTING;
379 + add_pending_object(&ref_list.revs,
380 + (struct object *) filter, "");
381 + ref_list.revs.limited = 1;
382 + prepare_revision_walk(&ref_list.revs);
383 + }
384
385 qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp);
386
387 detached = (detached && (kinds & REF_LOCAL_BRANCH));
371 - if (detached && has_commit(head_sha1, with_commit)) {
388 + if (detached && head_commit && has_commit(head_commit, with_commit)) {
389 struct ref_item item;
390 item.name = xstrdup("(no branch)");
391 item.kind = REF_LOCAL_BRANCH;
375 - hashcpy(item.sha1, head_sha1);
392 + item.commit = head_commit;
393 if (strlen(item.name) > ref_list.maxwidth)
377 - ref_list.maxwidth = strlen(item.name);
394 + ref_list.maxwidth = strlen(item.name);
395 print_ref_item(&item, ref_list.maxwidth, verbose, abbrev, 1);
396 free(item.name);
397 }