Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@xxxxxxxxx> --- Some time ago, I posted a patch that added date sort to git-branch and Peff pointed me to for-each-ref. I did not look at it closely. Now it does not seem hard to lend some code from for-each-ref to git-branch. I can list 10 most recently touched branches with git branch --sort=-committerdate -v --count=10 kind of cool. I don't think adding --format is necessary because git-branch already has its own formatting. Documentation/git-branch.txt | 13 +++++++++ Makefile | 1 + builtin/branch.c | 61 +++++++++++++++++++++++++++++++++-------- builtin/for-each-ref.c | 33 +++------------------- builtin/for-each-ref.h | 32 ++++++++++++++++++++++ 5 files changed, 100 insertions(+), 40 deletions(-) create mode 100644 builtin/for-each-ref.h diff --git a/Documentation/git-branch.txt b/Documentation/git-branch.txt index 0427e80..b6f2826 100644 --- a/Documentation/git-branch.txt +++ b/Documentation/git-branch.txt @@ -10,6 +10,7 @@ SYNOPSIS [verse] 'git branch' [--color[=<when>] | --no-color] [-r | -a] [--list] [-v [--abbrev=<length> | --no-abbrev]] + [--count=<count>] [(--sort=<key>)...] [(--merged | --no-merged | --contains) [<commit>]] [<pattern>...] 'git branch' [--set-upstream | --track | --no-track] [-l] [-f] <branchname> [<start-point>] 'git branch' (-m | -M) [<oldbranch>] <newbranch> @@ -192,6 +193,18 @@ start-point is either a local or remote-tracking branch. The new name for an existing branch. The same restrictions as for <branchname> apply. +<count>:: + By default the command shows all refs that match + `<pattern>`. This option makes it stop after showing + that many refs. + +<key>:: + A field name to sort on. Prefix `-` to sort in descending + order of the value. When unspecified, `refname` is used. You + may use the --sort=<key> option multiple times, in which case + the last key becomes the primary key. See + linkgit:for-each-ref[1] for field name details. + Examples -------- diff --git a/Makefile b/Makefile index a782409..daf3e46 100644 --- a/Makefile +++ b/Makefile @@ -2108,6 +2108,7 @@ builtin/log.o builtin/shortlog.o: shortlog.h builtin/prune.o builtin/reflog.o reachable.o: reachable.h builtin/commit.o builtin/revert.o wt-status.o: wt-status.h builtin/tar-tree.o archive-tar.o: tar.h +builtin/branch.o builtin/for-each-ref.o: builtin/for-each-ref.h connect.o transport.o url.o http-backend.o: url.h http-fetch.o http-walker.o remote-curl.o transport.o walker.o: walker.h http.o http-walker.o http-push.o http-fetch.o remote-curl.o: http.h url.h diff --git a/builtin/branch.c b/builtin/branch.c index 7095718..67bdbc7 100644 --- a/builtin/branch.c +++ b/builtin/branch.c @@ -15,6 +15,7 @@ #include "branch.h" #include "diff.h" #include "revision.h" +#include "for-each-ref.h" static const char * const builtin_branch_usage[] = { "git branch [options] [-r | -a] [--merged | --no-merged]", @@ -30,6 +31,9 @@ static const char * const builtin_branch_usage[] = { static const char *head; static unsigned char head_sha1[20]; +static struct ref_sort *sort = NULL, **sort_tail = &sort; +static int maxcount; + static int branch_use_color = -1; static char branch_colors[][COLOR_MAXLEN] = { GIT_COLOR_RESET, @@ -312,7 +316,7 @@ static int append_ref(const char *refname, const unsigned char *sha1, int flags, if ((kind & ref_list->kinds) == 0) return 0; - if (!match_patterns(cb->pattern, refname)) + if (cb->pattern && !match_patterns(cb->pattern, refname)) return 0; commit = NULL; @@ -510,10 +514,38 @@ static void show_detached(struct ref_list *ref_list) } } -static int print_ref_list(int kinds, int detached, int verbose, int abbrev, struct commit_list *with_commit, const char **pattern) +static int fetch_branches(struct ref_list *ref_list, + const char **pattern) { - int i; struct append_ref_cb cb; + cb.ref_list = ref_list; + cb.pattern = pattern; + cb.ret = 0; + if (sort) { + struct grab_ref_cbdata cbdata; + int i; + memset(&cbdata, 0, sizeof(cbdata)); + cbdata.grab_pattern = pattern; + for_each_rawref(grab_single_ref, &cbdata); + sort_refs(sort, cbdata.grab_array, cbdata.grab_cnt); + for (i = 0; i < cbdata.grab_cnt; i++) { + struct refinfo *ri = cbdata.grab_array[i]; + append_ref(ri->refname, ri->objectname, ri->flag, &cb); + } + } + else { + for_each_rawref(append_ref, &cb); + qsort(ref_list->list, ref_list->index, + sizeof(struct ref_item), ref_cmp); + } + return cb.ret; +} + +static int print_ref_list(int kinds, int detached, int verbose, int abbrev, + struct commit_list *with_commit, + const char **pattern) +{ + int i, ret; struct ref_list ref_list; memset(&ref_list, 0, sizeof(ref_list)); @@ -523,10 +555,7 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru 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; - cb.ret = 0; - for_each_rawref(append_ref, &cb); + ret = fetch_branches(&ref_list, pattern); if (merge_filter != NO_FILTER) { struct commit *filter; filter = lookup_commit_reference_gently(merge_filter_ref, 0); @@ -539,13 +568,13 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru ref_list.maxwidth = calc_maxwidth(&ref_list); } - 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); - for (i = 0; i < ref_list.index; i++) { + if (!maxcount) + maxcount = ref_list.index; + for (i = 0; i < maxcount; i++) { int current = !detached && (ref_list.list[i].kind == REF_LOCAL_BRANCH) && !strcmp(ref_list.list[i].name, head); @@ -558,10 +587,10 @@ static int print_ref_list(int kinds, int detached, int verbose, int abbrev, stru free_ref_list(&ref_list); - if (cb.ret) + if (ret) error(_("some refs could not be read")); - return cb.ret; + return ret; } static void rename_branch(const char *oldname, const char *newname, int force) @@ -702,6 +731,9 @@ int cmd_branch(int argc, const char **argv, const char *prefix) parse_opt_with_commit, (intptr_t) "HEAD", }, OPT__ABBREV(&abbrev), + OPT_CALLBACK(0 , "sort", sort_tail, "key", + "field name to sort on", &opt_parse_sort), + OPT_INTEGER( 0 , "count", &maxcount, "show only <n> matched refs"), OPT_GROUP("Specific git-branch actions:"), OPT_SET_INT('a', "all", &kinds, "list both remote-tracking and local branches", @@ -752,6 +784,11 @@ int cmd_branch(int argc, const char **argv, const char *prefix) argc = parse_options(argc, argv, prefix, options, builtin_branch_usage, 0); + if (maxcount < 0) { + error("invalid --count argument: `%d'", maxcount); + usage_with_options(builtin_branch_usage, options); + } + if (!delete && !rename && !edit_description && argc == 0) list = 1; diff --git a/builtin/for-each-ref.c b/builtin/for-each-ref.c index b01d76a..7b25c54 100644 --- a/builtin/for-each-ref.c +++ b/builtin/for-each-ref.c @@ -9,6 +9,7 @@ #include "quote.h" #include "parse-options.h" #include "remote.h" +#include "for-each-ref.h" /* Quoting styles */ #define QUOTE_NONE 0 @@ -19,25 +20,6 @@ typedef enum { FIELD_STR, FIELD_ULONG, FIELD_TIME } cmp_type; -struct atom_value { - const char *s; - unsigned long ul; /* used for sorting when not FIELD_STR */ -}; - -struct ref_sort { - struct ref_sort *next; - int atom; /* index into used_atom array */ - unsigned reverse : 1; -}; - -struct refinfo { - char *refname; - unsigned char objectname[20]; - int flag; - const char *symref; - struct atom_value *value; -}; - static struct { const char *name; cmp_type cmp_type; @@ -765,17 +747,12 @@ static void get_value(struct refinfo *ref, int atom, struct atom_value **v) *v = &ref->value[atom]; } -struct grab_ref_cbdata { - struct refinfo **grab_array; - const char **grab_pattern; - int grab_cnt; -}; - /* * A call-back given to for_each_ref(). Filter refs and keep them for * later object processing. */ -static int grab_single_ref(const char *refname, const unsigned char *sha1, int flag, void *cb_data) +int grab_single_ref(const char *refname, const unsigned char *sha1, + int flag, void *cb_data) { struct grab_ref_cbdata *cb = cb_data; struct refinfo *ref; @@ -858,7 +835,7 @@ static int compare_refs(const void *a_, const void *b_) return 0; } -static void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs) +void sort_refs(struct ref_sort *sort, struct refinfo **refs, int num_refs) { ref_sort = sort; qsort(refs, num_refs, sizeof(struct refinfo *), compare_refs); @@ -953,7 +930,7 @@ static struct ref_sort *default_sort(void) return sort; } -static int opt_parse_sort(const struct option *opt, const char *arg, int unset) +int opt_parse_sort(const struct option *opt, const char *arg, int unset) { struct ref_sort **sort_tail = opt->value; struct ref_sort *s; diff --git a/builtin/for-each-ref.h b/builtin/for-each-ref.h new file mode 100644 index 0000000..8542d66 --- /dev/null +++ b/builtin/for-each-ref.h @@ -0,0 +1,32 @@ +struct atom_value { + const char *s; + unsigned long ul; /* used for sorting when not FIELD_STR */ +}; + +struct ref_sort { + struct ref_sort *next; + int atom; /* index into used_atom array */ + unsigned reverse : 1; +}; + +struct refinfo { + char *refname; + unsigned char objectname[20]; + int flag; + const char *symref; + struct atom_value *value; +}; + +struct grab_ref_cbdata { + struct refinfo **grab_array; + const char **grab_pattern; + int grab_cnt; +}; + +extern int grab_single_ref(const char *refname, + const unsigned char *sha1, + int flag, void *cb_data); +extern int opt_parse_sort(const struct option *opt, + const char *arg, int unset); +extern void sort_refs(struct ref_sort *sort, + struct refinfo **refs, int num_refs); -- 1.7.8.36.g69ee2 -- 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