versioncmp: generalize version sort suffix reordering
authorSZEDER Gábor <szeder.dev@gmail.com>
Thu, 8 Dec 2016 14:24:01 +0000 (15:24 +0100)
committerJunio C Hamano <gitster@pobox.com>
Thu, 12 Jan 2017 20:25:24 +0000 (12:25 -0800)
The 'versionsort.prereleaseSuffix' configuration variable, as its name
suggests, is supposed to only deal with tagnames with prerelease
suffixes, and allows sorting those prerelease tags in a user-defined
order before the suffixless main release tag, instead of sorting them
simply lexicographically.

However, the previous changes in this series resulted in an
interesting and useful property of version sort:

- The empty string as a configured suffix matches all tagnames,
including tagnames without any suffix, but

- tagnames containing a "real" configured suffix are still ordered
according to that real suffix, because any longer suffix takes
precedence over the empty string.

Exploiting this property we can easily generalize suffix reordering
and specify the order of tags with given suffixes not only before but
even after a main release tag by using the empty suffix to denote the
position of the main release tag, without any algorithm changes:

$ git -c versionsort.prereleaseSuffix=-alpha \
-c versionsort.prereleaseSuffix=-beta \
-c versionsort.prereleaseSuffix="" \
-c versionsort.prereleaseSuffix=-gamma \
-c versionsort.prereleaseSuffix=-delta \
tag -l --sort=version:refname 'v3.0*'
v3.0-alpha1
v3.0-beta1
v3.0
v3.0-gamma1
v3.0-delta1

Since 'versionsort.prereleaseSuffix' is not a fitting name for a
configuration variable to control this more general suffix reordering,
introduce the new variable 'versionsort.suffix'. Still keep the old
configuration variable name as a deprecated alias, though, to avoid
suddenly breaking setups already using it. Ignore the old variable if
both old and new configuration variables are set, but emit a warning
so users will be aware of it and can fix their configuration. Extend
the documentation to describe and add a test to check this more
general behavior.

Note: since the empty suffix matches all tagnames, tagnames with
suffixes not included in the configuration are listed together with
the suffixless main release tag, ordered lexicographically right after
that, i.e. before tags with suffixes listed in the configuration
following the empty suffix.

Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
Documentation/config.txt
Documentation/git-tag.txt
t/t7004-tag.sh
versioncmp.c
index 9078c8c4f4db140955c651c6e16f2f956042dbb8..0573f1f866570286cbb0a40f16e9bf5b6d0ad48c 100644 (file)
@@ -3038,16 +3038,32 @@ user.signingKey::
        This option is passed unchanged to gpg's --local-user parameter,
        so you may specify a key using any method that gpg supports.
 
-versionsort.prereleaseSuffix::
-       When version sort is used in linkgit:git-tag[1], prerelease
-       tags (e.g. "1.0-rc1") may appear after the main release
-       "1.0". By specifying the suffix "-rc" in this variable,
-       "1.0-rc1" will appear before "1.0".
-+
-This variable can be specified multiple times, once per suffix. The
-order of suffixes in the config file determines the sorting order
-(e.g. if "-pre" appears before "-rc" in the config file then 1.0-preXX
-is sorted before 1.0-rcXX).
+versionsort.prereleaseSuffix (deprecated)::
+       Deprecated alias for `versionsort.suffix`.  Ignored if
+       `versionsort.suffix` is set.
+
+versionsort.suffix::
+       Even when version sort is used in linkgit:git-tag[1], tagnames
+       with the same base version but different suffixes are still sorted
+       lexicographically, resulting e.g. in prerelease tags appearing
+       after the main release (e.g. "1.0-rc1" after "1.0").  This
+       variable can be specified to determine the sorting order of tags
+       with different suffixes.
++
+By specifying a single suffix in this variable, any tagname containing
+that suffix will appear before the corresponding main release.  E.g. if
+the variable is set to "-rc", then all "1.0-rcX" tags will appear before
+"1.0".  If specified multiple times, once per suffix, then the order of
+suffixes in the configuration will determine the sorting order of tagnames
+with those suffixes.  E.g. if "-pre" appears before "-rc" in the
+configuration, then all "1.0-preX" tags will be listed before any
+"1.0-rcX" tags.  The placement of the main release tag relative to tags
+with various suffixes can be determined by specifying the empty suffix
+among those other suffixes.  E.g. if the suffixes "-rc", "", "-ck" and
+"-bfs" appear in the configuration in this order, then all "v4.8-rcX" tags
+are listed first, followed by "v4.8", then "v4.8-ckX" and finally
+"v4.8-bfsX".
++
 If more than one suffixes match the same tagname, then that tagname will
 be sorted according to the suffix which starts at the earliest position in
 the tagname.  If more than one different matching suffixes start at
index 80019c584b11b35b2b14da83a31f326634bc6708..44c956c67bb490e3ead55ecd7aa9d1475282e08b 100644 (file)
@@ -101,8 +101,8 @@ OPTIONS
        multiple times, in which case the last key becomes the primary
        key. Also supports "version:refname" or "v:refname" (tag
        names are treated as versions). The "version:refname" sort
-       order can also be affected by the
-       "versionsort.prereleaseSuffix" configuration variable.
+       order can also be affected by the "versionsort.suffix"
+       configuration variable.
        The keys supported are the same as those in `git for-each-ref`.
        Sort order defaults to the value configured for the `tag.sort`
        variable if it exists, or lexicographic order otherwise. See
index e2efe312dcf0ac985817417dd78d5fe128bd8349..bdd28dad14d6f6dd7730e2fff6a82dc9308f3a80 100755 (executable)
@@ -1595,6 +1595,41 @@ test_expect_success 'version sort with prerelease reordering, multiple suffixes
        test_cmp expect actual
 '
 
+test_expect_success 'version sort with general suffix reordering' '
+       test_config versionsort.suffix -alpha &&
+       git config --add versionsort.suffix -beta &&
+       git config --add versionsort.suffix ""  &&
+       git config --add versionsort.suffix -gamma &&
+       git config --add versionsort.suffix -delta &&
+       git tag foo1.10-alpha &&
+       git tag foo1.10-beta &&
+       git tag foo1.10-gamma &&
+       git tag foo1.10-delta &&
+       git tag foo1.10-unlisted-suffix &&
+       git tag -l --sort=version:refname "foo1.10*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.10-alpha
+       foo1.10-beta
+       foo1.10
+       foo1.10-unlisted-suffix
+       foo1.10-gamma
+       foo1.10-delta
+       EOF
+       test_cmp expect actual
+'
+
+test_expect_success 'versionsort.suffix overrides versionsort.prereleaseSuffix' '
+       test_config versionsort.suffix -before &&
+       test_config versionsort.prereleaseSuffix -after &&
+       git tag -l --sort=version:refname "foo1.7*" >actual &&
+       cat >expect <<-\EOF &&
+       foo1.7-before1
+       foo1.7
+       foo1.7-after1
+       EOF
+       test_cmp expect actual
+'
+
 test_expect_success 'version sort with very long prerelease suffix' '
        test_config versionsort.prereleaseSuffix -very-looooooooooooooooooooooooong-prerelease-suffix &&
        git tag -l --sort=version:refname
index 4cb400f90150af1a64352e66008c23df5ae1e759..9f81dc1062bc1f7f243afa8c0404ff5100fc0ef8 100644 (file)
@@ -159,8 +159,15 @@ int versioncmp(const char *s1, const char *s2)
        }
 
        if (!initialized) {
+               const struct string_list *deprecated_prereleases;
                initialized = 1;
-               prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
+               prereleases = git_config_get_value_multi("versionsort.suffix");
+               deprecated_prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
+               if (prereleases) {
+                       if (deprecated_prereleases)
+                               warning("ignoring versionsort.prereleasesuffix because versionsort.suffix is set");
+               } else
+                       prereleases = deprecated_prereleases;
        }
        if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
                                            &diff))