From: Michael Haggerty <mhagger@alum.mit.edu>
To: git@vger.kernel.org
Cc: Junio C Hamano <gitster@pobox.com>,
cmn@elego.de, Michael Haggerty <mhagger@alum.mit.edu>
Subject: [PATCH 4/6] Add a library function normalize_refname()
Date: Fri, 9 Sep 2011 13:46:16 +0200 [thread overview]
Message-ID: <1315568778-3592-5-git-send-email-mhagger@alum.mit.edu> (raw)
In-Reply-To: <1315568778-3592-1-git-send-email-mhagger@alum.mit.edu>
Implement check_ref_format() using the new function. Also use it to
replace collapse_slashes() in check-ref-format.c.
Signed-off-by: Michael Haggerty <mhagger@alum.mit.edu>
---
builtin/check-ref-format.c | 41 ++++-------------
refs.c | 109 +++++++++++++++++++++++++++----------------
refs.h | 24 +++++++---
t/t1402-check-ref-format.sh | 3 +
4 files changed, 98 insertions(+), 79 deletions(-)
diff --git a/builtin/check-ref-format.c b/builtin/check-ref-format.c
index c639400..4c202af 100644
--- a/builtin/check-ref-format.c
+++ b/builtin/check-ref-format.c
@@ -11,28 +11,6 @@ static const char builtin_check_ref_format_usage[] =
"git check-ref-format [--print] [options] <refname>\n"
" or: git check-ref-format --branch <branchname-shorthand>";
-/*
- * Remove leading slashes and replace each run of adjacent slashes in
- * src with a single slash, and write the result to dst.
- *
- * This function is similar to normalize_path_copy(), but stripped down
- * to meet check_ref_format's simpler needs.
- */
-static void collapse_slashes(char *dst, const char *src)
-{
- char ch;
- char prev = '/';
-
- while ((ch = *src++) != '\0') {
- if (prev == '/' && ch == prev)
- continue;
-
- *dst++ = ch;
- prev = ch;
- }
- *dst = '\0';
-}
-
static int check_ref_format_branch(const char *arg)
{
struct strbuf sb = STRBUF_INIT;
@@ -45,12 +23,15 @@ static int check_ref_format_branch(const char *arg)
return 0;
}
-static void refname_format_print(const char *arg)
+static int check_ref_format_print(const char *arg, int flags)
{
- char *refname = xmalloc(strlen(arg) + 1);
+ int refnamelen = strlen(arg) + 1;
+ char *refname = xmalloc(refnamelen);
- collapse_slashes(refname, arg);
+ if (normalize_refname(refname, refnamelen, arg, flags))
+ return 1;
printf("%s\n", refname);
+ return 0;
}
int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
@@ -79,12 +60,8 @@ int cmd_check_ref_format(int argc, const char **argv, const char *prefix)
}
if (! (i == argc - 1))
usage(builtin_check_ref_format_usage);
-
- if (check_ref_format(argv[i], flags))
- return 1;
-
if (print)
- refname_format_print(argv[i]);
-
- return 0;
+ return check_ref_format_print(argv[i], flags);
+ else
+ return !!check_ref_format(argv[i], flags);
}
diff --git a/refs.c b/refs.c
index a206a4c..372350e 100644
--- a/refs.c
+++ b/refs.c
@@ -872,55 +872,84 @@ static inline int bad_ref_char(int ch)
return 0;
}
-int check_ref_format(const char *ref, int flags)
+int normalize_refname(char *dst, int dstlen, const char *ref, int flags)
{
- int ch, level, last;
- const char *cp = ref;
-
- level = 0;
- while (1) {
- while ((ch = *cp++) == '/')
- ; /* tolerate duplicated slashes */
- if (!ch)
- /* should not end with slashes */
- return -1;
+ int ch, last, component_len, component_count = 0;
+ const char *cp = ref, *component;
- /* we are at the beginning of the path component */
- if (ch == '.')
- return -1;
- if (bad_ref_char(ch)) {
- if ((flags & REFNAME_REFSPEC_PATTERN) && ch == '*' &&
- (!*cp || *cp == '/'))
- /* Accept one wildcard as a full refname component. */
- flags &= ~REFNAME_REFSPEC_PATTERN;
- else
- return -1;
- }
+ ch = *cp;
+ do {
+ while (ch == '/')
+ ch = *++cp; /* tolerate leading and repeated slashes */
- last = ch;
- /* scan the rest of the path component */
- while ((ch = *cp++) != 0) {
- if (bad_ref_char(ch))
- return -1;
- if (ch == '/')
- break;
+ /*
+ * We are at the start of a path component. Record
+ * its start for later reference. If we are copying
+ * to dst, use the copy there, because we might be
+ * overwriting ref; otherwise, use the copy from the
+ * input string.
+ */
+ component = dst ? dst : cp;
+ component_len = 0;
+ last = '\0';
+ while (1) {
+ if (ch != 0 && bad_ref_char(ch)) {
+ if ((flags & REFNAME_REFSPEC_PATTERN) &&
+ ch == '*' &&
+ component_len == 0 &&
+ (cp[1] == 0 || cp[1] == '/')) {
+ /* Accept one wildcard as a full refname component. */
+ flags &= ~REFNAME_REFSPEC_PATTERN;
+ } else {
+ /* Illegal character in refname */
+ return -1;
+ }
+ }
if (last == '.' && ch == '.')
+ /* Refname must not contain "..". */
return -1;
if (last == '@' && ch == '{')
+ /* Refname must not contain "@{". */
return -1;
+ if (dst) {
+ if (dstlen-- <= 0)
+ /* Output array was too small. */
+ return -1;
+ *dst++ = ch;
+ }
+ if (ch == 0 || ch == '/')
+ break;
+ ++component_len;
last = ch;
+ ch = *++cp;
}
- level++;
- if (!ch) {
- if (ref <= cp - 2 && cp[-2] == '.')
- return -1;
- if (level < 2 && !(flags & REFNAME_ALLOW_ONELEVEL))
- return -1;
- if (has_extension(ref, ".lock"))
- return -1;
- return 0;
- }
- }
+
+ /* We are at the end of a path component. */
+ ++component_count;
+ if (component_len == 0)
+ /* Either ref was zero length or it ended with slash. */
+ return -1;
+
+ if (component[0] == '.')
+ /* Components must not start with '.'. */
+ return -1;
+ } while (ch != 0);
+
+ if (last == '.')
+ /* Refname must not end with '.'. */
+ return -1;
+ if (component_len >= 5 && !memcmp(&component[component_len - 5], ".lock", 5))
+ /* Refname must not end with ".lock". */
+ return -1;
+ if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2)
+ /* Refname must have at least two components. */
+ return -1;
+ return 0;
+}
+
+int check_ref_format(const char *ref, int flags)
+{
+ return normalize_refname(NULL, 0, ref, flags);
}
const char *prettify_refname(const char *name)
diff --git a/refs.h b/refs.h
index b248ce6..8a15f83 100644
--- a/refs.h
+++ b/refs.h
@@ -101,14 +101,24 @@ extern int for_each_reflog(each_ref_fn, void *);
#define REFNAME_REFSPEC_PATTERN 2
/*
- * Return 0 iff ref has the correct format for a refname according to
- * the rules described in Documentation/git-check-ref-format.txt. If
- * REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level
- * reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then
- * allow a "*" wildcard character in place of one of the name
- * components.
+ * Check that ref is a valid refname according to the rules described
+ * in Documentation/git-check-ref-format.txt and normalize it by
+ * stripping out superfluous "/" characters. If dst != NULL, write
+ * the normalized refname to dst, which must be an allocated character
+ * array with length dstlen (typically at least as long as ref). dst
+ * may point at the same memory as ref. Return 0 iff the refname was
+ * OK and fit into dst. If REFNAME_ALLOW_ONELEVEL is set in flags,
+ * then accept one-level reference names. If REFNAME_REFSPEC_PATTERN
+ * is set in flags, then allow a "*" wildcard characters in place of
+ * one of the name components.
*/
-extern int check_ref_format(const char *target, int flags);
+extern int normalize_refname(char *dst, int dstlen, const char *ref, int flags);
+
+/*
+ * Return 0 iff ref has the correct format for a refname. See
+ * normalize_refname() for details.
+ */
+extern int check_ref_format(const char *ref, int flags);
extern const char *prettify_refname(const char *refname);
extern char *shorten_unambiguous_ref(const char *ref, int strict);
diff --git a/t/t1402-check-ref-format.sh b/t/t1402-check-ref-format.sh
index 6ac5025..20a7782 100755
--- a/t/t1402-check-ref-format.sh
+++ b/t/t1402-check-ref-format.sh
@@ -39,6 +39,7 @@ invalid_ref 'heads/foo..bar'
invalid_ref 'heads/foo?bar'
valid_ref 'foo./bar'
invalid_ref 'heads/foo.lock'
+invalid_ref 'heads///foo.lock'
valid_ref 'heads/foo@bar'
invalid_ref 'heads/v@{ation'
invalid_ref 'heads/foo\bar'
@@ -152,5 +153,7 @@ invalid_ref_normalized '/foo'
invalid_ref_normalized 'heads/foo/../bar'
invalid_ref_normalized 'heads/./foo'
invalid_ref_normalized 'heads\foo'
+invalid_ref_normalized 'heads/foo.lock'
+invalid_ref_normalized 'heads///foo.lock'
test_done
--
1.7.6.8.gd2879
next prev parent reply other threads:[~2011-09-09 11:46 UTC|newest]
Thread overview: 13+ messages / expand[flat|nested] mbox.gz Atom feed top
2011-09-09 11:46 [PATCH 0/6] Improved infrastructure for refname normalization Michael Haggerty
2011-09-09 11:46 ` [PATCH 1/6] Change bad_ref_char() to return a boolean value Michael Haggerty
2011-09-09 11:46 ` [PATCH 2/6] git check-ref-format: add options --onelevel-ok and --refname-pattern Michael Haggerty
2011-09-09 11:46 ` [PATCH 3/6] Change check_ref_format() to take a flags argument Michael Haggerty
2011-09-09 11:46 ` Michael Haggerty [this message]
2011-09-09 11:46 ` [PATCH 5/6] Do not allow ".lock" at the end of any refname component Michael Haggerty
2011-09-09 11:46 ` [PATCH 6/6] Add a REFNAME_ALLOW_UNNORMALIZED flag to check_ref_format() Michael Haggerty
2011-09-09 23:30 ` Junio C Hamano
2011-09-10 4:04 ` Michael Haggerty
2011-09-09 14:06 ` [PATCH 0/6] Improved infrastructure for refname normalization A Large Angry SCM
2011-09-09 15:33 ` Michael Haggerty
2011-09-09 17:57 ` Junio C Hamano
2011-09-10 3:31 ` Michael Haggerty
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=1315568778-3592-5-git-send-email-mhagger@alum.mit.edu \
--to=mhagger@alum.mit.edu \
--cc=cmn@elego.de \
--cc=git@vger.kernel.org \
--cc=gitster@pobox.com \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).