Git fork
at reftables-rust 497 lines 12 kB view raw
1#define USE_THE_REPOSITORY_VARIABLE 2#define DISABLE_SIGN_COMPARE_WARNINGS 3 4#include "git-compat-util.h" 5#include "gettext.h" 6#include "hash.h" 7#include "hex.h" 8#include "string-list.h" 9#include "strvec.h" 10#include "refs.h" 11#include "refspec.h" 12#include "remote.h" 13#include "strbuf.h" 14 15/* 16 * Parses the provided refspec 'refspec' and populates the refspec_item 'item'. 17 * Returns 1 if successful and 0 if the refspec is invalid. 18 */ 19static int parse_refspec(struct refspec_item *item, const char *refspec, int fetch) 20{ 21 size_t llen; 22 int is_glob; 23 const char *lhs, *rhs; 24 int flags; 25 26 is_glob = 0; 27 28 lhs = refspec; 29 if (*lhs == '+') { 30 item->force = 1; 31 lhs++; 32 } else if (*lhs == '^') { 33 item->negative = 1; 34 lhs++; 35 } 36 37 rhs = strrchr(lhs, ':'); 38 39 /* negative refspecs only have one side */ 40 if (item->negative && rhs) 41 return 0; 42 43 /* 44 * Before going on, special case ":" (or "+:") as a refspec 45 * for pushing matching refs. 46 */ 47 if (!fetch && rhs == lhs && rhs[1] == '\0') { 48 item->matching = 1; 49 return 1; 50 } 51 52 if (rhs) { 53 size_t rlen = strlen(++rhs); 54 is_glob = (1 <= rlen && strchr(rhs, '*')); 55 item->dst = xstrndup(rhs, rlen); 56 } else { 57 item->dst = NULL; 58 } 59 60 llen = (rhs ? (rhs - lhs - 1) : strlen(lhs)); 61 if (1 <= llen && memchr(lhs, '*', llen)) { 62 if ((rhs && !is_glob) || (!rhs && !item->negative && fetch)) 63 return 0; 64 is_glob = 1; 65 } else if (rhs && is_glob) { 66 return 0; 67 } 68 69 item->pattern = is_glob; 70 if (llen == 1 && *lhs == '@') 71 item->src = xstrdup("HEAD"); 72 else 73 item->src = xstrndup(lhs, llen); 74 flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); 75 76 if (item->negative) { 77 struct object_id unused; 78 79 /* 80 * Negative refspecs only have a LHS, which indicates a ref 81 * (or pattern of refs) to exclude from other matches. This 82 * can either be a simple ref, or a glob pattern. Exact sha1 83 * match is not currently supported. 84 */ 85 if (!*item->src) 86 return 0; /* negative refspecs must not be empty */ 87 else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused)) 88 return 0; /* negative refpsecs cannot be exact sha1 */ 89 else if (!check_refname_format(item->src, flags)) 90 ; /* valid looking ref is ok */ 91 else 92 return 0; 93 94 /* the other rules below do not apply to negative refspecs */ 95 return 1; 96 } 97 98 if (fetch) { 99 struct object_id unused; 100 101 /* LHS */ 102 if (!*item->src) 103 ; /* empty is ok; it means "HEAD" */ 104 else if (llen == the_hash_algo->hexsz && !get_oid_hex(item->src, &unused)) 105 item->exact_sha1 = 1; /* ok */ 106 else if (!check_refname_format(item->src, flags)) 107 ; /* valid looking ref is ok */ 108 else 109 return 0; 110 /* RHS */ 111 if (!item->dst) 112 ; /* missing is ok; it is the same as empty */ 113 else if (!*item->dst) 114 ; /* empty is ok; it means "do not store" */ 115 else if (!check_refname_format(item->dst, flags)) 116 ; /* valid looking ref is ok */ 117 else 118 return 0; 119 } else { 120 /* 121 * LHS 122 * - empty is allowed; it means delete. 123 * - when wildcarded, it must be a valid looking ref. 124 * - otherwise, it must be an extended SHA-1, but 125 * there is no existing way to validate this. 126 */ 127 if (!*item->src) 128 ; /* empty is ok */ 129 else if (is_glob) { 130 if (check_refname_format(item->src, flags)) 131 return 0; 132 } 133 else 134 ; /* anything goes, for now */ 135 /* 136 * RHS 137 * - missing is allowed, but LHS then must be a 138 * valid looking ref. 139 * - empty is not allowed. 140 * - otherwise it must be a valid looking ref. 141 */ 142 if (!item->dst) { 143 if (check_refname_format(item->src, flags)) 144 return 0; 145 } else if (!*item->dst) { 146 return 0; 147 } else { 148 if (check_refname_format(item->dst, flags)) 149 return 0; 150 } 151 } 152 153 return 1; 154} 155 156static int refspec_item_init(struct refspec_item *item, const char *refspec, 157 int fetch) 158{ 159 memset(item, 0, sizeof(*item)); 160 item->raw = xstrdup(refspec); 161 return parse_refspec(item, refspec, fetch); 162} 163 164int refspec_item_init_fetch(struct refspec_item *item, const char *refspec) 165{ 166 return refspec_item_init(item, refspec, 1); 167} 168 169int refspec_item_init_push(struct refspec_item *item, const char *refspec) 170{ 171 return refspec_item_init(item, refspec, 0); 172} 173 174void refspec_item_clear(struct refspec_item *item) 175{ 176 FREE_AND_NULL(item->src); 177 FREE_AND_NULL(item->dst); 178 FREE_AND_NULL(item->raw); 179 item->force = 0; 180 item->pattern = 0; 181 item->matching = 0; 182 item->exact_sha1 = 0; 183} 184 185void refspec_init_fetch(struct refspec *rs) 186{ 187 struct refspec blank = REFSPEC_INIT_FETCH; 188 memcpy(rs, &blank, sizeof(*rs)); 189} 190 191void refspec_init_push(struct refspec *rs) 192{ 193 struct refspec blank = REFSPEC_INIT_PUSH; 194 memcpy(rs, &blank, sizeof(*rs)); 195} 196 197void refspec_append(struct refspec *rs, const char *refspec) 198{ 199 struct refspec_item item; 200 int ret; 201 202 if (rs->fetch) 203 ret = refspec_item_init_fetch(&item, refspec); 204 else 205 ret = refspec_item_init_push(&item, refspec); 206 if (!ret) 207 die(_("invalid refspec '%s'"), refspec); 208 209 ALLOC_GROW(rs->items, rs->nr + 1, rs->alloc); 210 rs->items[rs->nr] = item; 211 212 rs->nr++; 213} 214 215void refspec_appendf(struct refspec *rs, const char *fmt, ...) 216{ 217 va_list ap; 218 char *buf; 219 220 va_start(ap, fmt); 221 buf = xstrvfmt(fmt, ap); 222 va_end(ap); 223 224 refspec_append(rs, buf); 225 free(buf); 226} 227 228void refspec_appendn(struct refspec *rs, const char **refspecs, int nr) 229{ 230 int i; 231 for (i = 0; i < nr; i++) 232 refspec_append(rs, refspecs[i]); 233} 234 235void refspec_clear(struct refspec *rs) 236{ 237 int i; 238 239 for (i = 0; i < rs->nr; i++) 240 refspec_item_clear(&rs->items[i]); 241 242 FREE_AND_NULL(rs->items); 243 rs->alloc = 0; 244 rs->nr = 0; 245 246 rs->fetch = 0; 247} 248 249int valid_fetch_refspec(const char *fetch_refspec_str) 250{ 251 struct refspec_item refspec; 252 int ret = refspec_item_init_fetch(&refspec, fetch_refspec_str); 253 refspec_item_clear(&refspec); 254 return ret; 255} 256 257void refspec_ref_prefixes(const struct refspec *rs, 258 struct strvec *ref_prefixes) 259{ 260 int i; 261 for (i = 0; i < rs->nr; i++) { 262 const struct refspec_item *item = &rs->items[i]; 263 const char *prefix = NULL; 264 265 if (item->negative) 266 continue; 267 268 if (rs->fetch) { 269 if (item->exact_sha1) 270 continue; 271 prefix = item->src; 272 } else { 273 /* 274 * Pushes can have an explicit destination like 275 * "foo:bar", or can implicitly use the src for both 276 * ("foo" is the same as "foo:foo"). 277 */ 278 if (item->dst) 279 prefix = item->dst; 280 else if (item->src && !item->exact_sha1) 281 prefix = item->src; 282 } 283 284 if (!prefix) 285 continue; 286 287 if (item->pattern) { 288 const char *glob = strchr(prefix, '*'); 289 strvec_pushf(ref_prefixes, "%.*s", 290 (int)(glob - prefix), 291 prefix); 292 } else { 293 expand_ref_prefix(ref_prefixes, prefix); 294 } 295 } 296} 297 298int match_refname_with_pattern(const char *pattern, const char *refname, 299 const char *replacement, char **result) 300{ 301 const char *kstar = strchr(pattern, '*'); 302 size_t klen; 303 size_t ksuffixlen; 304 size_t namelen; 305 int ret; 306 if (!kstar) 307 die(_("pattern '%s' has no '*'"), pattern); 308 klen = kstar - pattern; 309 ksuffixlen = strlen(kstar + 1); 310 namelen = strlen(refname); 311 ret = !strncmp(refname, pattern, klen) && namelen >= klen + ksuffixlen && 312 !memcmp(refname + namelen - ksuffixlen, kstar + 1, ksuffixlen); 313 if (ret && replacement) { 314 struct strbuf sb = STRBUF_INIT; 315 const char *vstar = strchr(replacement, '*'); 316 if (!vstar) 317 die(_("replacement '%s' has no '*'"), replacement); 318 strbuf_add(&sb, replacement, vstar - replacement); 319 strbuf_add(&sb, refname + klen, namelen - klen - ksuffixlen); 320 strbuf_addstr(&sb, vstar + 1); 321 *result = strbuf_detach(&sb, NULL); 322 } 323 return ret; 324} 325 326static int refspec_match(const struct refspec_item *refspec, 327 const char *name) 328{ 329 if (refspec->pattern) 330 return match_refname_with_pattern(refspec->src, name, NULL, NULL); 331 332 return !strcmp(refspec->src, name); 333} 334 335int refname_matches_negative_refspec_item(const char *refname, struct refspec *rs) 336{ 337 int i; 338 339 for (i = 0; i < rs->nr; i++) { 340 if (rs->items[i].negative && refspec_match(&rs->items[i], refname)) 341 return 1; 342 } 343 return 0; 344} 345 346static int refspec_find_negative_match(struct refspec *rs, struct refspec_item *query) 347{ 348 int i, matched_negative = 0; 349 int find_src = !query->src; 350 struct string_list reversed = STRING_LIST_INIT_DUP; 351 const char *needle = find_src ? query->dst : query->src; 352 353 /* 354 * Check whether the queried ref matches any negative refpsec. If so, 355 * then we should ultimately treat this as not matching the query at 356 * all. 357 * 358 * Note that negative refspecs always match the source, but the query 359 * item uses the destination. To handle this, we apply pattern 360 * refspecs in reverse to figure out if the query source matches any 361 * of the negative refspecs. 362 * 363 * The first loop finds and expands all positive refspecs 364 * matched by the queried ref. 365 * 366 * The second loop checks if any of the results of the first loop 367 * match any negative refspec. 368 */ 369 for (i = 0; i < rs->nr; i++) { 370 struct refspec_item *refspec = &rs->items[i]; 371 char *expn_name; 372 373 if (refspec->negative) 374 continue; 375 376 /* Note the reversal of src and dst */ 377 if (refspec->pattern) { 378 const char *key = refspec->dst ? refspec->dst : refspec->src; 379 const char *value = refspec->src; 380 381 if (match_refname_with_pattern(key, needle, value, &expn_name)) 382 string_list_append_nodup(&reversed, expn_name); 383 } else if (refspec->matching) { 384 /* For the special matching refspec, any query should match */ 385 string_list_append(&reversed, needle); 386 } else if (!refspec->src) { 387 BUG("refspec->src should not be null here"); 388 } else if (!strcmp(needle, refspec->src)) { 389 string_list_append(&reversed, refspec->src); 390 } 391 } 392 393 for (i = 0; !matched_negative && i < reversed.nr; i++) { 394 if (refname_matches_negative_refspec_item(reversed.items[i].string, rs)) 395 matched_negative = 1; 396 } 397 398 string_list_clear(&reversed, 0); 399 400 return matched_negative; 401} 402 403void refspec_find_all_matches(struct refspec *rs, 404 struct refspec_item *query, 405 struct string_list *results) 406{ 407 int i; 408 int find_src = !query->src; 409 410 if (find_src && !query->dst) 411 BUG("refspec_find_all_matches: need either src or dst"); 412 413 if (refspec_find_negative_match(rs, query)) 414 return; 415 416 for (i = 0; i < rs->nr; i++) { 417 struct refspec_item *refspec = &rs->items[i]; 418 const char *key = find_src ? refspec->dst : refspec->src; 419 const char *value = find_src ? refspec->src : refspec->dst; 420 const char *needle = find_src ? query->dst : query->src; 421 char **result = find_src ? &query->src : &query->dst; 422 423 if (!refspec->dst || refspec->negative) 424 continue; 425 if (refspec->pattern) { 426 if (match_refname_with_pattern(key, needle, value, result)) 427 string_list_append_nodup(results, *result); 428 } else if (!strcmp(needle, key)) { 429 string_list_append(results, value); 430 } 431 } 432} 433 434int refspec_find_match(struct refspec *rs, struct refspec_item *query) 435{ 436 int i; 437 int find_src = !query->src; 438 const char *needle = find_src ? query->dst : query->src; 439 char **result = find_src ? &query->src : &query->dst; 440 441 if (find_src && !query->dst) 442 BUG("refspec_find_match: need either src or dst"); 443 444 if (refspec_find_negative_match(rs, query)) 445 return -1; 446 447 for (i = 0; i < rs->nr; i++) { 448 struct refspec_item *refspec = &rs->items[i]; 449 const char *key = find_src ? refspec->dst : refspec->src; 450 const char *value = find_src ? refspec->src : refspec->dst; 451 452 if (!refspec->dst || refspec->negative) 453 continue; 454 if (refspec->pattern) { 455 if (match_refname_with_pattern(key, needle, value, result)) { 456 query->force = refspec->force; 457 return 0; 458 } 459 } else if (!strcmp(needle, key)) { 460 *result = xstrdup(value); 461 query->force = refspec->force; 462 return 0; 463 } 464 } 465 return -1; 466} 467 468struct ref *apply_negative_refspecs(struct ref *ref_map, struct refspec *rs) 469{ 470 struct ref **tail; 471 472 for (tail = &ref_map; *tail; ) { 473 struct ref *ref = *tail; 474 475 if (refname_matches_negative_refspec_item(ref->name, rs)) { 476 *tail = ref->next; 477 free(ref->peer_ref); 478 free(ref); 479 } else 480 tail = &ref->next; 481 } 482 483 return ref_map; 484} 485 486char *apply_refspecs(struct refspec *rs, const char *name) 487{ 488 struct refspec_item query; 489 490 memset(&query, 0, sizeof(struct refspec_item)); 491 query.src = (char *)name; 492 493 if (refspec_find_match(rs, &query)) 494 return NULL; 495 496 return query.dst; 497}