versionsort: support reorder prerelease suffixes

Signed-off-by: Nguyễn Thái Ngọc Duy <pclouds@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>

Nguyễn Thái Ngọc Duy committed Feb 26, 2015 at 17:44 UTC d811c8e17c652e62a97e75abf80dfea5d81ef309
3 files changed +85
Documentation/config.txt
+7
@@ -2523,6 +2523,13 @@ user.signingkey::
2523 This option is passed unchanged to gpg's --local-user parameter,
2524 so you may specify a key using any method that gpg supports.
2525
2526 +versionsort.prereleaseSuffix::
2527 + When version sort is used in linkgit:git-tag[1], prerelease
2528 + tags (e.g. "1.0-rc1") may appear after the main release
2529 + "1.0". By specifying the suffix "-rc" in this variable,
2530 + "1.0-rc1" will appear before "1.0". One variable assignment
2531 + per suffix.
2532 +
2533 web.browser::
2534 Specify a web browser that may be used by some commands.
2535 Currently only linkgit:git-instaweb[1] and linkgit:git-help[1]
t/t7004-tag.sh
+28
@@ -1459,6 +1459,34 @@ test_expect_success 'invalid sort parameter in configuratoin' '
1459 test_cmp expect actual
1460 '
1461
1462 +test_expect_success 'version sort with prerelease reordering' '
1463 + git config --unset tag.sort &&
1464 + git config versionsort.prereleaseSuffix -rc &&
1465 + git tag foo1.6-rc1 &&
1466 + git tag foo1.6-rc2 &&
1467 + git tag -l --sort=version:refname "foo*" >actual &&
1468 + cat >expect <<-\EOF &&
1469 + foo1.3
1470 + foo1.6-rc1
1471 + foo1.6-rc2
1472 + foo1.6
1473 + foo1.10
1474 + EOF
1475 + test_cmp expect actual
1476 +'
1477 +
1478 +test_expect_success 'reverse version sort with prerelease reordering' '
1479 + git tag -l --sort=-version:refname "foo*" >actual &&
1480 + cat >expect <<-\EOF &&
1481 + foo1.10
1482 + foo1.6
1483 + foo1.6-rc2
1484 + foo1.6-rc1
1485 + foo1.3
1486 + EOF
1487 + test_cmp expect actual
1488 +'
1489 +
1490 run_with_limited_stack () {
1491 (ulimit -s 128 && "$@")
1492 }
versioncmp.c
+50
@@ -1,4 +1,5 @@
1 #include "cache.h"
2 +#include "string-list.h"
3
4 /*
5 * versioncmp(): copied from string/strverscmp.c in glibc commit
@@ -20,6 +21,48 @@
21 #define CMP 2
22 #define LEN 3
23
24 +static const struct string_list *prereleases;
25 +static int initialized;
26 +
27 +/*
28 + * p1 and p2 point to the first different character in two strings. If
29 + * either p1 or p2 starts with a prerelease suffix, it will be forced
30 + * to be on top.
31 + *
32 + * If both p1 and p2 start with (different) suffix, the order is
33 + * determined by config file.
34 + *
35 + * Note that we don't have to deal with the situation when both p1 and
36 + * p2 start with the same suffix because the common part is already
37 + * consumed by the caller.
38 + *
39 + * Return non-zero if *diff contains the return value for versioncmp()
40 + */
41 +static int swap_prereleases(const void *p1_,
42 + const void *p2_,
43 + int *diff)
44 +{
45 + const char *p1 = p1_;
46 + const char *p2 = p2_;
47 + int i, i1 = -1, i2 = -1;
48 +
49 + for (i = 0; i < prereleases->nr; i++) {
50 + const char *suffix = prereleases->items[i].string;
51 + if (i1 == -1 && starts_with(p1, suffix))
52 + i1 = i;
53 + if (i2 == -1 && starts_with(p2, suffix))
54 + i2 = i;
55 + }
56 + if (i1 == -1 && i2 == -1)
57 + return 0;
58 + if (i1 >= 0 && i2 >= 0)
59 + *diff = i1 - i2;
60 + else if (i1 >= 0)
61 + *diff = -1;
62 + else /* if (i2 >= 0) */
63 + *diff = 1;
64 + return 1;
65 +}
66
67 /*
68 * Compare S1 and S2 as strings holding indices/version numbers,
@@ -74,6 +117,13 @@ int versioncmp(const char *s1, const char *s2)
117 state += (c1 == '0') + (isdigit (c1) != 0);
118 }
119
120 + if (!initialized) {
121 + initialized = 1;
122 + prereleases = git_config_get_value_multi("versionsort.prereleasesuffix");
123 + }
124 + if (prereleases && swap_prereleases(p1 - 1, p2 - 1, &diff))
125 + return diff;
126 +
127 state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))];
128
129 switch (state) {