This allows more flexible pattern matching than the default globs. Signed-off-by: Mostyn Bramley-Moore <mostynb@xxxxxxxxx> --- Documentation/git-describe.txt | 15 +++++-- builtin/describe.c | 90 +++++++++++++++++++++++++++++++++++++++++- t/README | 3 +- t/t6120-describe.sh | 24 +++++++++-- 4 files changed, 123 insertions(+), 9 deletions(-) diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt index c8f28c8..b8279ec 100644 --- a/Documentation/git-describe.txt +++ b/Documentation/git-describe.txt @@ -81,9 +81,18 @@ OPTIONS that points at object deadbee....). --match <pattern>:: - Only consider tags matching the given `glob(7)` pattern, - excluding the "refs/tags/" prefix. This can be used to avoid - leaking private tags from the repository. + Only consider tags matching the given pattern, excluding the + "refs/tags/" prefix. This can be used to avoid leaking private + tags from the repository. The pattern type can be chosen by + specifying --glob or --perl-regexp. + +--glob:: + Use `glob(7)` patterns with --match, this is the default. + +-P:: +--perl-regexp:: + Use Perl-compatible regexp for patterns with --match. Requires + libpcre to be compiled in. --always:: Show uniquely abbreviated commit object as fallback. diff --git a/builtin/describe.c b/builtin/describe.c index 8a25abe..d9ab5bd 100644 --- a/builtin/describe.c +++ b/builtin/describe.c @@ -10,6 +10,10 @@ #include "hashmap.h" #include "argv-array.h" +#ifdef USE_LIBPCRE +#include <pcre.h> +#endif + #define SEEN (1u << 0) #define MAX_TAGS (FLAG_BITS - 1) @@ -19,6 +23,11 @@ static const char * const describe_usage[] = { NULL }; +enum match_type { + MATCH_GLOB = 0, + MATCH_PCRE +}; + static int debug; /* Display lots of verbose info */ static int all; /* Any valid ref can be used */ static int tags; /* Allow lightweight tags */ @@ -31,6 +40,12 @@ static int have_util; static const char *pattern; static int always; static const char *dirty; +static enum match_type pattern_type_arg = MATCH_GLOB; + +#ifdef USE_LIBPCRE +static pcre *pcre_regex = NULL; +static pcre_extra *extra = NULL; +#endif /* diff-index command arguments to check if working tree is dirty. */ static const char *diff_index_args[] = { @@ -119,6 +134,61 @@ static void add_to_known_names(const char *path, } } +#ifdef USE_LIBPCRE +static void pcre_init() +{ + const char *error = NULL; + int erroffset = -1; + int opts = PCRE_MULTILINE; + + pcre_regex = pcre_compile(pattern, opts, &error, &erroffset, NULL); + if (!pcre_regex) + die("invalid PCRE at position %d of \'%s\': %s", + erroffset, pattern, error); + + extra = pcre_study(pcre_regex, 0, &error); + if (!extra && error) + die("%s", error); +} + +static void pcre_cleanup() +{ + pcre_free(pcre_regex); + pcre_regex = NULL; + pcre_free(extra); + extra = NULL; +} + +/* return 1 on match, 0 on no match. */ +static int pcre_match(const char *text) +{ + int ovector[3], flags = 0; + int ret = pcre_exec(pcre_regex, extra, + text, strlen(text), 0, + flags, ovector, ARRAY_SIZE(ovector)); + if (ret < 0 && ret != PCRE_ERROR_NOMATCH) + die("pcre_exec failed with error code %d", ret); + else if (ret > 0) + return 0; /* no match */ + else + return 1; /* match */ +} +#endif + +/* Return 1 on match, 0 on no match. */ +static int match(const char *pattern, const char *text, enum match_type t) +{ + if (t == MATCH_GLOB) { + return wildmatch(pattern, text, 0, NULL); +#ifdef USE_LIBPCRE + } else if (t == MATCH_PCRE) { + return pcre_match(text); +#endif + } + + assert(0); /* None of the alternatives above were used. */ +} + static int get_name(const char *path, const struct object_id *oid, int flag, void *cb_data) { int is_tag = starts_with(path, "refs/tags/"); @@ -130,7 +200,7 @@ static int get_name(const char *path, const struct object_id *oid, int flag, voi return 0; /* Accept only tags that match the pattern, if given */ - if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL))) + if (pattern && (!is_tag || match(pattern, path + 10, pattern_type_arg))) return 0; /* Is it annotated? */ @@ -406,6 +476,14 @@ int cmd_describe(int argc, const char **argv, const char *prefix) N_("consider <n> most recent tags (default: 10)")), OPT_STRING(0, "match", &pattern, N_("pattern"), N_("only consider tags matching <pattern>")), + OPT_SET_INT(0, "glob", &pattern_type_arg, + N_("use glob patterns with --match (default)"), + MATCH_GLOB), +#ifdef USE_LIBPCRE + OPT_SET_INT('P', "perl-regexp", &pattern_type_arg, + N_("use Perl-compatible regular expressions with --match"), + MATCH_PCRE), +#endif OPT_BOOL(0, "always", &always, N_("show abbreviated commit object as fallback")), {OPTION_STRING, 0, "dirty", &dirty, N_("mark"), @@ -429,6 +507,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (longformat && abbrev == 0) die(_("--long is incompatible with --abbrev=0")); +#ifdef USE_LIBPCRE + if (pattern && pattern_type_arg == MATCH_PCRE) + pcre_init(); +#endif + if (contains) { struct argv_array args; @@ -455,6 +538,11 @@ int cmd_describe(int argc, const char **argv, const char *prefix) if (!names.size && !always) die(_("No names found, cannot describe anything.")); +#ifdef USE_LIBPCRE + if (pattern && pattern_type_arg == MATCH_PCRE) + pcre_cleanup(); +#endif + if (argc == 0) { if (dirty) { static struct lock_file index_lock; diff --git a/t/README b/t/README index 1dc908e..207257e 100644 --- a/t/README +++ b/t/README @@ -798,7 +798,8 @@ use these, and "test_set_prereq" for how to define your own. - LIBPCRE Git was compiled with USE_LIBPCRE=YesPlease. Wrap any tests - that use git-grep --perl-regexp or git-grep -P in these. + that use the --perl-regexp or -P flags to git-grep or + git-describe in these. - CASE_INSENSITIVE_FS diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh index 85f2694..47427c4 100755 --- a/t/t6120-describe.sh +++ b/t/t6120-describe.sh @@ -12,13 +12,14 @@ test_description='test describe ' . ./test-lib.sh -check_describe () { - expect="$1" - shift +_check_describe () { + pcre="$1" + expect="$2" + shift 2 R=$(git describe "$@" 2>err.actual) S=$? cat err.actual >&3 - test_expect_success "describe $*" ' + test_expect_success $pcre "describe $*" ' test $S = 0 && case "$R" in $expect) echo happy ;; @@ -28,6 +29,14 @@ check_describe () { ' } +check_describe() { + _check_describe "" $* +} + +check_describe_pcre() { + _check_describe LIBPCRE $* +} + test_expect_success setup ' test_tick && @@ -175,12 +184,19 @@ test_expect_success 'set-up matching pattern tests' ' ' check_describe "test-annotated-*" --match="test-*" +check_describe_pcre "test-annotated-*" --match="^test-" --perl-regexp check_describe "test1-lightweight-*" --tags --match="test1-*" +check_describe_pcre "test1-lightweight-*" --tags --match="^test1-" \ + --perl-regexp check_describe "test2-lightweight-*" --tags --match="test2-*" +check_describe_pcre "test2-lightweight-*" --tags --match="^test2-" \ + --perl-regexp check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^ +check_describe_pcre "test2-lightweight-*" --long --tags --match="^test2-" \ + --perl-regexp HEAD^ test_expect_success 'name-rev with exact tags' ' echo A >expect && -- 2.5.0 -- To unsubscribe from this list: send the line "unsubscribe git" in the body of a message to majordomo@xxxxxxxxxxxxxxx More majordomo info at http://vger.kernel.org/majordomo-info.html