Merge branch 'rs/parse-options-help-text-is-optional'
[alt-git.git] / refspec.c
blobd60932f4ded876476b7dec21456b9088260d0d1f
1 #include "git-compat-util.h"
2 #include "gettext.h"
3 #include "hash.h"
4 #include "hex.h"
5 #include "strvec.h"
6 #include "refs.h"
7 #include "refspec.h"
8 #include "strbuf.h"
10 static struct refspec_item s_tag_refspec = {
11 .force = 0,
12 .pattern = 1,
13 .matching = 0,
14 .exact_sha1 = 0,
15 .negative = 0,
16 .src = "refs/tags/*",
17 .dst = "refs/tags/*",
20 /* See TAG_REFSPEC for the string version */
21 const struct refspec_item *tag_refspec = &s_tag_refspec;
24 * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
25 * Returns 1 if successful and 0 if the refspec is invalid.
27 static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
29 size_t llen;
30 int is_glob;
31 const char *lhs, *rhs;
32 int flags;
34 is_glob = 0;
36 lhs = refspec;
37 if (*lhs == '+') {
38 item->force = 1;
39 lhs++;
40 } else if (*lhs == '^') {
41 item->negative = 1;
42 lhs++;
45 rhs = strrchr(lhs, ':');
47 /* negative refspecs only have one side */
48 if (item->negative && rhs)
49 return 0;
52 * Before going on, special case ":" (or "+:") as a refspec
53 * for pushing matching refs.
55 if (!fetch && rhs == lhs && rhs[1] == '\0') {
56 item->matching = 1;
57 return 1;
60 if (rhs) {
61 size_t rlen = strlen(++rhs);
62 is_glob = (1 <= rlen && strchr(rhs, '*'));
63 item->dst = xstrndup(rhs, rlen);
64 } else {
65 item->dst = NULL;
68 llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
69 if (1 <= llen && memchr(lhs, '*', llen)) {
70 if ((rhs && !is_glob) || (!rhs && !item->negative && fetch))
71 return 0;
72 is_glob = 1;
73 } else if (rhs && is_glob) {
74 return 0;
77 item->pattern = is_glob;
78 if (llen == 1 && *lhs == '@')
79 item->src = xstrdup("HEAD");
80 else
81 item->src = xstrndup(lhs, llen);
82 flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
84 if (item->negative) {
85 struct object_id unused;
88 * Negative refspecs only have a LHS, which indicates a ref
89 * (or pattern of refs) to exclude from other matches. This
90 * can either be a simple ref, or a glob pattern. Exact sha1
91 * match is not currently supported.
93 if (!*item->src)
94 return 0; /* negative refspecs must not be empty */
95 else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
96 return 0; /* negative refpsecs cannot be exact sha1 */
97 else if (!check_refname_format(item->src, flags))
98 ; /* valid looking ref is ok */
99 else
100 return 0;
102 /* the other rules below do not apply to negative refspecs */
103 return 1;
106 if (fetch) {
107 struct object_id unused;
109 /* LHS */
110 if (!*item->src)
111 ; /* empty is ok; it means "HEAD" */
112 else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
113 item->exact_sha1 = 1; /* ok */
114 else if (!check_refname_format(item->src, flags))
115 ; /* valid looking ref is ok */
116 else
117 return 0;
118 /* RHS */
119 if (!item->dst)
120 ; /* missing is ok; it is the same as empty */
121 else if (!*item->dst)
122 ; /* empty is ok; it means "do not store" */
123 else if (!check_refname_format(item->dst, flags))
124 ; /* valid looking ref is ok */
125 else
126 return 0;
127 } else {
129 * LHS
130 * - empty is allowed; it means delete.
131 * - when wildcarded, it must be a valid looking ref.
132 * - otherwise, it must be an extended SHA-1, but
133 * there is no existing way to validate this.
135 if (!*item->src)
136 ; /* empty is ok */
137 else if (is_glob) {
138 if (check_refname_format(item->src, flags))
139 return 0;
141 else
142 ; /* anything goes, for now */
144 * RHS
145 * - missing is allowed, but LHS then must be a
146 * valid looking ref.
147 * - empty is not allowed.
148 * - otherwise it must be a valid looking ref.
150 if (!item->dst) {
151 if (check_refname_format(item->src, flags))
152 return 0;
153 } else if (!*item->dst) {
154 return 0;
155 } else {
156 if (check_refname_format(item->dst, flags))
157 return 0;
161 return 1;
164 int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
166 memset(item, 0, sizeof(*item));
167 return parse_refspec(item, refspec, fetch);
170 void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
171 int fetch)
173 if (!refspec_item_init(item, refspec, fetch))
174 die(_("invalid refspec '%s'"), refspec);
177 void refspec_item_clear(struct refspec_item *item)
179 FREE_AND_NULL(item->src);
180 FREE_AND_NULL(item->dst);
181 item->force = 0;
182 item->pattern = 0;
183 item->matching = 0;
184 item->exact_sha1 = 0;
187 void refspec_init(struct refspec *rs, int fetch)
189 memset(rs, 0, sizeof(*rs));
190 rs->fetch = fetch;
193 static void refspec_append_nodup(struct refspec *rs, char *refspec)
195 struct refspec_item item;
197 refspec_item_init_or_die(&item, refspec, rs->fetch);
199 ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
200 rs->items[rs->nr++] = item;
202 ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
203 rs->raw[rs->raw_nr++] = refspec;
206 void refspec_append(struct refspec *rs, const char *refspec)
208 refspec_append_nodup(rs, xstrdup(refspec));
211 void refspec_appendf(struct refspec *rs, const char *fmt, ...)
213 va_list ap;
215 va_start(ap, fmt);
216 refspec_append_nodup(rs, xstrvfmt(fmt, ap));
217 va_end(ap);
220 void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
222 int i;
223 for (i = 0; i < nr; i++)
224 refspec_append(rs, refspecs[i]);
227 void refspec_clear(struct refspec *rs)
229 int i;
231 for (i = 0; i < rs->nr; i++)
232 refspec_item_clear(&rs->items[i]);
234 FREE_AND_NULL(rs->items);
235 rs->alloc = 0;
236 rs->nr = 0;
238 for (i = 0; i < rs->raw_nr; i++)
239 free((char *)rs->raw[i]);
240 FREE_AND_NULL(rs->raw);
241 rs->raw_alloc = 0;
242 rs->raw_nr = 0;
244 rs->fetch = 0;
247 int valid_fetch_refspec(const char *fetch_refspec_str)
249 struct refspec_item refspec;
250 int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
251 refspec_item_clear(&refspec);
252 return ret;
255 int valid_remote_name(const char *name)
257 int result;
258 struct strbuf refspec = STRBUF_INIT;
259 strbuf_addf(&refspec, "refs/heads/test:refs/remotes/%s/test", name);
260 result = valid_fetch_refspec(refspec.buf);
261 strbuf_release(&refspec);
262 return result;
265 void refspec_ref_prefixes(const struct refspec *rs,
266 struct strvec *ref_prefixes)
268 int i;
269 for (i = 0; i < rs->nr; i++) {
270 const struct refspec_item *item = &rs->items[i];
271 const char *prefix = NULL;
273 if (item->exact_sha1 || item->negative)
274 continue;
275 if (rs->fetch == REFSPEC_FETCH)
276 prefix = item->src;
277 else if (item->dst)
278 prefix = item->dst;
279 else if (item->src && !item->exact_sha1)
280 prefix = item->src;
282 if (!prefix)
283 continue;
285 if (item->pattern) {
286 const char *glob = strchr(prefix, '*');
287 strvec_pushf(ref_prefixes, "%.*s",
288 (int)(glob - prefix),
289 prefix);
290 } else {
291 expand_ref_prefix(ref_prefixes, prefix);