--sort=version sorts tags as versions. GNU extension's strverscmp is
used and no real compat implementation is provided so this is Linux only.

Signed-off-by: Nguyễn Thái Ngọc Duy <pclo...@gmail.com>
---
 I didn't know that coreutils' sort is simply a wrapper of strverscmp.
 With that GNU extension, implementing --sort is easy. Mac and Windows
 are welcome to reimplement strverscmp (of rip it off glibc).

 Documentation/git-tag.txt |  4 ++++
 builtin/tag.c             | 49 ++++++++++++++++++++++++++++++++++++++++++-----
 git-compat-util.h         |  7 +++++++
 3 files changed, 55 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-tag.txt b/Documentation/git-tag.txt
index 404257d..fc23dc0 100644
--- a/Documentation/git-tag.txt
+++ b/Documentation/git-tag.txt
@@ -95,6 +95,10 @@ 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 "version". Prepend
+       "-" to revert sort 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..db3567b 100644
--- a/builtin/tag.c
+++ b/builtin/tag.c
@@ -30,6 +30,8 @@ static const char * const git_tag_usage[] = {
 struct tag_filter {
        const char **patterns;
        int lines;
+       int version_sort;
+       struct string_list tags;
        struct commit_list *with_commit;
 };
 
@@ -166,7 +168,10 @@ static int show_reference(const char *refname, const 
unsigned char *sha1,
                        return 0;
 
                if (!filter->lines) {
-                       printf("%s\n", refname);
+                       if (filter->version_sort)
+                               string_list_append(&filter->tags, refname);
+                       else
+                               printf("%s\n", refname);
                        return 0;
                }
                printf("%-15s ", refname);
@@ -177,17 +182,38 @@ 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 version_sort)
 {
        struct tag_filter filter;
 
        filter.patterns = patterns;
        filter.lines = lines;
+       filter.version_sort = version_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 (version_sort) {
+               int i;
+               qsort(filter.tags.items, filter.tags.nr,
+                     sizeof(struct string_list_item), sort_by_version);
+               if (version_sort > 0)
+                       for (i = 0; i < filter.tags.nr; i++)
+                               printf("%s\n", filter.tags.items[i].string);
+               else
+                       for (i = filter.tags.nr - 1; i >= 0; i--)
+                               printf("%s\n", filter.tags.items[i].string);
+               string_list_clear(&filter.tags, 0);
+       }
        return 0;
 }
 
@@ -437,7 +463,8 @@ 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, version_sort = 0;
+       const char *sort = NULL;
        const char *msgfile = NULL, *keyid = NULL;
        struct msg_arg msg = { 0, STRBUF_INIT };
        struct commit_list *with_commit = NULL;
@@ -462,6 +489,7 @@ 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")),
+               OPT_STRING(0, "sort", &sort, N_("type"), N_("sort tags")),
 
                OPT_GROUP(N_("Tag listing options")),
                {
@@ -509,7 +537,18 @@ 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 (sort) {
+                       if (!strcmp(sort, "version"))
+                               version_sort = 1;
+                       else if (!strcmp(sort, "-version"))
+                               version_sort = -1;
+                       else
+                               die(_("unsupported sort type %s"), sort);
+               }
+               if (lines != -1 && version_sort)
+                       die(_("--sort and -l are incompatible"));
+               ret = list_tags(argv, lines == -1 ? 0 : lines, with_commit,
+                               version_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
-- 
1.9.0.40.gaa8c3ea

--
To unsubscribe from this list: send the line "unsubscribe git" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to