refspec: convert valid_fetch_refspec to use parse_refspec
[git] / refspec.c
1 #include "cache.h"
2 #include "refs.h"
3 #include "refspec.h"
4
5 static struct refspec_item s_tag_refspec = {
6         0,
7         1,
8         0,
9         0,
10         "refs/tags/*",
11         "refs/tags/*"
12 };
13
14 /* See TAG_REFSPEC for the string version */
15 const struct refspec_item *tag_refspec = &s_tag_refspec;
16
17 /*
18  * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
19  * Returns 1 if successful and 0 if the refspec is invalid.
20  */
21 static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
22 {
23         size_t llen;
24         int is_glob;
25         const char *lhs, *rhs;
26         int flags;
27
28         is_glob = 0;
29
30         lhs = refspec;
31         if (*lhs == '+') {
32                 item->force = 1;
33                 lhs++;
34         }
35
36         rhs = strrchr(lhs, ':');
37
38         /*
39          * Before going on, special case ":" (or "+:") as a refspec
40          * for pushing matching refs.
41          */
42         if (!fetch && rhs == lhs && rhs[1] == '\0') {
43                 item->matching = 1;
44                 return 1;
45         }
46
47         if (rhs) {
48                 size_t rlen = strlen(++rhs);
49                 is_glob = (1 <= rlen && strchr(rhs, '*'));
50                 item->dst = xstrndup(rhs, rlen);
51         }
52
53         llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
54         if (1 <= llen && memchr(lhs, '*', llen)) {
55                 if ((rhs && !is_glob) || (!rhs && fetch))
56                         return 0;
57                 is_glob = 1;
58         } else if (rhs && is_glob) {
59                 return 0;
60         }
61
62         item->pattern = is_glob;
63         item->src = xstrndup(lhs, llen);
64         flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
65
66         if (fetch) {
67                 struct object_id unused;
68
69                 /* LHS */
70                 if (!*item->src)
71                         ; /* empty is ok; it means "HEAD" */
72                 else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(item->src, &unused))
73                         item->exact_sha1 = 1; /* ok */
74                 else if (!check_refname_format(item->src, flags))
75                         ; /* valid looking ref is ok */
76                 else
77                         return 0;
78                 /* RHS */
79                 if (!item->dst)
80                         ; /* missing is ok; it is the same as empty */
81                 else if (!*item->dst)
82                         ; /* empty is ok; it means "do not store" */
83                 else if (!check_refname_format(item->dst, flags))
84                         ; /* valid looking ref is ok */
85                 else
86                         return 0;
87         } else {
88                 /*
89                  * LHS
90                  * - empty is allowed; it means delete.
91                  * - when wildcarded, it must be a valid looking ref.
92                  * - otherwise, it must be an extended SHA-1, but
93                  *   there is no existing way to validate this.
94                  */
95                 if (!*item->src)
96                         ; /* empty is ok */
97                 else if (is_glob) {
98                         if (check_refname_format(item->src, flags))
99                                 return 0;
100                 }
101                 else
102                         ; /* anything goes, for now */
103                 /*
104                  * RHS
105                  * - missing is allowed, but LHS then must be a
106                  *   valid looking ref.
107                  * - empty is not allowed.
108                  * - otherwise it must be a valid looking ref.
109                  */
110                 if (!item->dst) {
111                         if (check_refname_format(item->src, flags))
112                                 return 0;
113                 } else if (!*item->dst) {
114                         return 0;
115                 } else {
116                         if (check_refname_format(item->dst, flags))
117                                 return 0;
118                 }
119         }
120
121         return 1;
122 }
123
124 static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
125 {
126         int i;
127         struct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));
128
129         for (i = 0; i < nr_refspec; i++) {
130                 if (!parse_refspec(&rs[i], refspec[i], fetch))
131                         goto invalid;
132         }
133
134         return rs;
135
136  invalid:
137         if (verify) {
138                 /*
139                  * nr_refspec must be greater than zero and i must be valid
140                  * since it is only possible to reach this point from within
141                  * the for loop above.
142                  */
143                 free_refspec(i+1, rs);
144                 return NULL;
145         }
146         die("Invalid refspec '%s'", refspec[i]);
147 }
148
149 struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)
150 {
151         return parse_refspec_internal(nr_refspec, refspec, 1, 0);
152 }
153
154 struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)
155 {
156         return parse_refspec_internal(nr_refspec, refspec, 0, 0);
157 }
158
159 void free_refspec(int nr_refspec, struct refspec_item *refspec)
160 {
161         int i;
162
163         if (!refspec)
164                 return;
165
166         for (i = 0; i < nr_refspec; i++) {
167                 free(refspec[i].src);
168                 free(refspec[i].dst);
169         }
170         free(refspec);
171 }
172
173 void refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
174 {
175         memset(item, 0, sizeof(*item));
176
177         if (!parse_refspec(item, refspec, fetch))
178                 die("Invalid refspec '%s'", refspec);
179 }
180
181 void refspec_item_clear(struct refspec_item *item)
182 {
183         FREE_AND_NULL(item->src);
184         FREE_AND_NULL(item->dst);
185         item->force = 0;
186         item->pattern = 0;
187         item->matching = 0;
188         item->exact_sha1 = 0;
189 }
190
191 void refspec_init(struct refspec *rs, int fetch)
192 {
193         memset(rs, 0, sizeof(*rs));
194         rs->fetch = fetch;
195 }
196
197 void refspec_append(struct refspec *rs, const char *refspec)
198 {
199         struct refspec_item item;
200
201         refspec_item_init(&item, refspec, rs->fetch);
202
203         ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
204         rs->items[rs->nr++] = item;
205
206         ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
207         rs->raw[rs->raw_nr++] = xstrdup(refspec);
208 }
209
210 void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
211 {
212         int i;
213         for (i = 0; i < nr; i++)
214                 refspec_append(rs, refspecs[i]);
215 }
216
217 void refspec_clear(struct refspec *rs)
218 {
219         int i;
220
221         for (i = 0; i < rs->nr; i++)
222                 refspec_item_clear(&rs->items[i]);
223
224         FREE_AND_NULL(rs->items);
225         rs->alloc = 0;
226         rs->nr = 0;
227
228         for (i = 0; i < rs->raw_nr; i++)
229                 free((char *)rs->raw[i]);
230         FREE_AND_NULL(rs->raw);
231         rs->raw_alloc = 0;
232         rs->raw_nr = 0;
233
234         rs->fetch = 0;
235 }
236
237 int valid_fetch_refspec(const char *fetch_refspec_str)
238 {
239         struct refspec_item refspec;
240         int ret = parse_refspec(&refspec, fetch_refspec_str, REFSPEC_FETCH);
241         refspec_item_clear(&refspec);
242         return ret;
243 }