Add a perl-compatible regular expression version of the --match option,
which allows more flexible pattern matching.

Signed-off-by: Mostyn Bramley-Moore <most...@opera.com>
---
 Documentation/git-describe.txt |  5 +++
 builtin/describe.c             | 69 ++++++++++++++++++++++++++++++++++++++++++
 t/README                       |  3 +-
 t/t6120-describe.sh            | 29 +++++++++++++++---
 4 files changed, 101 insertions(+), 5 deletions(-)

diff --git a/Documentation/git-describe.txt b/Documentation/git-describe.txt
index c8f28c8..f646560 100644
--- a/Documentation/git-describe.txt
+++ b/Documentation/git-describe.txt
@@ -85,6 +85,11 @@ OPTIONS
        excluding the "refs/tags/" prefix.  This can be used to avoid
        leaking private tags from the repository.
 
+--pcre-match <pattern>::
+       Only consider tags matching the given `PCRE` pattern,
+       excluding the "refs/tags/" prefix.  This can be used to avoid
+       leaking private tags from the repository.
+
 --always::
        Show uniquely abbreviated commit object as fallback.
 
diff --git a/builtin/describe.c b/builtin/describe.c
index 2386c64..7ceecd7 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)
 
@@ -32,6 +36,12 @@ static const char *pattern;
 static int always;
 static const char *dirty;
 
+#ifdef USE_LIBPCRE
+static pcre *regex = NULL;
+static pcre_extra *extra = NULL;
+static const char *pcre_pattern = NULL;
+#endif
+
 /* diff-index command arguments to check if working tree is dirty. */
 static const char *diff_index_args[] = {
        "diff-index", "--quiet", "HEAD", "--", NULL
@@ -119,6 +129,47 @@ 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;
+
+       regex = pcre_compile(pcre_pattern, opts, &error, &erroffset, NULL);
+       if (!regex)
+               die("invalid PCRE at position %d of \'%s\': %s",
+                       erroffset, pcre_pattern, error);
+
+       extra = pcre_study(regex, 0, &error);
+       if (!extra && error)
+               die("%s", error);
+}
+
+static void pcre_cleanup()
+{
+       pcre_free(regex);
+       regex = NULL;
+       pcre_free(extra);
+       extra = NULL;
+}
+
+/* return 1 on match, 0 on no match. */
+static int pcre_match(const char *pattern, const char *text)
+{
+       int ovector[30], flags = 0; // FIXME: ovector size ... ?
+       int ret = pcre_exec(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);
+       if (ret > 0)
+               return 0; /* no match */
+
+       return 1; /* match */
+}
+#endif
+
 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/");
@@ -132,6 +183,10 @@ static int get_name(const char *path, const struct 
object_id *oid, int flag, voi
        /* Accept only tags that match the pattern, if given */
        if (pattern && (!is_tag || wildmatch(pattern, path + 10, 0, NULL)))
                return 0;
+#ifdef USE_LIBPCRE
+       if (pcre_pattern && (!is_tag || pcre_match(pcre_pattern, path + 10)))
+               return 0;
+#endif
 
        /* Is it annotated? */
        if (!peel_ref(path, peeled.hash)) {
@@ -406,6 +461,10 @@ 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_("glob"),
                           N_("only consider tags matching <glob>")),
+#ifdef USE_LIBPCRE
+               OPT_STRING(0, "pcre-match",  &pcre_pattern, N_("regex"),
+                          N_("only consider tags matching PCRE <regex>")),
+#endif
                OPT_BOOL(0, "always",        &always,
                        N_("show abbreviated commit object as fallback")),
                {OPTION_STRING, 0, "dirty",  &dirty, N_("mark"),
@@ -429,6 +488,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 (pcre_pattern)
+               pcre_init();
+#endif
+
        if (contains) {
                struct argv_array args;
 
@@ -455,6 +519,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 (pcre_pattern)
+               pcre_cleanup();
+#endif
+
        if (argc == 0) {
                if (dirty) {
                        static struct lock_file index_lock;
diff --git a/t/README b/t/README
index 1dc908e..1495f59 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 git-grep --perl-regexp, git-grep -P or
+   git describe --pcre-matches in these.
 
  - CASE_INSENSITIVE_FS
 
diff --git a/t/t6120-describe.sh b/t/t6120-describe.sh
index 85f2694..8290dea 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,24 @@ test_expect_success 'set-up matching pattern tests' '
 '
 
 check_describe "test-annotated-*" --match="test-*"
+check_describe_pcre "test-annotated-*" --pcre-match="^test-"
+check_describe_pcre "test-annotated-*" --match="test-*" --pcre-match="^test-"
 
 check_describe "test1-lightweight-*" --tags --match="test1-*"
+check_describe_pcre "test1-lightweight-*" --tags --pcre-match="^test1-"
+check_describe_pcre "test1-lightweight-*" --tags --match="test1-*" \
+       --pcre-match="^test1-"
 
 check_describe "test2-lightweight-*" --tags --match="test2-*"
+check_describe_pcre "test2-lightweight-*" --tags --pcre-match="^test2-"
+check_describe_pcre "test2-lightweight-*" --tags --match="test2-*" \
+       --pcre-match="^test2-"
 
 check_describe "test2-lightweight-*" --long --tags --match="test2-*" HEAD^
+check_describe_pcre "test2-lightweight-*" --long --tags \
+       --pcre-match="^test2-" HEAD^
+check_describe_pcre "test2-lightweight-*" --long --tags --match="test2-*" \
+       --pcre-match="^test2-" 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 majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

Reply via email to