Coverage Report

Created: 2024-09-08 06:24

/src/git/refspec.c
Line
Count
Source (jump to first uncovered line)
1
#define USE_THE_REPOSITORY_VARIABLE
2
3
#include "git-compat-util.h"
4
#include "gettext.h"
5
#include "hash.h"
6
#include "hex.h"
7
#include "strvec.h"
8
#include "refs.h"
9
#include "refspec.h"
10
#include "strbuf.h"
11
12
/*
13
 * Parses the provided refspec 'refspec' and populates the refspec_item 'item'.
14
 * Returns 1 if successful and 0 if the refspec is invalid.
15
 */
16
static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch)
17
0
{
18
0
  size_t llen;
19
0
  int is_glob;
20
0
  const char *lhs, *rhs;
21
0
  int flags;
22
23
0
  is_glob = 0;
24
25
0
  lhs = refspec;
26
0
  if (*lhs == '+') {
27
0
    item->force = 1;
28
0
    lhs++;
29
0
  } else if (*lhs == '^') {
30
0
    item->negative = 1;
31
0
    lhs++;
32
0
  }
33
34
0
  rhs = strrchr(lhs, ':');
35
36
  /* negative refspecs only have one side */
37
0
  if (item->negative && rhs)
38
0
    return 0;
39
40
  /*
41
   * Before going on, special case ":" (or "+:") as a refspec
42
   * for pushing matching refs.
43
   */
44
0
  if (!fetch && rhs == lhs && rhs[1] == '\0') {
45
0
    item->matching = 1;
46
0
    return 1;
47
0
  }
48
49
0
  if (rhs) {
50
0
    size_t rlen = strlen(++rhs);
51
0
    is_glob = (1 <= rlen && strchr(rhs, '*'));
52
0
    item->dst = xstrndup(rhs, rlen);
53
0
  } else {
54
0
    item->dst = NULL;
55
0
  }
56
57
0
  llen = (rhs ? (rhs - lhs - 1) : strlen(lhs));
58
0
  if (1 <= llen && memchr(lhs, '*', llen)) {
59
0
    if ((rhs && !is_glob) || (!rhs && !item->negative && fetch))
60
0
      return 0;
61
0
    is_glob = 1;
62
0
  } else if (rhs && is_glob) {
63
0
    return 0;
64
0
  }
65
66
0
  item->pattern = is_glob;
67
0
  if (llen == 1 && *lhs == '@')
68
0
    item->src = xstrdup("HEAD");
69
0
  else
70
0
    item->src = xstrndup(lhs, llen);
71
0
  flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0);
72
73
0
  if (item->negative) {
74
0
    struct object_id unused;
75
76
    /*
77
     * Negative refspecs only have a LHS, which indicates a ref
78
     * (or pattern of refs) to exclude from other matches. This
79
     * can either be a simple ref, or a glob pattern. Exact sha1
80
     * match is not currently supported.
81
     */
82
0
    if (!*item->src)
83
0
      return 0; /* negative refspecs must not be empty */
84
0
    else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
85
0
      return 0; /* negative refpsecs cannot be exact sha1 */
86
0
    else if (!check_refname_format(item->src, flags))
87
0
      ; /* valid looking ref is ok */
88
0
    else
89
0
      return 0;
90
91
    /* the other rules below do not apply to negative refspecs */
92
0
    return 1;
93
0
  }
94
95
0
  if (fetch) {
96
0
    struct object_id unused;
97
98
    /* LHS */
99
0
    if (!*item->src)
100
0
      ; /* empty is ok; it means "HEAD" */
101
0
    else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused))
102
0
      item->exact_sha1 = 1; /* ok */
103
0
    else if (!check_refname_format(item->src, flags))
104
0
      ; /* valid looking ref is ok */
105
0
    else
106
0
      return 0;
107
    /* RHS */
108
0
    if (!item->dst)
109
0
      ; /* missing is ok; it is the same as empty */
110
0
    else if (!*item->dst)
111
0
      ; /* empty is ok; it means "do not store" */
112
0
    else if (!check_refname_format(item->dst, flags))
113
0
      ; /* valid looking ref is ok */
114
0
    else
115
0
      return 0;
116
0
  } else {
117
    /*
118
     * LHS
119
     * - empty is allowed; it means delete.
120
     * - when wildcarded, it must be a valid looking ref.
121
     * - otherwise, it must be an extended SHA-1, but
122
     *   there is no existing way to validate this.
123
     */
124
0
    if (!*item->src)
125
0
      ; /* empty is ok */
126
0
    else if (is_glob) {
127
0
      if (check_refname_format(item->src, flags))
128
0
        return 0;
129
0
    }
130
0
    else
131
0
      ; /* anything goes, for now */
132
    /*
133
     * RHS
134
     * - missing is allowed, but LHS then must be a
135
     *   valid looking ref.
136
     * - empty is not allowed.
137
     * - otherwise it must be a valid looking ref.
138
     */
139
0
    if (!item->dst) {
140
0
      if (check_refname_format(item->src, flags))
141
0
        return 0;
142
0
    } else if (!*item->dst) {
143
0
      return 0;
144
0
    } else {
145
0
      if (check_refname_format(item->dst, flags))
146
0
        return 0;
147
0
    }
148
0
  }
149
150
0
  return 1;
151
0
}
152
153
int refspec_item_init(struct refspec_item *item, const char *refspec, int fetch)
154
0
{
155
0
  memset(item, 0, sizeof(*item));
156
0
  return parse_refspec(item, refspec, fetch);
157
0
}
158
159
void refspec_item_init_or_die(struct refspec_item *item, const char *refspec,
160
            int fetch)
161
0
{
162
0
  if (!refspec_item_init(item, refspec, fetch))
163
0
    die(_("invalid refspec '%s'"), refspec);
164
0
}
165
166
void refspec_item_clear(struct refspec_item *item)
167
0
{
168
0
  FREE_AND_NULL(item->src);
169
0
  FREE_AND_NULL(item->dst);
170
0
  item->force = 0;
171
0
  item->pattern = 0;
172
0
  item->matching = 0;
173
0
  item->exact_sha1 = 0;
174
0
}
175
176
void refspec_init(struct refspec *rs, int fetch)
177
0
{
178
0
  memset(rs, 0, sizeof(*rs));
179
0
  rs->fetch = fetch;
180
0
}
181
182
static void refspec_append_nodup(struct refspec *rs, char *refspec)
183
0
{
184
0
  struct refspec_item item;
185
186
0
  refspec_item_init_or_die(&item, refspec, rs->fetch);
187
188
0
  ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc);
189
0
  rs->items[rs->nr++] = item;
190
191
0
  ALLOC_GROW(rs->raw, rs->raw_nr + 1, rs->raw_alloc);
192
0
  rs->raw[rs->raw_nr++] = refspec;
193
0
}
194
195
void refspec_append(struct refspec *rs, const char *refspec)
196
0
{
197
0
  refspec_append_nodup(rs, xstrdup(refspec));
198
0
}
199
200
void refspec_appendf(struct refspec *rs, const char *fmt, ...)
201
0
{
202
0
  va_list ap;
203
204
0
  va_start(ap, fmt);
205
0
  refspec_append_nodup(rs, xstrvfmt(fmt, ap));
206
0
  va_end(ap);
207
0
}
208
209
void refspec_appendn(struct refspec *rs, const char **refspecs, int nr)
210
0
{
211
0
  int i;
212
0
  for (i = 0; i < nr; i++)
213
0
    refspec_append(rs, refspecs[i]);
214
0
}
215
216
void refspec_clear(struct refspec *rs)
217
0
{
218
0
  int i;
219
220
0
  for (i = 0; i < rs->nr; i++)
221
0
    refspec_item_clear(&rs->items[i]);
222
223
0
  FREE_AND_NULL(rs->items);
224
0
  rs->alloc = 0;
225
0
  rs->nr = 0;
226
227
0
  for (i = 0; i < rs->raw_nr; i++)
228
0
    free((char *)rs->raw[i]);
229
0
  FREE_AND_NULL(rs->raw);
230
0
  rs->raw_alloc = 0;
231
0
  rs->raw_nr = 0;
232
233
0
  rs->fetch = 0;
234
0
}
235
236
int valid_fetch_refspec(const char *fetch_refspec_str)
237
0
{
238
0
  struct refspec_item refspec;
239
0
  int ret = refspec_item_init(&refspec, fetch_refspec_str, REFSPEC_FETCH);
240
0
  refspec_item_clear(&refspec);
241
0
  return ret;
242
0
}
243
244
int valid_remote_name(const char *name)
245
0
{
246
0
  int result;
247
0
  struct strbuf refspec = STRBUF_INIT;
248
0
  strbuf_addf(&refspec, "refs/heads/test:refs/remotes/%s/test", name);
249
0
  result = valid_fetch_refspec(refspec.buf);
250
0
  strbuf_release(&refspec);
251
0
  return result;
252
0
}
253
254
void refspec_ref_prefixes(const struct refspec *rs,
255
        struct strvec *ref_prefixes)
256
0
{
257
0
  int i;
258
0
  for (i = 0; i < rs->nr; i++) {
259
0
    const struct refspec_item *item = &rs->items[i];
260
0
    const char *prefix = NULL;
261
262
0
    if (item->exact_sha1 || item->negative)
263
0
      continue;
264
0
    if (rs->fetch == REFSPEC_FETCH)
265
0
      prefix = item->src;
266
0
    else if (item->dst)
267
0
      prefix = item->dst;
268
0
    else if (item->src && !item->exact_sha1)
269
0
      prefix = item->src;
270
271
0
    if (!prefix)
272
0
      continue;
273
274
0
    if (item->pattern) {
275
0
      const char *glob = strchr(prefix, '*');
276
0
      strvec_pushf(ref_prefixes, "%.*s",
277
0
             (int)(glob - prefix),
278
0
             prefix);
279
0
    } else {
280
0
      expand_ref_prefix(ref_prefixes, prefix);
281
0
    }
282
0
  }
283
0
}