summaryrefslogtreecommitdiff
path: root/refspec.c
diff options
context:
space:
mode:
Diffstat (limited to 'refspec.c')
-rw-r--r--refspec.c90
1 files changed, 74 insertions, 16 deletions
diff --git a/refspec.c b/refspec.c
index 9a9bf21934..e3d852c0bf 100644
--- a/refspec.c
+++ b/refspec.c
@@ -1,5 +1,5 @@
#include "cache.h"
-#include "argv-array.h"
+#include "strvec.h"
#include "refs.h"
#include "refspec.h"
@@ -8,6 +8,7 @@ static struct refspec_item s_tag_refspec = {
1,
0,
0,
+ 0,
"refs/tags/*",
"refs/tags/*"
};
@@ -32,10 +33,17 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
if (*lhs == '+') {
item->force = 1;
lhs++;
+ } else if (*lhs == '^') {
+ item->negative = 1;
+ lhs++;
}
rhs = strrchr(lhs, ':');
+ /* negative refspecs only have one side */
+ if (item->negative && rhs)
+ return 0;
+
/*
* Before going on, special case ":" (or "+:") as a refspec
* for pushing matching refs.
@@ -55,7 +63,7 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
if (1 <= llen && memchr(lhs, '*', llen)) {
- if ((rhs && !is_glob) || (!rhs && fetch))
+ if ((rhs && !is_glob) || (!rhs && !item->negative && fetch))
return 0;
is_glob = 1;
} else if (rhs && is_glob) {
@@ -63,9 +71,34 @@ static int parse_refspec(struct refspec_item *item, const char *refspec, int fet
}
item->pattern = is_glob;
- item->src = xstrndup(lhs, llen);
+ if (llen == 1 && *lhs == '@')
+ item->src = xstrdup("HEAD");
+ else
+ item->src = xstrndup(lhs, llen);
flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
+ if (item->negative) {
+ struct object_id unused;
+
+ /*
+ * Negative refspecs only have a LHS, which indicates a ref
+ * (or pattern of refs) to exclude from other matches. This
+ * can either be a simple ref, or a glob pattern. Exact sha1
+ * match is not currently supported.
+ */
+ if (!*item->src)
+ return 0; /* negative refspecs must not be empty */
+ else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
+ return 0; /* negative refpsecs cannot be exact sha1 */
+ else if (!check_refname_format(item->src, flags))
+ ; /* valid looking ref is ok */
+ else
+ return 0;
+
+ /* the other rules below do not apply to negative refspecs */
+ return 1;
+ }
+
if (fetch) {
struct object_id unused;
@@ -153,7 +186,7 @@ void refspec_init(struct refspec *rs, int fetch)
rs->fetch = fetch;
}
-void refspec_append(struct refspec *rs, const char *refspec)
+static void refspec_append_nodup(struct refspec *rs, char *refspec)
{
struct refspec_item item;
@@ -163,7 +196,21 @@ void refspec_append(struct refspec *rs, const char *refspec)
rs->items[rs->nr++] = item;
ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
- rs->raw[rs->raw_nr++] = xstrdup(refspec);
+ rs->raw[rs->raw_nr++] = refspec;
+}
+
+void refspec_append(struct refspec *rs, const char *refspec)
+{
+ refspec_append_nodup(rs, xstrdup(refspec));
+}
+
+void refspec_appendf(struct refspec *rs, const char *fmt, ...)
+{
+ va_list ap;
+
+ va_start(ap, fmt);
+ refspec_append_nodup(rs, xstrvfmt(fmt, ap));
+ va_end(ap);
}
void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
@@ -201,15 +248,25 @@ int valid_fetch_refspec(const char *fetch_refspec_str)
return ret;
}
+int valid_remote_name(const char *name)
+{
+ int result;
+ struct strbuf refspec = STRBUF_INIT;
+ strbuf_addf(&refspec, "refs/heads/test:refs/remotes/%s/test", name);
+ result = valid_fetch_refspec(refspec.buf);
+ strbuf_release(&refspec);
+ return result;
+}
+
void refspec_ref_prefixes(const struct refspec *rs,
- struct argv_array *ref_prefixes)
+ struct strvec *ref_prefixes)
{
int i;
for (i = 0; i < rs->nr; i++) {
const struct refspec_item *item = &rs->items[i];
const char *prefix = NULL;
- if (item->exact_sha1)
+ if (item->exact_sha1 || item->negative)
continue;
if (rs->fetch == REFSPEC_FETCH)
prefix = item->src;
@@ -218,15 +275,16 @@ void refspec_ref_prefixes(const struct refspec *rs,
else if (item->src && !item->exact_sha1)
prefix = item->src;
- if (prefix) {
- if (item->pattern) {
- const char *glob = strchr(prefix, '*');
- argv_array_pushf(ref_prefixes, "%.*s",
- (int)(glob - prefix),
- prefix);
- } else {
- expand_ref_prefix(ref_prefixes, prefix);
- }
+ if (!prefix)
+ continue;
+
+ if (item->pattern) {
+ const char *glob = strchr(prefix, '*');
+ strvec_pushf(ref_prefixes, "%.*s",
+ (int)(glob - prefix),
+ prefix);
+ } else {
+ expand_ref_prefix(ref_prefixes, prefix);
}
}
}