All of lore.kernel.org
 help / color / mirror / Atom feed
From: "Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
To: git@vger.kernel.org
Cc: "Jeff King" <peff@peff.net>, "Junio C Hamano" <gitster@pobox.com>,
	"Nguyễn Thái Ngọc Duy" <pclouds@gmail.com>
Subject: [PATCH v2] tag: support --sort=<spec>
Date: Sat, 22 Feb 2014 10:29:22 +0700	[thread overview]
Message-ID: <1393039762-4843-1-git-send-email-pclouds@gmail.com> (raw)
In-Reply-To: <1392817167-29802-1-git-send-email-pclouds@gmail.com>

--sort=refname:version (or --sort=refname:v for short) sorts tags as
if they are versions. --sort=-refname reverses the order (with or
without ":version"). This syntax is chosen to make it compatible with
future extension in "for-each-ref --sort"

GNU extension strverscmp is used so this is Linux only. Mac and
Windows will need to bundle a compat implementation (and long term we
may want to use compat version only so we can make XXX-rc, XXX-pre...
appear before XXX)

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com>
---
 The new prereq GNULINUX is an ugly workaround until people provide
 strverscmp compat implementation. I hope that will happen soon as
 strverscmp.c does not look very complex.

 Documentation/git-tag.txt |  6 +++++
 builtin/tag.c             | 69 +++++++++++++++++++++++++++++++++++++++++++----
 git-compat-util.h         |  7 +++++
 t/t7004-tag.sh            | 43 +++++++++++++++++++++++++++++
 t/test-lib.sh             |  2 ++
 5 files changed, 122 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 404257d..d8633bb 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -95,6 +95,12 @@ OPTIONS
 	using fnmatch(3)).  Multiple patterns may be given; if any of
 	them matches, the tag is shown.
 
+--sort=<type>::
+	Sort in a specific order. Supported type is "refname"
+	(lexical order), "refname:version" or "refname:v" (tag names
+	are treated as version strings). Prepend "-" to reverse sorting
+	order.
+
 --column[=<options>]::
 --no-column::
 	Display tag listing in columns. See configuration variable
diff --git a/builtin/tag.c b/builtin/tag.c
index 74d3780..483d293 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -27,9 +27,16 @@ static const char * const git_tag_usage[] = {
 	NULL
 };
 
+#define STRCMP_SORT     0	/* must be zero */
+#define STRVERSCMP_SORT 1
+#define SORT_MASK       0x7fff
+#define REVERSE_SORT    0x8000
+
 struct tag_filter {
 	const char **patterns;
 	int lines;
+	int sort;
+	struct string_list tags;
 	struct commit_list *with_commit;
 };
 
@@ -166,7 +173,10 @@ static int show_reference(const char *refname, const unsigned char *sha1,
 			return 0;
 
 		if (!filter->lines) {
-			printf("%s\n", refname);
+			if (filter->sort)
+				string_list_append(&filter->tags, refname);
+			else
+				printf("%s\n", refname);
 			return 0;
 		}
 		printf("%-15s ", refname);
@@ -177,17 +187,39 @@ static int show_reference(const char *refname, const unsigned char *sha1,
 	return 0;
 }
 
+static int sort_by_version(const void *a_, const void *b_)
+{
+	const struct string_list_item *a = a_;
+	const struct string_list_item *b = b_;
+	return strverscmp(a->string, b->string);
+}
+
 static int list_tags(const char **patterns, int lines,
-			struct commit_list *with_commit)
+		     struct commit_list *with_commit, int sort)
 {
 	struct tag_filter filter;
 
 	filter.patterns = patterns;
 	filter.lines = lines;
+	filter.sort = sort;
 	filter.with_commit = with_commit;
+	memset(&filter.tags, 0, sizeof(filter.tags));
+	filter.tags.strdup_strings = 1;
 
 	for_each_tag_ref(show_reference, (void *) &filter);
-
+	if (sort) {
+		int i;
+		if ((sort & SORT_MASK) == STRVERSCMP_SORT)
+			qsort(filter.tags.items, filter.tags.nr,
+			      sizeof(struct string_list_item), sort_by_version);
+		if (sort & REVERSE_SORT)
+			for (i = filter.tags.nr - 1; i >= 0; i--)
+				printf("%s\n", filter.tags.items[i].string);
+		else
+			for (i = 0; i < filter.tags.nr; i++)
+				printf("%s\n", filter.tags.items[i].string);
+		string_list_clear(&filter.tags, 0);
+	}
 	return 0;
 }
 
@@ -427,6 +459,27 @@ static int parse_opt_points_at(const struct option *opt __attribute__((unused)),
 	return 0;
 }
 
+static int parse_opt_sort(const struct option *opt, const char *arg, int unset)
+{
+	int *sort = opt->value;
+	if (*arg == '-') {
+		*sort = REVERSE_SORT;
+		arg++;
+	} else
+		*sort = STRCMP_SORT;
+	if (!starts_with(arg, "refname") ||
+	    (arg[7] != ':' && arg[7] != '\0'))
+		die(_("unsupported sort field %s"), arg);
+	if (arg[7] == ':') {
+		const char *modifier = arg + 8;
+		if (!strcmp(modifier, "version") || !strcmp(modifier, "v"))
+			*sort |= STRVERSCMP_SORT;
+		else
+			die(_("unsupported modifier %s"), modifier);
+	}
+	return 0;
+}
+
 int cmd_tag(int argc, const char **argv, const char *prefix)
 {
 	struct strbuf buf = STRBUF_INIT;
@@ -437,7 +490,7 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 	struct create_tag_options opt;
 	char *cleanup_arg = NULL;
 	int annotate = 0, force = 0, lines = -1;
-	int cmdmode = 0;
+	int cmdmode = 0, sort = 0;
 	const char *msgfile = NULL, *keyid = NULL;
 	struct msg_arg msg = { 0, STRBUF_INIT };
 	struct commit_list *with_commit = NULL;
@@ -462,6 +515,10 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 					N_("use another key to sign the tag")),
 		OPT__FORCE(&force, N_("replace the tag if exists")),
 		OPT_COLUMN(0, "column", &colopts, N_("show tag list in columns")),
+		{
+			OPTION_CALLBACK, 0, "sort", &sort, N_("type"), N_("sort tags"),
+			PARSE_OPT_NONEG, parse_opt_sort
+		},
 
 		OPT_GROUP(N_("Tag listing options")),
 		{
@@ -509,7 +566,9 @@ int cmd_tag(int argc, const char **argv, const char *prefix)
 			copts.padding = 2;
 			run_column_filter(colopts, &copts);
 		}
-		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit);
+		if (lines != -1 && sort)
+			die(_("--sort and -n are incompatible"));
+		ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit, sort);
 		if (column_active(colopts))
 			stop_column_filter();
 		return ret;
diff --git a/git-compat-util.h b/git-compat-util.h
index cbd86c3..22089e9 100644
--- a/git-compat-util.h
+++ b/git-compat-util.h
@@ -721,4 +721,11 @@ void warn_on_inaccessible(const char *path);
 /* Get the passwd entry for the UID of the current process. */
 struct passwd *xgetpwuid_self(void);
 
+#ifndef __GNU_LIBRARY__
+static inline int strverscmp(const char *s1, const char *s2)
+{
+	die("strverscmp() not supported");
+}
+#endif
+
 #endif
diff --git a/t/t7004-tag.sh b/t/t7004-tag.sh
index c8d6e9f..0b7b170 100755
--- a/t/t7004-tag.sh
+++ b/t/t7004-tag.sh
@@ -1380,4 +1380,47 @@ test_expect_success 'multiple --points-at are OR-ed together' '
 	test_cmp expect actual
 '
 
+test_expect_success GNULINUX 'lexical sort' '
+	git tag foo1.3 &&
+	git tag foo1.6 &&
+	git tag foo1.10 &&
+	git tag -l --sort=refname "foo*" >actual &&
+	cat >expect <<EOF &&
+foo1.10
+foo1.3
+foo1.6
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success GNULINUX 'version sort' '
+	git tag -l --sort=refname:version "foo*" >actual &&
+	cat >expect <<EOF &&
+foo1.3
+foo1.6
+foo1.10
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success GNULINUX 'reverse version sort' '
+	git tag -l --sort=-refname:version "foo*" >actual &&
+	cat >expect <<EOF &&
+foo1.10
+foo1.6
+foo1.3
+EOF
+	test_cmp expect actual
+'
+
+test_expect_success GNULINUX 'reverse lexical sort' '
+	git tag -l --sort=-refname "foo*" >actual &&
+	cat >expect <<EOF &&
+foo1.6
+foo1.3
+foo1.10
+EOF
+	test_cmp expect actual
+'
+
 test_done
diff --git a/t/test-lib.sh b/t/test-lib.sh
index 1531c24..5e8c39a 100644
--- a/t/test-lib.sh
+++ b/t/test-lib.sh
@@ -771,6 +771,8 @@ case $(uname -s) in
 	;;
 esac
 
+[ "$(uname -o)" = "GNU/Linux" ] && test_set_prereq GNULINUX
+
 ( COLUMNS=1 && test $COLUMNS = 1 ) && test_set_prereq COLUMNS_CAN_BE_1
 test -z "$NO_PERL" && test_set_prereq PERL
 test -z "$NO_PYTHON" && test_set_prereq PYTHON
-- 
1.9.0.40.gaa8c3ea

  parent reply	other threads:[~2014-02-22  3:29 UTC|newest]

Thread overview: 25+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2014-02-19 13:39 [PATCH] tag: support --sort=version Nguyễn Thái Ngọc Duy
2014-02-19 14:09 ` Jeff King
2014-02-19 14:19   ` Duy Nguyen
2014-02-20 20:43     ` Jeff King
2014-02-21 11:58       ` Duy Nguyen
2014-02-21 17:48         ` Junio C Hamano
2014-02-22  7:59         ` Jeff King
2014-02-22  9:07           ` Duy Nguyen
2014-02-19 18:43 ` Eric Sunshine
2014-02-22  3:29 ` Nguyễn Thái Ngọc Duy [this message]
2014-02-22  8:04   ` [PATCH v2] tag: support --sort=<spec> Jeff King
2014-02-24 16:39     ` Junio C Hamano
2014-02-24 23:30       ` Duy Nguyen
2014-02-24 23:33         ` Jeff King
2014-02-25 12:22   ` [PATCH v3] " Nguyễn Thái Ngọc Duy
2014-02-25 17:42     ` Junio C Hamano
2014-02-26  9:05     ` Jeff King
2014-02-26 11:03       ` Duy Nguyen
2014-02-26 11:08         ` Jeff King
2014-02-26 11:11           ` Duy Nguyen
2014-02-26 11:17             ` Jeff King
2014-02-26 11:31           ` Duy Nguyen
2014-02-27 12:56     ` [PATCH v4] " Nguyễn Thái Ngọc Duy
2014-02-27 13:11       ` Jeff King
2014-02-27 13:23         ` Duy Nguyen

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1393039762-4843-1-git-send-email-pclouds@gmail.com \
    --to=pclouds@gmail.com \
    --cc=git@vger.kernel.org \
    --cc=gitster@pobox.com \
    --cc=peff@peff.net \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.