refspec: rename struct refspec to struct refspec_item
[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 static struct refspec_item *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify)
18 {
19         int i;
20         struct refspec_item *rs = xcalloc(nr_refspec, sizeof(*rs));
21
22         for (i = 0; i < nr_refspec; i++) {
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[i];
31                 if (*lhs == '+') {
32                         rs[i].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                         rs[i].matching = 1;
44                         continue;
45                 }
46
47                 if (rhs) {
48                         size_t rlen = strlen(++rhs);
49                         is_glob = (1 <= rlen && strchr(rhs, '*'));
50                         rs[i].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                                 goto invalid;
57                         is_glob = 1;
58                 } else if (rhs && is_glob) {
59                         goto invalid;
60                 }
61
62                 rs[i].pattern = is_glob;
63                 rs[i].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 (!*rs[i].src)
71                                 ; /* empty is ok; it means "HEAD" */
72                         else if (llen == GIT_SHA1_HEXSZ && !get_oid_hex(rs[i].src, &unused))
73                                 rs[i].exact_sha1 = 1; /* ok */
74                         else if (!check_refname_format(rs[i].src, flags))
75                                 ; /* valid looking ref is ok */
76                         else
77                                 goto invalid;
78                         /* RHS */
79                         if (!rs[i].dst)
80                                 ; /* missing is ok; it is the same as empty */
81                         else if (!*rs[i].dst)
82                                 ; /* empty is ok; it means "do not store" */
83                         else if (!check_refname_format(rs[i].dst, flags))
84                                 ; /* valid looking ref is ok */
85                         else
86                                 goto invalid;
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 (!*rs[i].src)
96                                 ; /* empty is ok */
97                         else if (is_glob) {
98                                 if (check_refname_format(rs[i].src, flags))
99                                         goto invalid;
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 (!rs[i].dst) {
111                                 if (check_refname_format(rs[i].src, flags))
112                                         goto invalid;
113                         } else if (!*rs[i].dst) {
114                                 goto invalid;
115                         } else {
116                                 if (check_refname_format(rs[i].dst, flags))
117                                         goto invalid;
118                         }
119                 }
120         }
121         return rs;
122
123  invalid:
124         if (verify) {
125                 /*
126                  * nr_refspec must be greater than zero and i must be valid
127                  * since it is only possible to reach this point from within
128                  * the for loop above.
129                  */
130                 free_refspec(i+1, rs);
131                 return NULL;
132         }
133         die("Invalid refspec '%s'", refspec[i]);
134 }
135
136 int valid_fetch_refspec(const char *fetch_refspec_str)
137 {
138         struct refspec_item *refspec;
139
140         refspec = parse_refspec_internal(1, &fetch_refspec_str, 1, 1);
141         free_refspec(1, refspec);
142         return !!refspec;
143 }
144
145 struct refspec_item *parse_fetch_refspec(int nr_refspec, const char **refspec)
146 {
147         return parse_refspec_internal(nr_refspec, refspec, 1, 0);
148 }
149
150 struct refspec_item *parse_push_refspec(int nr_refspec, const char **refspec)
151 {
152         return parse_refspec_internal(nr_refspec, refspec, 0, 0);
153 }
154
155 void free_refspec(int nr_refspec, struct refspec_item *refspec)
156 {
157         int i;
158
159         if (!refspec)
160                 return;
161
162         for (i = 0; i < nr_refspec; i++) {
163                 free(refspec[i].src);
164                 free(refspec[i].dst);
165         }
166         free(refspec);
167 }