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
next prev 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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).