2023-04-22 20:17:17 +00:00
|
|
|
#include "git-compat-util.h"
|
2017-06-14 18:07:36 +00:00
|
|
|
#include "config.h"
|
2015-02-26 10:44:01 +00:00
|
|
|
#include "string-list.h"
|
2023-04-22 20:17:17 +00:00
|
|
|
#include "versioncmp.h"
|
2014-02-27 12:56:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* versioncmp(): copied from string/strverscmp.c in glibc commit
|
|
|
|
* ee9247c38a8def24a59eb5cfb7196a98bef8cfdc, reformatted to Git coding
|
|
|
|
* style. The implementation is under LGPL-2.1 and Git relicenses it
|
|
|
|
* to GPLv2.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*
|
|
|
|
* states: S_N: normal, S_I: comparing integral part, S_F: comparing
|
|
|
|
* fractionnal parts, S_Z: idem but with leading Zeroes only
|
|
|
|
*/
|
|
|
|
#define S_N 0x0
|
|
|
|
#define S_I 0x3
|
|
|
|
#define S_F 0x6
|
|
|
|
#define S_Z 0x9
|
|
|
|
|
|
|
|
/* result_type: CMP: return diff; LEN: compare using len_diff/diff */
|
|
|
|
#define CMP 2
|
|
|
|
#define LEN 3
|
|
|
|
|
2015-02-26 10:44:01 +00:00
|
|
|
static const struct string_list *prereleases;
|
|
|
|
static int initialized;
|
|
|
|
|
2016-12-08 14:48:11 +00:00
|
|
|
struct suffix_match {
|
|
|
|
int conf_pos;
|
|
|
|
int start;
|
|
|
|
int len;
|
|
|
|
};
|
|
|
|
|
|
|
|
static void find_better_matching_suffix(const char *tagname, const char *suffix,
|
|
|
|
int suffix_len, int start, int conf_pos,
|
|
|
|
struct suffix_match *match)
|
|
|
|
{
|
|
|
|
/*
|
|
|
|
* A better match either starts earlier or starts at the same offset
|
|
|
|
* but is longer.
|
|
|
|
*/
|
|
|
|
int end = match->len < suffix_len ? match->start : match->start-1;
|
|
|
|
int i;
|
|
|
|
for (i = start; i <= end; i++)
|
|
|
|
if (starts_with(tagname + i, suffix)) {
|
|
|
|
match->conf_pos = conf_pos;
|
|
|
|
match->start = i;
|
|
|
|
match->len = suffix_len;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-02-26 10:44:01 +00:00
|
|
|
/*
|
2016-12-08 14:23:58 +00:00
|
|
|
* off is the offset of the first different character in the two strings
|
versioncmp: cope with common part overlapping with prerelease suffix
Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames overlaps
with the leading character(s) of one or more configured prerelease
suffixes. Note the position of "v2.1.0-beta-1":
$ git -c versionsort.prereleaseSuffix=-beta \
tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2
The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function looks for a
configured prerelease suffix _starting at_ that character. Thus, when
in the above example the sorting algorithm happens to compare the
tagnames "v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() tries to
match the suffix "-beta" against "beta-1" to no avail, and the two
tagnames erroneously end up being ordered lexicographically.
To fix this issue change swap_prereleases() to look for configured
prerelease suffixes _containing_ the position of that first different
character.
Care must be taken, when a configured suffix is longer than the
tagnames' common part up to the first different character, to avoid
reading memory before the beginning of the tagnames. Add a test that
uses an exceptionally long prerelease suffix to check for this, in the
hope that in case of a regression the illegal memory access causes a
segfault in 'git tag' on one of the commonly used platforms (the test
happens to pass successfully on my Linux system with the safety check
removed), or at least makes valgrind complain.
Under some circumstances it's possible that more than one prerelease
suffixes can be found in the same tagname around that first different
character. With this simple bugfix patch such a tagname is sorted
according to the contained suffix that comes first in the
configuration for now. This is less than ideal in some cases, and the
following patch will take care of those.
Reported-by: Leho Kraav <leho@conversionready.com>
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:23:59 +00:00
|
|
|
* s1 and s2. If either s1 or s2 contains a prerelease suffix containing
|
versioncmp: use earliest-longest contained suffix to determine sorting order
When comparing tagnames, it is possible that a tagname contains more
than one of the configured prerelease suffixes around the first
different character. After fixing a bug in the previous commit such a
tagname is sorted according to the contained suffix which comes first
in the configuration. This is, however, not quite the right thing to
do in the following corner cases:
1. $ git -c versionsort.suffix=-bar
-c versionsort.suffix=-foo-baz
-c versionsort.suffix=-foo-bar
tag -l --sort=version:refname 'v1*'
v1.0-foo-bar
v1.0-foo-baz
The suffix of the tagname 'v1.0-foo-bar' is clearly '-foo-bar',
so it should be listed last. However, as it also contains '-bar'
around the first different character, it is listed first instead,
because that '-bar' suffix comes first the configuration.
2. One of the configured suffixes starts with the other:
$ git -c versionsort.prereleasesuffix=-pre \
-c versionsort.prereleasesuffix=-prerelease \
tag -l --sort=version:refname 'v2*'
v2.0-prerelease1
v2.0-pre1
v2.0-pre2
Here the tagname 'v2.0-prerelease1' should be the last. When
comparing 'v2.0-pre1' and 'v2.0-prerelease1' the first different
characters are '1' and 'r', respectively. Since this first
different character must be part of the configured suffix, the
'-pre' suffix is not recognized in the first tagname. OTOH, the
'-prerelease' suffix is properly recognized in
'v2.0-prerelease1', thus it is listed first.
Improve version sort in these corner cases, and
- look for a configured prerelease suffix containing the first
different character or ending right before it, so the '-pre'
suffixes are recognized in case (2). This also means that
when comparing tagnames 'v2.0-pre1' and 'v2.0-pre2',
swap_prereleases() would find the '-pre' suffix in both, but then
it will return "undecided" and the caller will do the right thing
by sorting based in '1' and '2'.
- If the tagname contains more than one suffix, then give precedence
to the contained suffix that starts at the earliest offset in the
tagname to address (1).
- If there are more than one suffixes starting at that earliest
position, then give precedence to the longest of those suffixes,
thus ensuring that in (2) the tagname 'v2.0-prerelease1' won't be
sorted based on the '-pre' suffix.
Add tests for these corner cases and adjust the documentation
accordingly.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:24:00 +00:00
|
|
|
* that offset or a suffix ends right before that offset, then that
|
|
|
|
* string will be forced to be on top.
|
2015-02-26 10:44:01 +00:00
|
|
|
*
|
versioncmp: cope with common part overlapping with prerelease suffix
Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames overlaps
with the leading character(s) of one or more configured prerelease
suffixes. Note the position of "v2.1.0-beta-1":
$ git -c versionsort.prereleaseSuffix=-beta \
tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2
The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function looks for a
configured prerelease suffix _starting at_ that character. Thus, when
in the above example the sorting algorithm happens to compare the
tagnames "v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() tries to
match the suffix "-beta" against "beta-1" to no avail, and the two
tagnames erroneously end up being ordered lexicographically.
To fix this issue change swap_prereleases() to look for configured
prerelease suffixes _containing_ the position of that first different
character.
Care must be taken, when a configured suffix is longer than the
tagnames' common part up to the first different character, to avoid
reading memory before the beginning of the tagnames. Add a test that
uses an exceptionally long prerelease suffix to check for this, in the
hope that in case of a regression the illegal memory access causes a
segfault in 'git tag' on one of the commonly used platforms (the test
happens to pass successfully on my Linux system with the safety check
removed), or at least makes valgrind complain.
Under some circumstances it's possible that more than one prerelease
suffixes can be found in the same tagname around that first different
character. With this simple bugfix patch such a tagname is sorted
according to the contained suffix that comes first in the
configuration for now. This is less than ideal in some cases, and the
following patch will take care of those.
Reported-by: Leho Kraav <leho@conversionready.com>
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:23:59 +00:00
|
|
|
* If both s1 and s2 contain a (different) suffix around that position,
|
2016-12-08 14:23:58 +00:00
|
|
|
* their order is determined by the order of those two suffixes in the
|
|
|
|
* configuration.
|
versioncmp: cope with common part overlapping with prerelease suffix
Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames overlaps
with the leading character(s) of one or more configured prerelease
suffixes. Note the position of "v2.1.0-beta-1":
$ git -c versionsort.prereleaseSuffix=-beta \
tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2
The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function looks for a
configured prerelease suffix _starting at_ that character. Thus, when
in the above example the sorting algorithm happens to compare the
tagnames "v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() tries to
match the suffix "-beta" against "beta-1" to no avail, and the two
tagnames erroneously end up being ordered lexicographically.
To fix this issue change swap_prereleases() to look for configured
prerelease suffixes _containing_ the position of that first different
character.
Care must be taken, when a configured suffix is longer than the
tagnames' common part up to the first different character, to avoid
reading memory before the beginning of the tagnames. Add a test that
uses an exceptionally long prerelease suffix to check for this, in the
hope that in case of a regression the illegal memory access causes a
segfault in 'git tag' on one of the commonly used platforms (the test
happens to pass successfully on my Linux system with the safety check
removed), or at least makes valgrind complain.
Under some circumstances it's possible that more than one prerelease
suffixes can be found in the same tagname around that first different
character. With this simple bugfix patch such a tagname is sorted
according to the contained suffix that comes first in the
configuration for now. This is less than ideal in some cases, and the
following patch will take care of those.
Reported-by: Leho Kraav <leho@conversionready.com>
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:23:59 +00:00
|
|
|
* If any of the strings contains more than one different suffixes around
|
|
|
|
* that position, then that string is sorted according to the contained
|
versioncmp: use earliest-longest contained suffix to determine sorting order
When comparing tagnames, it is possible that a tagname contains more
than one of the configured prerelease suffixes around the first
different character. After fixing a bug in the previous commit such a
tagname is sorted according to the contained suffix which comes first
in the configuration. This is, however, not quite the right thing to
do in the following corner cases:
1. $ git -c versionsort.suffix=-bar
-c versionsort.suffix=-foo-baz
-c versionsort.suffix=-foo-bar
tag -l --sort=version:refname 'v1*'
v1.0-foo-bar
v1.0-foo-baz
The suffix of the tagname 'v1.0-foo-bar' is clearly '-foo-bar',
so it should be listed last. However, as it also contains '-bar'
around the first different character, it is listed first instead,
because that '-bar' suffix comes first the configuration.
2. One of the configured suffixes starts with the other:
$ git -c versionsort.prereleasesuffix=-pre \
-c versionsort.prereleasesuffix=-prerelease \
tag -l --sort=version:refname 'v2*'
v2.0-prerelease1
v2.0-pre1
v2.0-pre2
Here the tagname 'v2.0-prerelease1' should be the last. When
comparing 'v2.0-pre1' and 'v2.0-prerelease1' the first different
characters are '1' and 'r', respectively. Since this first
different character must be part of the configured suffix, the
'-pre' suffix is not recognized in the first tagname. OTOH, the
'-prerelease' suffix is properly recognized in
'v2.0-prerelease1', thus it is listed first.
Improve version sort in these corner cases, and
- look for a configured prerelease suffix containing the first
different character or ending right before it, so the '-pre'
suffixes are recognized in case (2). This also means that
when comparing tagnames 'v2.0-pre1' and 'v2.0-pre2',
swap_prereleases() would find the '-pre' suffix in both, but then
it will return "undecided" and the caller will do the right thing
by sorting based in '1' and '2'.
- If the tagname contains more than one suffix, then give precedence
to the contained suffix that starts at the earliest offset in the
tagname to address (1).
- If there are more than one suffixes starting at that earliest
position, then give precedence to the longest of those suffixes,
thus ensuring that in (2) the tagname 'v2.0-prerelease1' won't be
sorted based on the '-pre' suffix.
Add tests for these corner cases and adjust the documentation
accordingly.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:24:00 +00:00
|
|
|
* suffix which starts at the earliest offset in that string.
|
|
|
|
* If more than one different contained suffixes start at that earliest
|
|
|
|
* offset, then that string is sorted according to the longest of those
|
|
|
|
* suffixes.
|
2015-02-26 10:44:01 +00:00
|
|
|
*
|
|
|
|
* Return non-zero if *diff contains the return value for versioncmp()
|
|
|
|
*/
|
2016-12-08 14:23:58 +00:00
|
|
|
static int swap_prereleases(const char *s1,
|
|
|
|
const char *s2,
|
|
|
|
int off,
|
2015-02-26 10:44:01 +00:00
|
|
|
int *diff)
|
|
|
|
{
|
2016-12-08 14:48:11 +00:00
|
|
|
int i;
|
|
|
|
struct suffix_match match1 = { -1, off, -1 };
|
|
|
|
struct suffix_match match2 = { -1, off, -1 };
|
2015-02-26 10:44:01 +00:00
|
|
|
|
|
|
|
for (i = 0; i < prereleases->nr; i++) {
|
|
|
|
const char *suffix = prereleases->items[i].string;
|
2016-12-08 14:48:11 +00:00
|
|
|
int start, suffix_len = strlen(suffix);
|
versioncmp: cope with common part overlapping with prerelease suffix
Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames overlaps
with the leading character(s) of one or more configured prerelease
suffixes. Note the position of "v2.1.0-beta-1":
$ git -c versionsort.prereleaseSuffix=-beta \
tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2
The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function looks for a
configured prerelease suffix _starting at_ that character. Thus, when
in the above example the sorting algorithm happens to compare the
tagnames "v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() tries to
match the suffix "-beta" against "beta-1" to no avail, and the two
tagnames erroneously end up being ordered lexicographically.
To fix this issue change swap_prereleases() to look for configured
prerelease suffixes _containing_ the position of that first different
character.
Care must be taken, when a configured suffix is longer than the
tagnames' common part up to the first different character, to avoid
reading memory before the beginning of the tagnames. Add a test that
uses an exceptionally long prerelease suffix to check for this, in the
hope that in case of a regression the illegal memory access causes a
segfault in 'git tag' on one of the commonly used platforms (the test
happens to pass successfully on my Linux system with the safety check
removed), or at least makes valgrind complain.
Under some circumstances it's possible that more than one prerelease
suffixes can be found in the same tagname around that first different
character. With this simple bugfix patch such a tagname is sorted
according to the contained suffix that comes first in the
configuration for now. This is less than ideal in some cases, and the
following patch will take care of those.
Reported-by: Leho Kraav <leho@conversionready.com>
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:23:59 +00:00
|
|
|
if (suffix_len < off)
|
versioncmp: use earliest-longest contained suffix to determine sorting order
When comparing tagnames, it is possible that a tagname contains more
than one of the configured prerelease suffixes around the first
different character. After fixing a bug in the previous commit such a
tagname is sorted according to the contained suffix which comes first
in the configuration. This is, however, not quite the right thing to
do in the following corner cases:
1. $ git -c versionsort.suffix=-bar
-c versionsort.suffix=-foo-baz
-c versionsort.suffix=-foo-bar
tag -l --sort=version:refname 'v1*'
v1.0-foo-bar
v1.0-foo-baz
The suffix of the tagname 'v1.0-foo-bar' is clearly '-foo-bar',
so it should be listed last. However, as it also contains '-bar'
around the first different character, it is listed first instead,
because that '-bar' suffix comes first the configuration.
2. One of the configured suffixes starts with the other:
$ git -c versionsort.prereleasesuffix=-pre \
-c versionsort.prereleasesuffix=-prerelease \
tag -l --sort=version:refname 'v2*'
v2.0-prerelease1
v2.0-pre1
v2.0-pre2
Here the tagname 'v2.0-prerelease1' should be the last. When
comparing 'v2.0-pre1' and 'v2.0-prerelease1' the first different
characters are '1' and 'r', respectively. Since this first
different character must be part of the configured suffix, the
'-pre' suffix is not recognized in the first tagname. OTOH, the
'-prerelease' suffix is properly recognized in
'v2.0-prerelease1', thus it is listed first.
Improve version sort in these corner cases, and
- look for a configured prerelease suffix containing the first
different character or ending right before it, so the '-pre'
suffixes are recognized in case (2). This also means that
when comparing tagnames 'v2.0-pre1' and 'v2.0-pre2',
swap_prereleases() would find the '-pre' suffix in both, but then
it will return "undecided" and the caller will do the right thing
by sorting based in '1' and '2'.
- If the tagname contains more than one suffix, then give precedence
to the contained suffix that starts at the earliest offset in the
tagname to address (1).
- If there are more than one suffixes starting at that earliest
position, then give precedence to the longest of those suffixes,
thus ensuring that in (2) the tagname 'v2.0-prerelease1' won't be
sorted based on the '-pre' suffix.
Add tests for these corner cases and adjust the documentation
accordingly.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:24:00 +00:00
|
|
|
start = off - suffix_len;
|
versioncmp: cope with common part overlapping with prerelease suffix
Version sort with prerelease reordering sometimes puts tagnames in the
wrong order, when the common part of two compared tagnames overlaps
with the leading character(s) of one or more configured prerelease
suffixes. Note the position of "v2.1.0-beta-1":
$ git -c versionsort.prereleaseSuffix=-beta \
tag -l --sort=version:refname v2.1.*
v2.1.0-beta-2
v2.1.0-beta-3
v2.1.0
v2.1.0-RC1
v2.1.0-RC2
v2.1.0-beta-1
v2.1.1
v2.1.2
The reason is that when comparing a pair of tagnames, first
versioncmp() looks for the first different character in a pair of
tagnames, and then the swap_prereleases() helper function looks for a
configured prerelease suffix _starting at_ that character. Thus, when
in the above example the sorting algorithm happens to compare the
tagnames "v2.1.0-beta-1" and "v2.1.0-RC2", swap_prereleases() tries to
match the suffix "-beta" against "beta-1" to no avail, and the two
tagnames erroneously end up being ordered lexicographically.
To fix this issue change swap_prereleases() to look for configured
prerelease suffixes _containing_ the position of that first different
character.
Care must be taken, when a configured suffix is longer than the
tagnames' common part up to the first different character, to avoid
reading memory before the beginning of the tagnames. Add a test that
uses an exceptionally long prerelease suffix to check for this, in the
hope that in case of a regression the illegal memory access causes a
segfault in 'git tag' on one of the commonly used platforms (the test
happens to pass successfully on my Linux system with the safety check
removed), or at least makes valgrind complain.
Under some circumstances it's possible that more than one prerelease
suffixes can be found in the same tagname around that first different
character. With this simple bugfix patch such a tagname is sorted
according to the contained suffix that comes first in the
configuration for now. This is less than ideal in some cases, and the
following patch will take care of those.
Reported-by: Leho Kraav <leho@conversionready.com>
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:23:59 +00:00
|
|
|
else
|
|
|
|
start = 0;
|
2016-12-08 14:48:11 +00:00
|
|
|
find_better_matching_suffix(s1, suffix, suffix_len, start,
|
|
|
|
i, &match1);
|
|
|
|
find_better_matching_suffix(s2, suffix, suffix_len, start,
|
|
|
|
i, &match2);
|
2015-02-26 10:44:01 +00:00
|
|
|
}
|
2016-12-08 14:48:11 +00:00
|
|
|
if (match1.conf_pos == -1 && match2.conf_pos == -1)
|
2015-02-26 10:44:01 +00:00
|
|
|
return 0;
|
2016-12-08 14:48:11 +00:00
|
|
|
if (match1.conf_pos == match2.conf_pos)
|
versioncmp: use earliest-longest contained suffix to determine sorting order
When comparing tagnames, it is possible that a tagname contains more
than one of the configured prerelease suffixes around the first
different character. After fixing a bug in the previous commit such a
tagname is sorted according to the contained suffix which comes first
in the configuration. This is, however, not quite the right thing to
do in the following corner cases:
1. $ git -c versionsort.suffix=-bar
-c versionsort.suffix=-foo-baz
-c versionsort.suffix=-foo-bar
tag -l --sort=version:refname 'v1*'
v1.0-foo-bar
v1.0-foo-baz
The suffix of the tagname 'v1.0-foo-bar' is clearly '-foo-bar',
so it should be listed last. However, as it also contains '-bar'
around the first different character, it is listed first instead,
because that '-bar' suffix comes first the configuration.
2. One of the configured suffixes starts with the other:
$ git -c versionsort.prereleasesuffix=-pre \
-c versionsort.prereleasesuffix=-prerelease \
tag -l --sort=version:refname 'v2*'
v2.0-prerelease1
v2.0-pre1
v2.0-pre2
Here the tagname 'v2.0-prerelease1' should be the last. When
comparing 'v2.0-pre1' and 'v2.0-prerelease1' the first different
characters are '1' and 'r', respectively. Since this first
different character must be part of the configured suffix, the
'-pre' suffix is not recognized in the first tagname. OTOH, the
'-prerelease' suffix is properly recognized in
'v2.0-prerelease1', thus it is listed first.
Improve version sort in these corner cases, and
- look for a configured prerelease suffix containing the first
different character or ending right before it, so the '-pre'
suffixes are recognized in case (2). This also means that
when comparing tagnames 'v2.0-pre1' and 'v2.0-pre2',
swap_prereleases() would find the '-pre' suffix in both, but then
it will return "undecided" and the caller will do the right thing
by sorting based in '1' and '2'.
- If the tagname contains more than one suffix, then give precedence
to the contained suffix that starts at the earliest offset in the
tagname to address (1).
- If there are more than one suffixes starting at that earliest
position, then give precedence to the longest of those suffixes,
thus ensuring that in (2) the tagname 'v2.0-prerelease1' won't be
sorted based on the '-pre' suffix.
Add tests for these corner cases and adjust the documentation
accordingly.
Signed-off-by: SZEDER Gábor <szeder.dev@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2016-12-08 14:24:00 +00:00
|
|
|
/* Found the same suffix in both, e.g. "-rc" in "v1.0-rcX"
|
|
|
|
* and "v1.0-rcY": the caller should decide based on "X"
|
|
|
|
* and "Y". */
|
|
|
|
return 0;
|
|
|
|
|
2016-12-08 14:48:11 +00:00
|
|
|
if (match1.conf_pos >= 0 && match2.conf_pos >= 0)
|
|
|
|
*diff = match1.conf_pos - match2.conf_pos;
|
|
|
|
else if (match1.conf_pos >= 0)
|
2015-02-26 10:44:01 +00:00
|
|
|
*diff = -1;
|
2016-12-08 14:48:11 +00:00
|
|
|
else /* if (match2.conf_pos >= 0) */
|
2015-02-26 10:44:01 +00:00
|
|
|
*diff = 1;
|
|
|
|
return 1;
|
|
|
|
}
|
2014-02-27 12:56:52 +00:00
|
|
|
|
|
|
|
/*
|
|
|
|
* Compare S1 and S2 as strings holding indices/version numbers,
|
|
|
|
* returning less than, equal to or greater than zero if S1 is less
|
|
|
|
* than, equal to or greater than S2 (for more info, see the texinfo
|
|
|
|
* doc).
|
|
|
|
*/
|
|
|
|
|
|
|
|
int versioncmp(const char *s1, const char *s2)
|
|
|
|
{
|
|
|
|
const unsigned char *p1 = (const unsigned char *) s1;
|
|
|
|
const unsigned char *p2 = (const unsigned char *) s2;
|
|
|
|
unsigned char c1, c2;
|
|
|
|
int state, diff;
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Symbol(s) 0 [1-9] others
|
|
|
|
* Transition (10) 0 (01) d (00) x
|
|
|
|
*/
|
|
|
|
static const uint8_t next_state[] = {
|
|
|
|
/* state x d 0 */
|
|
|
|
/* S_N */ S_N, S_I, S_Z,
|
|
|
|
/* S_I */ S_N, S_I, S_I,
|
|
|
|
/* S_F */ S_N, S_F, S_F,
|
|
|
|
/* S_Z */ S_N, S_F, S_Z
|
|
|
|
};
|
|
|
|
|
|
|
|
static const int8_t result_type[] = {
|
|
|
|
/* state x/x x/d x/0 d/x d/d d/0 0/x 0/d 0/0 */
|
|
|
|
|
|
|
|
/* S_N */ CMP, CMP, CMP, CMP, LEN, CMP, CMP, CMP, CMP,
|
|
|
|
/* S_I */ CMP, -1, -1, +1, LEN, LEN, +1, LEN, LEN,
|
|
|
|
/* S_F */ CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP, CMP,
|
|
|
|
/* S_Z */ CMP, +1, +1, -1, CMP, CMP, -1, CMP, CMP
|
|
|
|
};
|
|
|
|
|
|
|
|
if (p1 == p2)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
c1 = *p1++;
|
|
|
|
c2 = *p2++;
|
|
|
|
/* Hint: '0' is a digit too. */
|
|
|
|
state = S_N + ((c1 == '0') + (isdigit (c1) != 0));
|
|
|
|
|
|
|
|
while ((diff = c1 - c2) == 0) {
|
|
|
|
if (c1 == '\0')
|
|
|
|
return diff;
|
|
|
|
|
|
|
|
state = next_state[state];
|
|
|
|
c1 = *p1++;
|
|
|
|
c2 = *p2++;
|
|
|
|
state += (c1 == '0') + (isdigit (c1) != 0);
|
|
|
|
}
|
|
|
|
|
2015-02-26 10:44:01 +00:00
|
|
|
if (!initialized) {
|
2023-03-28 14:04:23 +00:00
|
|
|
const char *const newk = "versionsort.suffix";
|
|
|
|
const char *const oldk = "versionsort.prereleasesuffix";
|
2023-03-28 14:04:24 +00:00
|
|
|
const struct string_list *newl;
|
2023-03-28 14:04:23 +00:00
|
|
|
const struct string_list *oldl;
|
config API: add "string" version of *_value_multi(), fix segfaults
Fix numerous and mostly long-standing segfaults in consumers of
the *_config_*value_multi() API. As discussed in the preceding commit
an empty key in the config syntax yields a "NULL" string, which these
users would give to strcmp() (or similar), resulting in segfaults.
As this change shows, most users users of the *_config_*value_multi()
API didn't really want such an an unsafe and low-level API, let's give
them something with the safety of git_config_get_string() instead.
This fix is similar to what the *_string() functions and others
acquired in[1] and [2]. Namely introducing and using a safer
"*_get_string_multi()" variant of the low-level "_*value_multi()"
function.
This fixes segfaults in code introduced in:
- d811c8e17c6 (versionsort: support reorder prerelease suffixes, 2015-02-26)
- c026557a373 (versioncmp: generalize version sort suffix reordering, 2016-12-08)
- a086f921a72 (submodule: decouple url and submodule interest, 2017-03-17)
- a6be5e6764a (log: add log.excludeDecoration config option, 2020-04-16)
- 92156291ca8 (log: add default decoration filter, 2022-08-05)
- 50a044f1e40 (gc: replace config subprocesses with API calls, 2022-09-27)
There are now two users ofthe low-level API:
- One in "builtin/for-each-repo.c", which we'll convert in a
subsequent commit.
- The "t/helper/test-config.c" code added in [3].
As seen in the preceding commit we need to give the
"t/helper/test-config.c" caller these "NULL" entries.
We could also alter the underlying git_configset_get_value_multi()
function to be "string safe", but doing so would leave no room for
other variants of "*_get_value_multi()" that coerce to other types.
Such coercion can't be built on the string version, since as we've
established "NULL" is a true value in the boolean context, but if we
coerced it to "" for use in a list of strings it'll be subsequently
coerced to "false" as a boolean.
The callback pattern being used here will make it easy to introduce
e.g. a "multi" variant which coerces its values to "bool", "int",
"path" etc.
1. 40ea4ed9032 (Add config_error_nonbool() helper function,
2008-02-11)
2. 6c47d0e8f39 (config.c: guard config parser from value=NULL,
2008-02-11).
3. 4c715ebb96a (test-config: add tests for the config_set API,
2014-07-28)
Signed-off-by: Ævar Arnfjörð Bjarmason <avarab@gmail.com>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2023-03-28 14:04:27 +00:00
|
|
|
int new = git_config_get_string_multi(newk, &newl);
|
|
|
|
int old = git_config_get_string_multi(oldk, &oldl);
|
2023-03-28 14:04:23 +00:00
|
|
|
|
2023-03-28 14:04:24 +00:00
|
|
|
if (!new && !old)
|
2023-03-28 14:04:23 +00:00
|
|
|
warning("ignoring %s because %s is set", oldk, newk);
|
2023-03-28 14:04:24 +00:00
|
|
|
if (!new)
|
|
|
|
prereleases = newl;
|
|
|
|
else if (!old)
|
2023-03-28 14:04:23 +00:00
|
|
|
prereleases = oldl;
|
|
|
|
|
2015-02-26 10:44:01 +00:00
|
|
|
initialized = 1;
|
|
|
|
}
|
2016-12-08 14:23:58 +00:00
|
|
|
if (prereleases && swap_prereleases(s1, s2, (const char *) p1 - s1 - 1,
|
|
|
|
&diff))
|
2015-02-26 10:44:01 +00:00
|
|
|
return diff;
|
|
|
|
|
2014-02-27 12:56:52 +00:00
|
|
|
state = result_type[state * 3 + (((c2 == '0') + (isdigit (c2) != 0)))];
|
|
|
|
|
|
|
|
switch (state) {
|
|
|
|
case CMP:
|
|
|
|
return diff;
|
|
|
|
|
|
|
|
case LEN:
|
|
|
|
while (isdigit (*p1++))
|
|
|
|
if (!isdigit (*p2++))
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
return isdigit (*p2) ? -1 : diff;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return state;
|
|
|
|
}
|
|
|
|
}
|