blob: d60932f4ded876476b7dec21456b9088260d0d1f [file] [log] [blame]
Elijah Newren36bf1952023-02-24 00:09:24 +00001#include "git-compat-util.h"
Elijah Newrena6dc3d32023-03-21 06:25:53 +00002#include "gettext.h"
Elijah Newrend1cbe1e2023-04-22 20:17:20 +00003#include "hash.h"
Elijah Newren41771fa2023-02-24 00:09:27 +00004#include "hex.h"
Jeff Kingdbbcd442020-07-28 16:23:39 -04005#include "strvec.h"
Brandon Williamsec0cb492018-05-16 15:57:48 -07006#include "refs.h"
7#include "refspec.h"
Elijah Newrend4a4f922023-04-22 20:17:26 +00008#include "strbuf.h"
Brandon Williamsec0cb492018-05-16 15:57:48 -07009
Brandon Williams0ad4a5f2018-05-16 15:57:49 -070010static struct refspec_item s_tag_refspec = {
Ævar Arnfjörð Bjarmasonb3454e22022-02-24 10:33:05 +010011 .force = 0,
12 .pattern = 1,
13 .matching = 0,
14 .exact_sha1 = 0,
15 .negative = 0,
16 .src = "refs/tags/*",
17 .dst = "refs/tags/*",
Brandon Williamsec0cb492018-05-16 15:57:48 -070018};
19
20/* See TAG_REFSPEC for the string version */
Brandon Williams0ad4a5f2018-05-16 15:57:49 -070021const struct refspec_item *tag_refspec = &s_tag_refspec;
Brandon Williamsec0cb492018-05-16 15:57:48 -070022
Brandon Williams3eec3702018-05-16 15:57:50 -070023/*
24 * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
25 * Returns 1 if successful and 0 if the refspec is invalid.
26 */
27static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
28{
29 size_t llen;
30 int is_glob;
31 const char *lhs, *rhs;
32 int flags;
33
34 is_glob = 0;
35
36 lhs = refspec;
37 if (*lhs == '+') {
38 item->force = 1;
39 lhs++;
Jacob Kellerc0192df2020-09-30 14:25:29 -070040 } else if (*lhs == '^') {
41 item->negative = 1;
42 lhs++;
Brandon Williams3eec3702018-05-16 15:57:50 -070043 }
44
45 rhs = strrchr(lhs, ':');
46
Jacob Kellerc0192df2020-09-30 14:25:29 -070047 /* negative refspecs only have one side */
48 if (item->negative && rhs)
49 return 0;
50
Brandon Williams3eec3702018-05-16 15:57:50 -070051 /*
52 * Before going on, special case ":" (or "+:") as a refspec
53 * for pushing matching refs.
54 */
55 if (!fetch && rhs == lhs && rhs[1] == '\0') {
56 item->matching = 1;
57 return 1;
58 }
59
60 if (rhs) {
61 size_t rlen = strlen(++rhs);
62 is_glob = (1 <= rlen && strchr(rhs, '*'));
63 item->dst = xstrndup(rhs, rlen);
Junio C Hamanoc3072c62018-06-01 11:33:19 +090064 } else {
65 item->dst = NULL;
Brandon Williams3eec3702018-05-16 15:57:50 -070066 }
67
68 llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
69 if (1 <= llen && memchr(lhs, '*', llen)) {
Jacob Kellerc0192df2020-09-30 14:25:29 -070070 if ((rhs && !is_glob) || (!rhs && !item->negative && fetch))
Brandon Williams3eec3702018-05-16 15:57:50 -070071 return 0;
72 is_glob = 1;
73 } else if (rhs && is_glob) {
74 return 0;
75 }
76
77 item->pattern = is_glob;
Felipe Contreras374fbae2020-11-25 18:16:16 -060078 if (llen == 1 && *lhs == '@')
79 item->src = xstrdup("HEAD");
80 else
81 item->src = xstrndup(lhs, llen);
Brandon Williams3eec3702018-05-16 15:57:50 -070082 flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
83
Jacob Kellerc0192df2020-09-30 14:25:29 -070084 if (item->negative) {
85 struct object_id unused;
86
87 /*
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.
92 */
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;
101
102 /* the other rules below do not apply to negative refspecs */
103 return 1;
104 }
105
Brandon Williams3eec3702018-05-16 15:57:50 -0700106 if (fetch) {
107 struct object_id unused;
108
109 /* LHS */
110 if (!*item->src)
111 ; /* empty is ok; it means "HEAD" */
brian m. carlsonb8d45d02019-02-19 00:05:21 +0000112 else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
Brandon Williams3eec3702018-05-16 15:57:50 -0700113 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 {
128 /*
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.
134 */
135 if (!*item->src)
136 ; /* empty is ok */
137 else if (is_glob) {
138 if (check_refname_format(item->src, flags))
139 return 0;
140 }
141 else
142 ; /* anything goes, for now */
143 /*
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.
149 */
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;
158 }
159 }
160
161 return 1;
162}
163
Ævar Arnfjörð Bjarmasonc495fd32018-06-05 19:54:39 +0000164int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
Brandon Williams6d4c0572018-05-16 15:57:51 -0700165{
166 memset(item, 0, sizeof(*item));
Ævar Arnfjörð Bjarmasonc495fd32018-06-05 19:54:39 +0000167 return parse_refspec(item, refspec, fetch);
168}
Brandon Williams6d4c0572018-05-16 15:57:51 -0700169
Ævar Arnfjörð Bjarmasondc064222018-06-05 19:54:38 +0000170void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
171 int fetch)
Brandon Williams6d4c0572018-05-16 15:57:51 -0700172{
Ævar Arnfjörð Bjarmasonc495fd32018-06-05 19:54:39 +0000173 if (!refspec_item_init(item, refspec, fetch))
Nguyễn Thái Ngọc Duy1b5e07b2018-07-21 09:49:36 +0200174 die(_("invalid refspec '%s'"), refspec);
Brandon Williams6d4c0572018-05-16 15:57:51 -0700175}
176
177void refspec_item_clear(struct refspec_item *item)
178{
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;
185}
186
187void refspec_init(struct refspec *rs, int fetch)
188{
189 memset(rs, 0, sizeof(*rs));
190 rs->fetch = fetch;
191}
192
René Scharfe1af8b8c2020-09-05 16:49:30 +0200193static void refspec_append_nodup(struct refspec *rs, char *refspec)
Brandon Williams6d4c0572018-05-16 15:57:51 -0700194{
195 struct refspec_item item;
196
Ævar Arnfjörð Bjarmasondc064222018-06-05 19:54:38 +0000197 refspec_item_init_or_die(&item, refspec, rs->fetch);
Brandon Williams6d4c0572018-05-16 15:57:51 -0700198
199 ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
200 rs->items[rs->nr++] = item;
201
202 ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
René Scharfe1af8b8c2020-09-05 16:49:30 +0200203 rs->raw[rs->raw_nr++] = refspec;
204}
205
206void refspec_append(struct refspec *rs, const char *refspec)
207{
208 refspec_append_nodup(rs, xstrdup(refspec));
209}
210
211void refspec_appendf(struct refspec *rs, const char *fmt, ...)
212{
213 va_list ap;
214
215 va_start(ap, fmt);
216 refspec_append_nodup(rs, xstrvfmt(fmt, ap));
217 va_end(ap);
Brandon Williams6d4c0572018-05-16 15:57:51 -0700218}
219
220void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
221{
222 int i;
223 for (i = 0; i < nr; i++)
224 refspec_append(rs, refspecs[i]);
225}
226
227void refspec_clear(struct refspec *rs)
228{
229 int i;
230
231 for (i = 0; i < rs->nr; i++)
232 refspec_item_clear(&rs->items[i]);
233
234 FREE_AND_NULL(rs->items);
235 rs->alloc = 0;
236 rs->nr = 0;
237
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;
243
244 rs->fetch = 0;
245}
Brandon Williamsc8fa9ef2018-05-16 15:57:52 -0700246
247int valid_fetch_refspec(const char *fetch_refspec_str)
248{
249 struct refspec_item refspec;
Martin Ågren7865d152018-06-05 19:54:40 +0000250 int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
Brandon Williamsc8fa9ef2018-05-16 15:57:52 -0700251 refspec_item_clear(&refspec);
252 return ret;
253}
Brandon Williams6373cb52018-05-16 16:48:21 -0700254
Sean Baragf2c6fda2020-10-01 03:46:13 +0000255int valid_remote_name(const char *name)
256{
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;
263}
264
Brandon Williams6373cb52018-05-16 16:48:21 -0700265void refspec_ref_prefixes(const struct refspec *rs,
Jeff Kingc972bf42020-07-28 16:25:12 -0400266 struct strvec *ref_prefixes)
Brandon Williams6373cb52018-05-16 16:48:21 -0700267{
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;
272
Jacob Kellerc0192df2020-09-30 14:25:29 -0700273 if (item->exact_sha1 || item->negative)
Jonathan Nieder6c301ad2018-05-31 00:23:39 -0700274 continue;
Brandon Williams6373cb52018-05-16 16:48:21 -0700275 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;
281
Felipe Contrerasbfded872020-11-30 18:46:46 -0600282 if (!prefix)
283 continue;
284
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);
Brandon Williams6373cb52018-05-16 16:48:21 -0700292 }
293 }
294}