[PATCH] branch: borrow --sort and --count from for-each-ref

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



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


[Index of Archives]     [Linux Kernel Development]     [Gcc Help]     [IETF Annouce]     [DCCP]     [Netdev]     [Networking]     [Security]     [V4L]     [Bugtraq]     [Yosemite]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Linux SCSI]     [Fedora Users]