From 1051e40dba16bd8e490c41ce926c8c36b913de72 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:06 +0530 Subject: branch: refactor width computation Remove unnecessary variables from ref_list and ref_item which were used for width computation. This is to make ref_item similar to ref-filter's ref_array_item. This will ensure a smooth port of branch.c to use ref-filter APIs in further patches. Previously the maxwidth was computed when inserting the refs into the ref_list. Now, we obtain the entire ref_list and then compute maxwidth. Based-on-patch-by: Jeff King Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index 4fc8beb..28a10d6 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -282,14 +282,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, struct ref_item { char *name; char *dest; - unsigned int kind, width; + unsigned int kind; struct commit *commit; int ignore; }; struct ref_list { struct rev_info revs; - int index, alloc, maxwidth, verbose, abbrev; + int index, alloc, verbose, abbrev; struct ref_item *list; struct commit_list *with_commit; int kinds; @@ -386,15 +386,8 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag newitem->name = xstrdup(refname); newitem->kind = kind; newitem->commit = commit; - newitem->width = utf8_strwidth(refname); newitem->dest = resolve_symref(orig_refname, prefix); newitem->ignore = 0; - /* adjust for "remotes/" */ - if (newitem->kind == REF_REMOTE_BRANCH && - ref_list->kinds != REF_REMOTE_BRANCH) - newitem->width += 8; - if (newitem->width > ref_list->maxwidth) - ref_list->maxwidth = newitem->width; return 0; } @@ -505,11 +498,12 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, } static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, - int abbrev, int current, char *prefix) + int abbrev, int current, const char *remote_prefix) { char c; int color; struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; + const char *prefix = ""; if (item->ignore) return; @@ -520,6 +514,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, break; case REF_REMOTE_BRANCH: color = BRANCH_COLOR_REMOTE; + prefix = remote_prefix; break; default: color = BRANCH_COLOR_PLAIN; @@ -557,16 +552,22 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, strbuf_release(&out); } -static int calc_maxwidth(struct ref_list *refs) +static int calc_maxwidth(struct ref_list *refs, int remote_bonus) { - int i, w = 0; + int i, max = 0; for (i = 0; i < refs->index; i++) { - if (refs->list[i].ignore) + struct ref_item *it = &refs->list[i]; + int w; + + if (it->ignore) continue; - if (refs->list[i].width > w) - w = refs->list[i].width; + w = utf8_strwidth(it->name); + if (it->kind == REF_REMOTE_BRANCH) + w += remote_bonus; + if (w > max) + max = w; } - return w; + return max; } static char *get_head_description(void) @@ -600,21 +601,18 @@ static char *get_head_description(void) return strbuf_detach(&desc, NULL); } -static void show_detached(struct ref_list *ref_list) +static void show_detached(struct ref_list *ref_list, int maxwidth) { struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) { struct ref_item item; item.name = get_head_description(); - item.width = utf8_strwidth(item.name); item.kind = REF_LOCAL_BRANCH; item.dest = NULL; item.commit = head_commit; item.ignore = 0; - if (item.width > ref_list->maxwidth) - ref_list->maxwidth = item.width; - print_ref_item(&item, ref_list->maxwidth, ref_list->verbose, ref_list->abbrev, 1, ""); + print_ref_item(&item, maxwidth, ref_list->verbose, ref_list->abbrev, 1, ""); free(item.name); } } @@ -624,6 +622,16 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru int i; struct append_ref_cb cb; struct ref_list ref_list; + int maxwidth = 0; + const char *remote_prefix = ""; + + /* + * If we are listing more than just remote branches, + * then remote branches will have a "remotes/" prefix. + * We need to account for this in the width. + */ + if (kinds != REF_REMOTE_BRANCH) + remote_prefix = "remotes/"; memset(&ref_list, 0, sizeof(ref_list)); ref_list.kinds = kinds; @@ -667,26 +675,22 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru clear_commit_marks(item->commit, ALL_REV_FLAGS); } clear_commit_marks(filter, ALL_REV_FLAGS); - - if (verbose) - ref_list.maxwidth = calc_maxwidth(&ref_list); } + if (verbose) + maxwidth = calc_maxwidth(&ref_list, strlen(remote_prefix)); qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); detached = (detached && (kinds & REF_LOCAL_BRANCH)); if (detached && match_patterns(pattern, "HEAD")) - show_detached(&ref_list); + show_detached(&ref_list, maxwidth); for (i = 0; i < ref_list.index; i++) { int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); - char *prefix = (kinds != REF_REMOTE_BRANCH && - ref_list.list[i].kind == REF_REMOTE_BRANCH) - ? "remotes/" : ""; - print_ref_item(&ref_list.list[i], ref_list.maxwidth, verbose, - abbrev, current, prefix); + print_ref_item(&ref_list.list[i], maxwidth, verbose, + abbrev, current, remote_prefix); } free_ref_list(&ref_list); -- cgit v0.10.2-6-g49f6 From 2dad24a5c39bb26a815c35c5db7a35499bad48e1 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:07 +0530 Subject: branch: bump get_head_description() to the top This is a preperatory patch for 'roll show_detached HEAD into regular ref_list'. This patch moves get_head_description() to the top so that it can be used in print_ref_item(). Based-on-patch-by: Jeff King Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index 28a10d6..193296a 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -497,6 +497,37 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, strbuf_release(&subject); } +static char *get_head_description(void) +{ + struct strbuf desc = STRBUF_INIT; + struct wt_status_state state; + memset(&state, 0, sizeof(state)); + wt_status_get_state(&state, 1); + if (state.rebase_in_progress || + state.rebase_interactive_in_progress) + strbuf_addf(&desc, _("(no branch, rebasing %s)"), + state.branch); + else if (state.bisect_in_progress) + strbuf_addf(&desc, _("(no branch, bisect started on %s)"), + state.branch); + else if (state.detached_from) { + /* TRANSLATORS: make sure these match _("HEAD detached at ") + and _("HEAD detached from ") in wt-status.c */ + if (state.detached_at) + strbuf_addf(&desc, _("(HEAD detached at %s)"), + state.detached_from); + else + strbuf_addf(&desc, _("(HEAD detached from %s)"), + state.detached_from); + } + else + strbuf_addstr(&desc, _("(no branch)")); + free(state.branch); + free(state.onto); + free(state.detached_from); + return strbuf_detach(&desc, NULL); +} + static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int abbrev, int current, const char *remote_prefix) { @@ -570,37 +601,6 @@ static int calc_maxwidth(struct ref_list *refs, int remote_bonus) return max; } -static char *get_head_description(void) -{ - struct strbuf desc = STRBUF_INIT; - struct wt_status_state state; - memset(&state, 0, sizeof(state)); - wt_status_get_state(&state, 1); - if (state.rebase_in_progress || - state.rebase_interactive_in_progress) - strbuf_addf(&desc, _("(no branch, rebasing %s)"), - state.branch); - else if (state.bisect_in_progress) - strbuf_addf(&desc, _("(no branch, bisect started on %s)"), - state.branch); - else if (state.detached_from) { - /* TRANSLATORS: make sure these match _("HEAD detached at ") - and _("HEAD detached from ") in wt-status.c */ - if (state.detached_at) - strbuf_addf(&desc, _("(HEAD detached at %s)"), - state.detached_from); - else - strbuf_addf(&desc, _("(HEAD detached from %s)"), - state.detached_from); - } - else - strbuf_addstr(&desc, _("(no branch)")); - free(state.branch); - free(state.onto); - free(state.detached_from); - return strbuf_detach(&desc, NULL); -} - static void show_detached(struct ref_list *ref_list, int maxwidth) { struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); -- cgit v0.10.2-6-g49f6 From 23e714df91cb8d824f6fd0594cd80c8a7dd9b751 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:08 +0530 Subject: branch: roll show_detached HEAD into regular ref_list Remove show_detached() and make detached HEAD to be rolled into regular ref_list by adding REF_DETACHED_HEAD as a kind of branch and supporting the same in append_ref(). This eliminates the need for an extra function and helps in easier porting of branch.c to use ref-filter APIs. Before show_detached() used to check if the HEAD branch satisfies the '--contains' option, now that is taken care by append_ref(). Based-on-patch-by: Jeff King Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index 193296a..a2a35f4 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -28,8 +28,9 @@ static const char * const builtin_branch_usage[] = { NULL }; -#define REF_LOCAL_BRANCH 0x01 -#define REF_REMOTE_BRANCH 0x02 +#define REF_DETACHED_HEAD 0x01 +#define REF_LOCAL_BRANCH 0x02 +#define REF_REMOTE_BRANCH 0x04 static const char *head; static unsigned char head_sha1[20]; @@ -352,8 +353,12 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag break; } } - if (ARRAY_SIZE(ref_kind) <= i) - return 0; + if (ARRAY_SIZE(ref_kind) <= i) { + if (!strcmp(refname, "HEAD")) + kind = REF_DETACHED_HEAD; + else + return 0; + } /* Don't add types the caller doesn't want */ if ((kind & ref_list->kinds) == 0) @@ -535,6 +540,8 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, int color; struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; const char *prefix = ""; + const char *desc = item->name; + char *to_free = NULL; if (item->ignore) return; @@ -547,6 +554,10 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, color = BRANCH_COLOR_REMOTE; prefix = remote_prefix; break; + case REF_DETACHED_HEAD: + color = BRANCH_COLOR_CURRENT; + desc = to_free = get_head_description(); + break; default: color = BRANCH_COLOR_PLAIN; break; @@ -558,7 +569,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, color = BRANCH_COLOR_CURRENT; } - strbuf_addf(&name, "%s%s", prefix, item->name); + strbuf_addf(&name, "%s%s", prefix, desc); if (verbose) { int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), @@ -581,6 +592,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } strbuf_release(&name); strbuf_release(&out); + free(to_free); } static int calc_maxwidth(struct ref_list *refs, int remote_bonus) @@ -601,22 +613,6 @@ static int calc_maxwidth(struct ref_list *refs, int remote_bonus) return max; } -static void show_detached(struct ref_list *ref_list, int maxwidth) -{ - struct commit *head_commit = lookup_commit_reference_gently(head_sha1, 1); - - if (head_commit && is_descendant_of(head_commit, ref_list->with_commit)) { - struct ref_item item; - item.name = get_head_description(); - item.kind = REF_LOCAL_BRANCH; - item.dest = NULL; - item.commit = head_commit; - item.ignore = 0; - print_ref_item(&item, maxwidth, ref_list->verbose, ref_list->abbrev, 1, ""); - free(item.name); - } -} - static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) { int i; @@ -643,7 +639,14 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru cb.ref_list = &ref_list; cb.pattern = pattern; cb.ret = 0; + /* + * First we obtain all regular branch refs and if the HEAD is + * detached then we insert that ref to the end of the ref_fist + * so that it can be printed and removed first. + */ for_each_rawref(append_ref, &cb); + if (detached) + head_ref(append_ref, &cb); /* * The following implementation is currently duplicated in ref-filter. It * will eventually be removed when we port branch.c to use ref-filter APIs. @@ -681,14 +684,12 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); - detached = (detached && (kinds & REF_LOCAL_BRANCH)); - if (detached && match_patterns(pattern, "HEAD")) - show_detached(&ref_list, maxwidth); - for (i = 0; i < ref_list.index; i++) { - int current = !detached && - (ref_list.list[i].kind == REF_LOCAL_BRANCH) && + int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); + /* If detached the first ref_item is the current ref */ + if (detached && i == 0) + current = 1; print_ref_item(&ref_list.list[i], maxwidth, verbose, abbrev, current, remote_prefix); } @@ -914,7 +915,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("branch name required")); return delete_branches(argc, argv, delete > 1, kinds, quiet); } else if (list) { - int ret = print_ref_list(kinds, detached, verbose, abbrev, + int ret; + /* git branch --local also shows HEAD when it is detached */ + if (kinds & REF_LOCAL_BRANCH) + kinds |= REF_DETACHED_HEAD; + ret = print_ref_list(kinds, detached, verbose, abbrev, with_commit, argv); print_columns(&output, colopts, NULL); string_list_clear(&output, 0); -- cgit v0.10.2-6-g49f6 From f65f13911afa8dd66d163049d91dbfe2ed73717e Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:09 +0530 Subject: branch: move 'current' check down to the presentation layer We check if given ref is the current branch in print_ref_list(). Move this check to print_ref_item() where it is checked right before printing. This enables a smooth transition to using ref-filter APIs, as we can later replace the current check while printing to just check for FILTER_REFS_DETACHED instead. Based-on-patch-by: Jeff King Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index a2a35f4..1a664ed 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -534,9 +534,10 @@ static char *get_head_description(void) } static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, - int abbrev, int current, const char *remote_prefix) + int abbrev, int detached, const char *remote_prefix) { char c; + int current = 0; int color; struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; const char *prefix = ""; @@ -548,15 +549,18 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, switch (item->kind) { case REF_LOCAL_BRANCH: - color = BRANCH_COLOR_LOCAL; + if (!detached && !strcmp(item->name, head)) + current = 1; + else + color = BRANCH_COLOR_LOCAL; break; case REF_REMOTE_BRANCH: color = BRANCH_COLOR_REMOTE; prefix = remote_prefix; break; case REF_DETACHED_HEAD: - color = BRANCH_COLOR_CURRENT; desc = to_free = get_head_description(); + current = 1; break; default: color = BRANCH_COLOR_PLAIN; @@ -684,15 +688,9 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); - for (i = 0; i < ref_list.index; i++) { - int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && - !strcmp(ref_list.list[i].name, head); - /* If detached the first ref_item is the current ref */ - if (detached && i == 0) - current = 1; + for (i = 0; i < ref_list.index; i++) print_ref_item(&ref_list.list[i], maxwidth, verbose, - abbrev, current, remote_prefix); - } + abbrev, detached, remote_prefix); free_ref_list(&ref_list); -- cgit v0.10.2-6-g49f6 From ca41799068e101e908544d4e2000a747b1389ebc Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Thu, 24 Sep 2015 23:39:08 +0530 Subject: branch: drop non-commit error reporting Remove the error "branch '%s' does not point at a commit" in append_ref(), which reports branch refs which do not point to commits. Also remove the error "some refs could not be read" in print_ref_list() which is triggered as a consequence of the first error. The purpose of these codepaths is not to diagnose and report a repository corruption. If we care about such a corruption, we should report it from fsck instead, which we already do. This also helps in a smooth port of branch.c to use ref-filter APIs over the following patches. On the other hand, ref-filter ignores refs which do not point at commits silently. Based-on-patch-by: Jeff King Helped-by: Junio C Hamano Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index 1a664ed..ebc3742 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -313,7 +313,6 @@ static char *resolve_symref(const char *src, const char *prefix) struct append_ref_cb { struct ref_list *ref_list; const char **pattern; - int ret; }; static int match_patterns(const char **pattern, const char *refname) @@ -370,10 +369,8 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag commit = NULL; if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { commit = lookup_commit_reference_gently(oid->hash, 1); - if (!commit) { - cb->ret = error(_("branch '%s' does not point at a commit"), refname); + if (!commit) return 0; - } /* Filter with with_commit if specified */ if (!is_descendant_of(commit, ref_list->with_commit)) @@ -617,7 +614,7 @@ static int calc_maxwidth(struct ref_list *refs, int remote_bonus) return max; } -static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) +static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) { int i; struct append_ref_cb cb; @@ -642,7 +639,6 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru init_revisions(&ref_list.revs, NULL); cb.ref_list = &ref_list; cb.pattern = pattern; - cb.ret = 0; /* * First we obtain all regular branch refs and if the HEAD is * detached then we insert that ref to the end of the ref_fist @@ -693,11 +689,6 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru abbrev, detached, remote_prefix); free_ref_list(&ref_list); - - if (cb.ret) - error(_("some refs could not be read")); - - return cb.ret; } static void rename_branch(const char *oldname, const char *newname, int force) @@ -913,15 +904,14 @@ int cmd_branch(int argc, const char **argv, const char *prefix) die(_("branch name required")); return delete_branches(argc, argv, delete > 1, kinds, quiet); } else if (list) { - int ret; /* git branch --local also shows HEAD when it is detached */ if (kinds & REF_LOCAL_BRANCH) kinds |= REF_DETACHED_HEAD; - ret = print_ref_list(kinds, detached, verbose, abbrev, + print_ref_list(kinds, detached, verbose, abbrev, with_commit, argv); print_columns(&output, colopts, NULL); string_list_clear(&output, 0); - return ret; + return 0; } else if (edit_description) { const char *branch_name; -- cgit v0.10.2-6-g49f6 From 1511b22d40d102f397104858a617aa1662bb1c98 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:11 +0530 Subject: branch.c: use 'ref-filter' data structures Make 'branch.c' use 'ref-filter' data structures and make changes to support the new data structures. This is a part of the process of porting 'branch.c' to use 'ref-filter' APIs. This is a temporary step before porting 'branch.c' to use 'ref-filter' completely. As this is a temporary step, most of the code introduced here will be removed when 'branch.c' is ported over to use 'ref-filter' APIs. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/builtin/branch.c b/builtin/branch.c index ebc3742..bfbba2f 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -19,6 +19,7 @@ #include "column.h" #include "utf8.h" #include "wt-status.h" +#include "ref-filter.h" static const char * const builtin_branch_usage[] = { N_("git branch [] [-r | -a] [--merged | --no-merged]"), @@ -28,10 +29,6 @@ static const char * const builtin_branch_usage[] = { NULL }; -#define REF_DETACHED_HEAD 0x01 -#define REF_LOCAL_BRANCH 0x02 -#define REF_REMOTE_BRANCH 0x04 - static const char *head; static unsigned char head_sha1[20]; @@ -53,13 +50,6 @@ enum color_branch { BRANCH_COLOR_UPSTREAM = 5 }; -static enum merge_filter { - NO_FILTER = 0, - SHOW_NOT_MERGED, - SHOW_MERGED -} merge_filter; -static unsigned char merge_filter_ref[20]; - static struct string_list output = STRING_LIST_INIT_DUP; static unsigned int colopts; @@ -122,7 +112,7 @@ static int branch_merged(int kind, const char *name, void *reference_name_to_free = NULL; int merged; - if (kind == REF_LOCAL_BRANCH) { + if (kind == FILTER_REFS_BRANCHES) { struct branch *branch = branch_get(name); const char *upstream = branch_get_upstream(branch, NULL); unsigned char sha1[20]; @@ -200,14 +190,14 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, struct strbuf bname = STRBUF_INIT; switch (kinds) { - case REF_REMOTE_BRANCH: + case FILTER_REFS_REMOTES: fmt = "refs/remotes/%s"; /* For subsequent UI messages */ remote_branch = 1; force = 1; break; - case REF_LOCAL_BRANCH: + case FILTER_REFS_BRANCHES: fmt = "refs/heads/%s"; break; default: @@ -224,7 +214,7 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, int flags = 0; strbuf_branchname(&bname, argv[i]); - if (kinds == REF_LOCAL_BRANCH && !strcmp(head, bname.buf)) { + if (kinds == FILTER_REFS_BRANCHES && !strcmp(head, bname.buf)) { error(_("Cannot delete the branch '%s' " "which you are currently on."), bname.buf); ret = 1; @@ -280,22 +270,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, return(ret); } -struct ref_item { - char *name; - char *dest; - unsigned int kind; - struct commit *commit; - int ignore; -}; - -struct ref_list { - struct rev_info revs; - int index, alloc, verbose, abbrev; - struct ref_item *list; - struct commit_list *with_commit; - int kinds; -}; - static char *resolve_symref(const char *src, const char *prefix) { unsigned char sha1[20]; @@ -310,11 +284,6 @@ static char *resolve_symref(const char *src, const char *prefix) return xstrdup(dst); } -struct append_ref_cb { - struct ref_list *ref_list; - const char **pattern; -}; - static int match_patterns(const char **pattern, const char *refname) { if (!*pattern) @@ -327,11 +296,29 @@ static int match_patterns(const char **pattern, const char *refname) return 0; } +/* + * Allocate memory for a new ref_array_item and insert that into the + * given ref_array. Doesn't take the objectname unlike + * new_ref_array_item(). This is a temporary function which will be + * removed when we port branch.c to use ref-filter APIs. + */ +static struct ref_array_item *ref_array_append(struct ref_array *array, const char *refname) +{ + size_t len = strlen(refname); + struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1); + memcpy(ref->refname, refname, len); + ref->refname[len] = '\0'; + REALLOC_ARRAY(array->items, array->nr + 1); + array->items[array->nr++] = ref; + return ref; +} + static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data) { - struct append_ref_cb *cb = (struct append_ref_cb *)(cb_data); - struct ref_list *ref_list = cb->ref_list; - struct ref_item *newitem; + struct ref_filter_cbdata *cb = (struct ref_filter_cbdata *)(cb_data); + struct ref_filter *filter = cb->filter; + struct ref_array *array = cb->array; + struct ref_array_item *item; struct commit *commit; int kind, i; const char *prefix, *orig_refname = refname; @@ -340,8 +327,8 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag int kind; const char *prefix; } ref_kind[] = { - { REF_LOCAL_BRANCH, "refs/heads/" }, - { REF_REMOTE_BRANCH, "refs/remotes/" }, + { FILTER_REFS_BRANCHES, "refs/heads/" }, + { FILTER_REFS_REMOTES, "refs/remotes/" }, }; /* Detect kind */ @@ -354,65 +341,52 @@ static int append_ref(const char *refname, const struct object_id *oid, int flag } if (ARRAY_SIZE(ref_kind) <= i) { if (!strcmp(refname, "HEAD")) - kind = REF_DETACHED_HEAD; + kind = FILTER_REFS_DETACHED_HEAD; else return 0; } /* Don't add types the caller doesn't want */ - if ((kind & ref_list->kinds) == 0) + if ((kind & filter->kind) == 0) return 0; - if (!match_patterns(cb->pattern, refname)) + if (!match_patterns(filter->name_patterns, refname)) return 0; commit = NULL; - if (ref_list->verbose || ref_list->with_commit || merge_filter != NO_FILTER) { + if (filter->verbose || filter->with_commit || filter->merge != REF_FILTER_MERGED_NONE) { commit = lookup_commit_reference_gently(oid->hash, 1); if (!commit) return 0; /* Filter with with_commit if specified */ - if (!is_descendant_of(commit, ref_list->with_commit)) + if (!is_descendant_of(commit, filter->with_commit)) return 0; - if (merge_filter != NO_FILTER) - add_pending_object(&ref_list->revs, + if (filter->merge != REF_FILTER_MERGED_NONE) + add_pending_object(array->revs, (struct object *)commit, refname); } - ALLOC_GROW(ref_list->list, ref_list->index + 1, ref_list->alloc); + item = ref_array_append(array, refname); /* Record the new item */ - newitem = &(ref_list->list[ref_list->index++]); - newitem->name = xstrdup(refname); - newitem->kind = kind; - newitem->commit = commit; - newitem->dest = resolve_symref(orig_refname, prefix); - newitem->ignore = 0; + item->kind = kind; + item->commit = commit; + item->symref = resolve_symref(orig_refname, prefix); + item->ignore = 0; return 0; } -static void free_ref_list(struct ref_list *ref_list) -{ - int i; - - for (i = 0; i < ref_list->index; i++) { - free(ref_list->list[i].name); - free(ref_list->list[i].dest); - } - free(ref_list->list); -} - static int ref_cmp(const void *r1, const void *r2) { - struct ref_item *c1 = (struct ref_item *)(r1); - struct ref_item *c2 = (struct ref_item *)(r2); + struct ref_array_item *c1 = *((struct ref_array_item **)r1); + struct ref_array_item *c2 = *((struct ref_array_item **)r2); if (c1->kind != c2->kind) return c1->kind - c2->kind; - return strcmp(c1->name, c2->name); + return strcmp(c1->refname, c2->refname); } static void fill_tracking_info(struct strbuf *stat, const char *branch_name, @@ -477,8 +451,8 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, free(ref); } -static void add_verbose_info(struct strbuf *out, struct ref_item *item, - int verbose, int abbrev) +static void add_verbose_info(struct strbuf *out, struct ref_array_item *item, + struct ref_filter *filter) { struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; const char *sub = _(" **** invalid ref ****"); @@ -489,11 +463,11 @@ static void add_verbose_info(struct strbuf *out, struct ref_item *item, sub = subject.buf; } - if (item->kind == REF_LOCAL_BRANCH) - fill_tracking_info(&stat, item->name, verbose > 1); + if (item->kind == FILTER_REFS_BRANCHES) + fill_tracking_info(&stat, item->refname, filter->verbose > 1); strbuf_addf(out, " %s %s%s", - find_unique_abbrev(item->commit->object.sha1, abbrev), + find_unique_abbrev(item->commit->object.sha1, filter->abbrev), stat.buf, sub); strbuf_release(&stat); strbuf_release(&subject); @@ -530,32 +504,32 @@ static char *get_head_description(void) return strbuf_detach(&desc, NULL); } -static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, - int abbrev, int detached, const char *remote_prefix) +static void print_ref_item(struct ref_array_item *item, int maxwidth, + struct ref_filter *filter, const char *remote_prefix) { char c; int current = 0; int color; struct strbuf out = STRBUF_INIT, name = STRBUF_INIT; const char *prefix = ""; - const char *desc = item->name; + const char *desc = item->refname; char *to_free = NULL; if (item->ignore) return; switch (item->kind) { - case REF_LOCAL_BRANCH: - if (!detached && !strcmp(item->name, head)) + case FILTER_REFS_BRANCHES: + if (!filter->detached && !strcmp(item->refname, head)) current = 1; else color = BRANCH_COLOR_LOCAL; break; - case REF_REMOTE_BRANCH: + case FILTER_REFS_REMOTES: color = BRANCH_COLOR_REMOTE; prefix = remote_prefix; break; - case REF_DETACHED_HEAD: + case FILTER_REFS_DETACHED_HEAD: desc = to_free = get_head_description(); current = 1; break; @@ -571,7 +545,7 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, } strbuf_addf(&name, "%s%s", prefix, desc); - if (verbose) { + if (filter->verbose) { int utf8_compensation = strlen(name.buf) - utf8_strwidth(name.buf); strbuf_addf(&out, "%c %s%-*s%s", c, branch_get_color(color), maxwidth + utf8_compensation, name.buf, @@ -580,13 +554,13 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), name.buf, branch_get_color(BRANCH_COLOR_RESET)); - if (item->dest) - strbuf_addf(&out, " -> %s", item->dest); - else if (verbose) + if (item->symref) + strbuf_addf(&out, " -> %s", item->symref); + else if (filter->verbose) /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ - add_verbose_info(&out, item, verbose, abbrev); + add_verbose_info(&out, item, filter); if (column_active(colopts)) { - assert(!verbose && "--column and --verbose are incompatible"); + assert(!filter->verbose && "--column and --verbose are incompatible"); string_list_append(&output, out.buf); } else { printf("%s\n", out.buf); @@ -596,17 +570,17 @@ static void print_ref_item(struct ref_item *item, int maxwidth, int verbose, free(to_free); } -static int calc_maxwidth(struct ref_list *refs, int remote_bonus) +static int calc_maxwidth(struct ref_array *refs, int remote_bonus) { int i, max = 0; - for (i = 0; i < refs->index; i++) { - struct ref_item *it = &refs->list[i]; + for (i = 0; i < refs->nr; i++) { + struct ref_array_item *it = refs->items[i]; int w; if (it->ignore) continue; - w = utf8_strwidth(it->name); - if (it->kind == REF_REMOTE_BRANCH) + w = utf8_strwidth(it->refname); + if (it->kind == FILTER_REFS_REMOTES) w += remote_bonus; if (w > max) max = w; @@ -614,81 +588,74 @@ static int calc_maxwidth(struct ref_list *refs, int remote_bonus) return max; } -static void print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) +static void print_ref_list(struct ref_filter *filter) { int i; - struct append_ref_cb cb; - struct ref_list ref_list; + struct ref_array array; + struct ref_filter_cbdata data; int maxwidth = 0; const char *remote_prefix = ""; + struct rev_info revs; /* * If we are listing more than just remote branches, * then remote branches will have a "remotes/" prefix. * We need to account for this in the width. */ - if (kinds != REF_REMOTE_BRANCH) + if (filter->kind != FILTER_REFS_REMOTES) remote_prefix = "remotes/"; - memset(&ref_list, 0, sizeof(ref_list)); - ref_list.kinds = kinds; - ref_list.verbose = verbose; - ref_list.abbrev = abbrev; - ref_list.with_commit = with_commit; - if (merge_filter != NO_FILTER) - init_revisions(&ref_list.revs, NULL); - cb.ref_list = &ref_list; - cb.pattern = pattern; + memset(&array, 0, sizeof(array)); + if (filter->merge != REF_FILTER_MERGED_NONE) + init_revisions(&revs, NULL); + + data.array = &array; + data.filter = filter; + array.revs = &revs; + /* * First we obtain all regular branch refs and if the HEAD is * detached then we insert that ref to the end of the ref_fist * so that it can be printed and removed first. */ - for_each_rawref(append_ref, &cb); - if (detached) - head_ref(append_ref, &cb); + for_each_rawref(append_ref, &data); + if (filter->detached) + head_ref(append_ref, &data); /* * The following implementation is currently duplicated in ref-filter. It * will eventually be removed when we port branch.c to use ref-filter APIs. */ - if (merge_filter != NO_FILTER) { - struct commit *filter; - filter = lookup_commit_reference_gently(merge_filter_ref, 0); - if (!filter) - die(_("object '%s' does not point to a commit"), - sha1_to_hex(merge_filter_ref)); - - filter->object.flags |= UNINTERESTING; - add_pending_object(&ref_list.revs, - (struct object *) filter, ""); - ref_list.revs.limited = 1; - - if (prepare_revision_walk(&ref_list.revs)) + if (filter->merge != REF_FILTER_MERGED_NONE) { + filter->merge_commit->object.flags |= UNINTERESTING; + add_pending_object(&revs, &filter->merge_commit->object, ""); + revs.limited = 1; + + if (prepare_revision_walk(&revs)) die(_("revision walk setup failed")); - for (i = 0; i < ref_list.index; i++) { - struct ref_item *item = &ref_list.list[i]; + for (i = 0; i < array.nr; i++) { + struct ref_array_item *item = array.items[i]; struct commit *commit = item->commit; int is_merged = !!(commit->object.flags & UNINTERESTING); - item->ignore = is_merged != (merge_filter == SHOW_MERGED); + item->ignore = is_merged != (filter->merge == REF_FILTER_MERGED_INCLUDE); } - for (i = 0; i < ref_list.index; i++) { - struct ref_item *item = &ref_list.list[i]; + for (i = 0; i < array.nr; i++) { + struct ref_array_item *item = array.items[i]; clear_commit_marks(item->commit, ALL_REV_FLAGS); } - clear_commit_marks(filter, ALL_REV_FLAGS); + clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS); } - if (verbose) - maxwidth = calc_maxwidth(&ref_list, strlen(remote_prefix)); - qsort(ref_list.list, ref_list.index, sizeof(struct ref_item), ref_cmp); + if (filter->verbose) + maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); + + qsort(array.items, array.nr, sizeof(struct ref_array_item *), ref_cmp); - for (i = 0; i < ref_list.index; i++) - print_ref_item(&ref_list.list[i], maxwidth, verbose, - abbrev, detached, remote_prefix); + for (i = 0; i < array.nr; i++) + print_ref_item(array.items[i], maxwidth, filter, remote_prefix); - free_ref_list(&ref_list); + ref_array_clear(&array); } static void rename_branch(const char *oldname, const char *newname, int force) @@ -744,24 +711,6 @@ static void rename_branch(const char *oldname, const char *newname, int force) strbuf_release(&newsection); } -/* - * This function is duplicated in ref-filter. It will eventually be removed - * when we port branch.c to use ref-filter APIs. - */ -static int opt_parse_merge_filter(const struct option *opt, const char *arg, int unset) -{ - merge_filter = ((opt->long_name[0] == 'n') - ? SHOW_NOT_MERGED - : SHOW_MERGED); - if (unset) - merge_filter = SHOW_NOT_MERGED; /* b/c for --no-merged */ - if (!arg) - arg = "HEAD"; - if (get_sha1(arg, merge_filter_ref)) - die(_("malformed object name %s"), arg); - return 0; -} - static const char edit_description[] = "BRANCH_DESCRIPTION"; static int edit_branch_description(const char *branch_name) @@ -801,17 +750,15 @@ static int edit_branch_description(const char *branch_name) int cmd_branch(int argc, const char **argv, const char *prefix) { int delete = 0, rename = 0, force = 0, list = 0; - int verbose = 0, abbrev = -1, detached = 0; int reflog = 0, edit_description = 0; int quiet = 0, unset_upstream = 0; const char *new_upstream = NULL; enum branch_track track; - int kinds = REF_LOCAL_BRANCH; - struct commit_list *with_commit = NULL; + struct ref_filter filter; struct option options[] = { OPT_GROUP(N_("Generic options")), - OPT__VERBOSE(&verbose, + OPT__VERBOSE(&filter.verbose, N_("show hash and subject, give twice for upstream branch")), OPT__QUIET(&quiet, N_("suppress informational messages")), OPT_SET_INT('t', "track", &track, N_("set up tracking mode (see git-pull(1))"), @@ -821,15 +768,15 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_STRING('u', "set-upstream-to", &new_upstream, "upstream", "change the upstream info"), OPT_BOOL(0, "unset-upstream", &unset_upstream, "Unset the upstream info"), OPT__COLOR(&branch_use_color, N_("use colored output")), - OPT_SET_INT('r', "remotes", &kinds, N_("act on remote-tracking branches"), - REF_REMOTE_BRANCH), - OPT_CONTAINS(&with_commit, N_("print only branches that contain the commit")), - OPT_WITH(&with_commit, N_("print only branches that contain the commit")), - OPT__ABBREV(&abbrev), + OPT_SET_INT('r', "remotes", &filter.kind, N_("act on remote-tracking branches"), + FILTER_REFS_REMOTES), + OPT_CONTAINS(&filter.with_commit, N_("print only branches that contain the commit")), + OPT_WITH(&filter.with_commit, N_("print only branches that contain the commit")), + OPT__ABBREV(&filter.abbrev), OPT_GROUP(N_("Specific git-branch actions:")), - OPT_SET_INT('a', "all", &kinds, N_("list both remote-tracking and local branches"), - REF_REMOTE_BRANCH | REF_LOCAL_BRANCH), + OPT_SET_INT('a', "all", &filter.kind, N_("list both remote-tracking and local branches"), + FILTER_REFS_REMOTES | FILTER_REFS_BRANCHES), OPT_BIT('d', "delete", &delete, N_("delete fully merged branch"), 1), OPT_BIT('D', NULL, &delete, N_("delete branch (even if not merged)"), 2), OPT_BIT('m', "move", &rename, N_("move/rename a branch and its reflog"), 1), @@ -839,22 +786,16 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_BOOL(0, "edit-description", &edit_description, N_("edit the description for the branch")), OPT__FORCE(&force, N_("force creation, move/rename, deletion")), - { - OPTION_CALLBACK, 0, "no-merged", &merge_filter_ref, - N_("commit"), N_("print only not merged branches"), - PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, - opt_parse_merge_filter, (intptr_t) "HEAD", - }, - { - OPTION_CALLBACK, 0, "merged", &merge_filter_ref, - N_("commit"), N_("print only merged branches"), - PARSE_OPT_LASTARG_DEFAULT | PARSE_OPT_NONEG, - opt_parse_merge_filter, (intptr_t) "HEAD", - }, + OPT_MERGED(&filter, N_("print only branches that are merged")), + OPT_NO_MERGED(&filter, N_("print only branches that are not merged")), OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), OPT_END(), }; + memset(&filter, 0, sizeof(filter)); + filter.kind = FILTER_REFS_BRANCHES; + filter.abbrev = -1; + if (argc == 2 && !strcmp(argv[1], "-h")) usage_with_options(builtin_branch_usage, options); @@ -866,11 +807,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!head) die(_("Failed to resolve HEAD as a valid ref.")); if (!strcmp(head, "HEAD")) - detached = 1; + filter.detached = 1; else if (!skip_prefix(head, "refs/heads/", &head)) die(_("HEAD not found below refs/heads!")); - hashcpy(merge_filter_ref, head_sha1); - argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, 0); @@ -878,17 +817,17 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) list = 1; - if (with_commit || merge_filter != NO_FILTER) + if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE) list = 1; if (!!delete + !!rename + !!new_upstream + list + unset_upstream > 1) usage_with_options(builtin_branch_usage, options); - if (abbrev == -1) - abbrev = DEFAULT_ABBREV; + if (filter.abbrev == -1) + filter.abbrev = DEFAULT_ABBREV; finalize_colopts(&colopts, -1); - if (verbose) { + if (filter.verbose) { if (explicitly_enable_column(colopts)) die(_("--column and --verbose are incompatible")); colopts = 0; @@ -902,13 +841,13 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (delete) { if (!argc) die(_("branch name required")); - return delete_branches(argc, argv, delete > 1, kinds, quiet); + return delete_branches(argc, argv, delete > 1, filter.kind, quiet); } else if (list) { /* git branch --local also shows HEAD when it is detached */ - if (kinds & REF_LOCAL_BRANCH) - kinds |= REF_DETACHED_HEAD; - print_ref_list(kinds, detached, verbose, abbrev, - with_commit, argv); + if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached) + filter.kind |= FILTER_REFS_DETACHED_HEAD; + filter.name_patterns = argv; + print_ref_list(&filter); print_columns(&output, colopts, NULL); string_list_clear(&output, 0); return 0; @@ -918,7 +857,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) struct strbuf branch_ref = STRBUF_INIT; if (!argc) { - if (detached) + if (filter.detached) die(_("Cannot give description to detached HEAD")); branch_name = head; } else if (argc == 1) @@ -1006,7 +945,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!branch) die(_("no such branch '%s'"), argv[0]); - if (kinds != REF_LOCAL_BRANCH) + if (filter.kind != FILTER_REFS_BRANCHES) die(_("-a and -r options to 'git branch' do not make sense with a branch name")); if (track == BRANCH_TRACK_OVERRIDE) diff --git a/ref-filter.h b/ref-filter.h index a5cfa5e..fadebc5 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -36,6 +36,7 @@ struct ref_array_item { unsigned char objectname[20]; int flag; unsigned int kind; + int ignore : 1; /* To be removed in the next patch */ const char *symref; struct commit *commit; struct atom_value *value; @@ -45,6 +46,7 @@ struct ref_array_item { struct ref_array { int nr, alloc; struct ref_array_item **items; + struct rev_info *revs; }; struct ref_filter { @@ -60,9 +62,12 @@ struct ref_filter { struct commit *merge_commit; unsigned int with_commit_tag_algo : 1, - match_as_path : 1; + match_as_path : 1, + detached : 1; unsigned int kind, lines; + int abbrev, + verbose; }; struct ref_filter_cbdata { -- cgit v0.10.2-6-g49f6 From aedcb7dc75e5c260f20bebe14925f3ac4841b03d Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:12 +0530 Subject: branch.c: use 'ref-filter' APIs Make 'branch.c' use 'ref-filter' APIs for iterating through refs sorting. This removes most of the code used in 'branch.c' replacing it with calls to the 'ref-filter' library. Make 'branch.c' use the 'filter_refs()' function provided by 'ref-filter' to filter out tags based on the options set. We provide a sorting option provided for 'branch.c' by using the sorting options provided by 'ref-filter'. Also by default, we sort by 'refname'. Since 'HEAD' is alphabatically before 'refs/...' we end up with an array consisting of the 'HEAD' ref then the local branches and finally the remote-tracking branches. Also remove the 'ignore' variable from ref_array_item as it was previously used for the '--merged' option and now that is handled by ref-filter. Modify some of the tests in t1430 to check the stderr for a warning regarding the broken ref. This is done as ref-filter throws a warning for broken refs rather than directly printing them. Add tests and documentation for the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index a67138a..c45295d 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -11,7 +11,7 @@ SYNOPSIS 'git branch' [--color[=] | --no-color] [-r | -a] [--list] [-v [--abbrev= | --no-abbrev]] [--column[=] | --no-column] - [(--merged | --no-merged | --contains) []] [...] + [(--merged | --no-merged | --contains) []] [--sort=] [...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] [] 'git branch' (--set-upstream-to= | -u ) [] 'git branch' --unset-upstream [] @@ -229,6 +229,16 @@ start-point is either a local or remote-tracking branch. The new name for an existing branch. The same restrictions as for apply. +--sort=:: + Sort based on the key given. Prefix `-` to sort in descending + order of the value. You may use the --sort= option + multiple times, in which case the last key becomes the primary + key. The keys supported are the same as those in `git + for-each-ref`. Sort order defaults to sorting based on the + full refname (including `refs/...` prefix). This lists + detached HEAD (if present) first, then local branches and + finally remote-tracking branches. + Examples -------- diff --git a/builtin/branch.c b/builtin/branch.c index bfbba2f..b83116a 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -270,125 +270,6 @@ static int delete_branches(int argc, const char **argv, int force, int kinds, return(ret); } -static char *resolve_symref(const char *src, const char *prefix) -{ - unsigned char sha1[20]; - int flag; - const char *dst; - - dst = resolve_ref_unsafe(src, 0, sha1, &flag); - if (!(dst && (flag & REF_ISSYMREF))) - return NULL; - if (prefix) - skip_prefix(dst, prefix, &dst); - return xstrdup(dst); -} - -static int match_patterns(const char **pattern, const char *refname) -{ - if (!*pattern) - return 1; /* no pattern always matches */ - while (*pattern) { - if (!wildmatch(*pattern, refname, 0, NULL)) - return 1; - pattern++; - } - return 0; -} - -/* - * Allocate memory for a new ref_array_item and insert that into the - * given ref_array. Doesn't take the objectname unlike - * new_ref_array_item(). This is a temporary function which will be - * removed when we port branch.c to use ref-filter APIs. - */ -static struct ref_array_item *ref_array_append(struct ref_array *array, const char *refname) -{ - size_t len = strlen(refname); - struct ref_array_item *ref = xcalloc(1, sizeof(struct ref_array_item) + len + 1); - memcpy(ref->refname, refname, len); - ref->refname[len] = '\0'; - REALLOC_ARRAY(array->items, array->nr + 1); - array->items[array->nr++] = ref; - return ref; -} - -static int append_ref(const char *refname, const struct object_id *oid, int flags, void *cb_data) -{ - struct ref_filter_cbdata *cb = (struct ref_filter_cbdata *)(cb_data); - struct ref_filter *filter = cb->filter; - struct ref_array *array = cb->array; - struct ref_array_item *item; - struct commit *commit; - int kind, i; - const char *prefix, *orig_refname = refname; - - static struct { - int kind; - const char *prefix; - } ref_kind[] = { - { FILTER_REFS_BRANCHES, "refs/heads/" }, - { FILTER_REFS_REMOTES, "refs/remotes/" }, - }; - - /* Detect kind */ - for (i = 0; i < ARRAY_SIZE(ref_kind); i++) { - prefix = ref_kind[i].prefix; - if (skip_prefix(refname, prefix, &refname)) { - kind = ref_kind[i].kind; - break; - } - } - if (ARRAY_SIZE(ref_kind) <= i) { - if (!strcmp(refname, "HEAD")) - kind = FILTER_REFS_DETACHED_HEAD; - else - return 0; - } - - /* Don't add types the caller doesn't want */ - if ((kind & filter->kind) == 0) - return 0; - - if (!match_patterns(filter->name_patterns, refname)) - return 0; - - commit = NULL; - if (filter->verbose || filter->with_commit || filter->merge != REF_FILTER_MERGED_NONE) { - commit = lookup_commit_reference_gently(oid->hash, 1); - if (!commit) - return 0; - - /* Filter with with_commit if specified */ - if (!is_descendant_of(commit, filter->with_commit)) - return 0; - - if (filter->merge != REF_FILTER_MERGED_NONE) - add_pending_object(array->revs, - (struct object *)commit, refname); - } - - item = ref_array_append(array, refname); - - /* Record the new item */ - item->kind = kind; - item->commit = commit; - item->symref = resolve_symref(orig_refname, prefix); - item->ignore = 0; - - return 0; -} - -static int ref_cmp(const void *r1, const void *r2) -{ - struct ref_array_item *c1 = *((struct ref_array_item **)r1); - struct ref_array_item *c2 = *((struct ref_array_item **)r2); - - if (c1->kind != c2->kind) - return c1->kind - c2->kind; - return strcmp(c1->refname, c2->refname); -} - static void fill_tracking_info(struct strbuf *stat, const char *branch_name, int show_upstream_ref) { @@ -452,7 +333,7 @@ static void fill_tracking_info(struct strbuf *stat, const char *branch_name, } static void add_verbose_info(struct strbuf *out, struct ref_array_item *item, - struct ref_filter *filter) + struct ref_filter *filter, const char *refname) { struct strbuf subject = STRBUF_INIT, stat = STRBUF_INIT; const char *sub = _(" **** invalid ref ****"); @@ -464,7 +345,7 @@ static void add_verbose_info(struct strbuf *out, struct ref_array_item *item, } if (item->kind == FILTER_REFS_BRANCHES) - fill_tracking_info(&stat, item->refname, filter->verbose > 1); + fill_tracking_info(&stat, refname, filter->verbose > 1); strbuf_addf(out, " %s %s%s", find_unique_abbrev(item->commit->object.sha1, filter->abbrev), @@ -504,8 +385,8 @@ static char *get_head_description(void) return strbuf_detach(&desc, NULL); } -static void print_ref_item(struct ref_array_item *item, int maxwidth, - struct ref_filter *filter, const char *remote_prefix) +static void format_and_print_ref_item(struct ref_array_item *item, int maxwidth, + struct ref_filter *filter, const char *remote_prefix) { char c; int current = 0; @@ -515,17 +396,16 @@ static void print_ref_item(struct ref_array_item *item, int maxwidth, const char *desc = item->refname; char *to_free = NULL; - if (item->ignore) - return; - switch (item->kind) { case FILTER_REFS_BRANCHES: - if (!filter->detached && !strcmp(item->refname, head)) + skip_prefix(desc, "refs/heads/", &desc); + if (!filter->detached && !strcmp(desc, head)) current = 1; else color = BRANCH_COLOR_LOCAL; break; case FILTER_REFS_REMOTES: + skip_prefix(desc, "refs/remotes/", &desc); color = BRANCH_COLOR_REMOTE; prefix = remote_prefix; break; @@ -554,11 +434,13 @@ static void print_ref_item(struct ref_array_item *item, int maxwidth, strbuf_addf(&out, "%c %s%s%s", c, branch_get_color(color), name.buf, branch_get_color(BRANCH_COLOR_RESET)); - if (item->symref) - strbuf_addf(&out, " -> %s", item->symref); + if (item->symref) { + skip_prefix(item->symref, "refs/remotes/", &desc); + strbuf_addf(&out, " -> %s", desc); + } else if (filter->verbose) /* " f7c0c00 [ahead 58, behind 197] vcs-svn: drop obj_pool.h" */ - add_verbose_info(&out, item, filter); + add_verbose_info(&out, item, filter, desc); if (column_active(colopts)) { assert(!filter->verbose && "--column and --verbose are incompatible"); string_list_append(&output, out.buf); @@ -575,11 +457,13 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus) int i, max = 0; for (i = 0; i < refs->nr; i++) { struct ref_array_item *it = refs->items[i]; + const char *desc = it->refname; int w; - if (it->ignore) - continue; - w = utf8_strwidth(it->refname); + skip_prefix(it->refname, "refs/heads/", &desc); + skip_prefix(it->refname, "refs/remotes/", &desc); + w = utf8_strwidth(desc); + if (it->kind == FILTER_REFS_REMOTES) w += remote_bonus; if (w > max) @@ -588,14 +472,12 @@ static int calc_maxwidth(struct ref_array *refs, int remote_bonus) return max; } -static void print_ref_list(struct ref_filter *filter) +static void print_ref_list(struct ref_filter *filter, struct ref_sorting *sorting) { int i; struct ref_array array; - struct ref_filter_cbdata data; int maxwidth = 0; const char *remote_prefix = ""; - struct rev_info revs; /* * If we are listing more than just remote branches, @@ -606,54 +488,26 @@ static void print_ref_list(struct ref_filter *filter) remote_prefix = "remotes/"; memset(&array, 0, sizeof(array)); - if (filter->merge != REF_FILTER_MERGED_NONE) - init_revisions(&revs, NULL); - - data.array = &array; - data.filter = filter; - array.revs = &revs; - - /* - * First we obtain all regular branch refs and if the HEAD is - * detached then we insert that ref to the end of the ref_fist - * so that it can be printed and removed first. - */ - for_each_rawref(append_ref, &data); - if (filter->detached) - head_ref(append_ref, &data); - /* - * The following implementation is currently duplicated in ref-filter. It - * will eventually be removed when we port branch.c to use ref-filter APIs. - */ - if (filter->merge != REF_FILTER_MERGED_NONE) { - filter->merge_commit->object.flags |= UNINTERESTING; - add_pending_object(&revs, &filter->merge_commit->object, ""); - revs.limited = 1; - - if (prepare_revision_walk(&revs)) - die(_("revision walk setup failed")); - - for (i = 0; i < array.nr; i++) { - struct ref_array_item *item = array.items[i]; - struct commit *commit = item->commit; - int is_merged = !!(commit->object.flags & UNINTERESTING); - item->ignore = is_merged != (filter->merge == REF_FILTER_MERGED_INCLUDE); - } - for (i = 0; i < array.nr; i++) { - struct ref_array_item *item = array.items[i]; - clear_commit_marks(item->commit, ALL_REV_FLAGS); - } - clear_commit_marks(filter->merge_commit, ALL_REV_FLAGS); - } + verify_ref_format("%(refname)%(symref)"); + filter_refs(&array, filter, filter->kind | FILTER_REFS_INCLUDE_BROKEN); if (filter->verbose) maxwidth = calc_maxwidth(&array, strlen(remote_prefix)); - qsort(array.items, array.nr, sizeof(struct ref_array_item *), ref_cmp); + /* + * If no sorting parameter is given then we default to sorting + * by 'refname'. This would give us an alphabetically sorted + * array with the 'HEAD' ref at the beginning followed by + * local branches 'refs/heads/...' and finally remote-tacking + * branches 'refs/remotes/...'. + */ + if (!sorting) + sorting = ref_default_sorting(); + ref_array_sort(sorting, &array); for (i = 0; i < array.nr; i++) - print_ref_item(array.items[i], maxwidth, filter, remote_prefix); + format_and_print_ref_item(array.items[i], maxwidth, filter, remote_prefix); ref_array_clear(&array); } @@ -755,6 +609,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) const char *new_upstream = NULL; enum branch_track track; struct ref_filter filter; + static struct ref_sorting *sorting = NULL, **sorting_tail = &sorting; struct option options[] = { OPT_GROUP(N_("Generic options")), @@ -789,6 +644,8 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_MERGED(&filter, N_("print only branches that are merged")), OPT_NO_MERGED(&filter, N_("print only branches that are not merged")), OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), + OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), + N_("field name to sort on"), &parse_opt_ref_sorting), OPT_END(), }; @@ -847,7 +704,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if ((filter.kind & FILTER_REFS_BRANCHES) && filter.detached) filter.kind |= FILTER_REFS_DETACHED_HEAD; filter.name_patterns = argv; - print_ref_list(&filter); + print_ref_list(&filter, sorting); print_columns(&output, colopts, NULL); string_list_clear(&output, 0); return 0; diff --git a/ref-filter.c b/ref-filter.c index fd839ac..dbd8fce 100644 --- a/ref-filter.c +++ b/ref-filter.c @@ -1331,7 +1331,7 @@ static int ref_filter_handler(const char *refname, const struct object_id *oid, * obtain the commit using the 'oid' available and discard all * non-commits early. The actual filtering is done later. */ - if (filter->merge_commit || filter->with_commit) { + if (filter->merge_commit || filter->with_commit || filter->verbose) { commit = lookup_commit_reference_gently(oid->hash, 1); if (!commit) return 0; diff --git a/ref-filter.h b/ref-filter.h index fadebc5..14d435e 100644 --- a/ref-filter.h +++ b/ref-filter.h @@ -36,7 +36,6 @@ struct ref_array_item { unsigned char objectname[20]; int flag; unsigned int kind; - int ignore : 1; /* To be removed in the next patch */ const char *symref; struct commit *commit; struct atom_value *value; diff --git a/t/t1430-bad-ref-name.sh b/t/t1430-bad-ref-name.sh index 16d0b8b..c465abe 100755 --- a/t/t1430-bad-ref-name.sh +++ b/t/t1430-bad-ref-name.sh @@ -38,18 +38,20 @@ test_expect_success 'fast-import: fail on invalid branch name "bad[branch]name"' test_must_fail git fast-import output && - grep -e "broken\.\.\.ref" output + git branch >output 2>error && + grep -e "broken\.\.\.ref" error && + ! grep -e "broken\.\.\.ref" output ' test_expect_success 'branch -d can delete badly named ref' ' cp .git/refs/heads/master .git/refs/heads/broken...ref && test_when_finished "rm -f .git/refs/heads/broken...ref" && git branch -d broken...ref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -57,7 +59,8 @@ test_expect_success 'branch -D can delete badly named ref' ' cp .git/refs/heads/master .git/refs/heads/broken...ref && test_when_finished "rm -f .git/refs/heads/broken...ref" && git branch -D broken...ref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -85,7 +88,8 @@ test_expect_success 'branch -D cannot delete absolute path' ' test_expect_success 'git branch cannot create a badly named ref' ' test_when_finished "rm -f .git/refs/heads/broken...ref" && test_must_fail git branch broken...ref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -95,7 +99,8 @@ test_expect_success 'branch -m cannot rename to a bad ref name' ' git branch goodref && test_must_fail git branch -m goodref broken...ref && test_cmp_rev master goodref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -104,14 +109,16 @@ test_expect_failure 'branch -m can rename from a bad ref name' ' test_when_finished "rm -f .git/refs/heads/broken...ref" && git branch -m broken...ref renamed && test_cmp_rev master renamed && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' test_expect_success 'push cannot create a badly named ref' ' test_when_finished "rm -f .git/refs/heads/broken...ref" && test_must_fail git push "file://$(pwd)" HEAD:refs/heads/broken...ref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -131,7 +138,8 @@ test_expect_failure 'push --mirror can delete badly named ref' ' cp .git/refs/heads/master .git/refs/heads/broken...ref ) && git -C src push --mirror "file://$top/dest" && - git -C dest branch >output && + git -C dest branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' @@ -159,7 +167,8 @@ test_expect_success 'update-ref -d can delete broken name' ' cp .git/refs/heads/master .git/refs/heads/broken...ref && test_when_finished "rm -f .git/refs/heads/broken...ref" && git update-ref -d refs/heads/broken...ref && - git branch >output && + git branch >output 2>error && + ! grep -e "broken\.\.\.ref" error && ! grep -e "broken\.\.\.ref" output ' diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index f51d0f3..a427163 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -143,4 +143,15 @@ EOF test_i18ncmp expect actual ' +test_expect_success 'git branch `--sort` option' ' + cat >expect <<-\EOF && + branch-two + * (HEAD detached from fromtag) + branch-one + master + EOF + git branch --sort=objectsize >actual && + test_i18ncmp expect actual +' + test_done -- cgit v0.10.2-6-g49f6 From aa3bc55e408d4daab52239d6b80f7d4bb87f6de7 Mon Sep 17 00:00:00 2001 From: Karthik Nayak Date: Wed, 23 Sep 2015 23:41:13 +0530 Subject: branch: add '--points-at' option Add the '--points-at' option provided by 'ref-filter'. The option lets the user to list only branches which points at the given object. Add documentation and tests for the same. Mentored-by: Christian Couder Mentored-by: Matthieu Moy Signed-off-by: Karthik Nayak Signed-off-by: Junio C Hamano diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index c45295d..03c7af1 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -11,7 +11,8 @@ SYNOPSIS 'git branch' [--color[=] | --no-color] [-r | -a] [--list] [-v [--abbrev= | --no-abbrev]] [--column[=] | --no-column] - [(--merged | --no-merged | --contains) []] [--sort=] [...] + [(--merged | --no-merged | --contains) []] [--sort=] + [--points-at ] [...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] [] 'git branch' (--set-upstream-to= | -u ) [] 'git branch' --unset-upstream [] @@ -240,6 +241,9 @@ start-point is either a local or remote-tracking branch. finally remote-tracking branches. +--points-at :: + Only list branches of the given object. + Examples -------- diff --git a/builtin/branch.c b/builtin/branch.c index b83116a..b7a60f4 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -26,6 +26,7 @@ static const char * const builtin_branch_usage[] = { N_("git branch [] [-l] [-f] []"), N_("git branch [] [-r] (-d | -D) ..."), N_("git branch [] (-m | -M) [] "), + N_("git branch [] [-r | -a] [--points-at]"), NULL }; @@ -646,6 +647,10 @@ int cmd_branch(int argc, const char **argv, const char *prefix) OPT_COLUMN(0, "column", &colopts, N_("list branches in columns")), OPT_CALLBACK(0 , "sort", sorting_tail, N_("key"), N_("field name to sort on"), &parse_opt_ref_sorting), + { + OPTION_CALLBACK, 0, "points-at", &filter.points_at, N_("object"), + N_("print only branches of the object"), 0, parse_opt_object_name + }, OPT_END(), }; @@ -674,7 +679,7 @@ int cmd_branch(int argc, const char **argv, const char *prefix) if (!delete && !rename && !edit_description && !new_upstream && !unset_upstream && argc == 0) list = 1; - if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE) + if (filter.with_commit || filter.merge != REF_FILTER_MERGED_NONE || filter.points_at.nr) list = 1; if (!!delete + !!rename + !!new_upstream + diff --git a/t/t3203-branch-output.sh b/t/t3203-branch-output.sh index a427163..f1ae5ff 100755 --- a/t/t3203-branch-output.sh +++ b/t/t3203-branch-output.sh @@ -154,4 +154,13 @@ test_expect_success 'git branch `--sort` option' ' test_i18ncmp expect actual ' +test_expect_success 'git branch --points-at option' ' + cat >expect <<-\EOF && + branch-one + master + EOF + git branch --points-at=branch-one >actual && + test_cmp expect actual +' + test_done -- cgit v0.10.2-6-g49f6