Git fork

Merge branch 'mh/check-ref-format-3'

* mh/check-ref-format-3: (23 commits)
add_ref(): verify that the refname is formatted correctly
resolve_ref(): expand documentation
resolve_ref(): also treat a too-long SHA1 as invalid
resolve_ref(): emit warnings for improperly-formatted references
resolve_ref(): verify that the input refname has the right format
remote: avoid passing NULL to read_ref()
remote: use xstrdup() instead of strdup()
resolve_ref(): do not follow incorrectly-formatted symbolic refs
resolve_ref(): extract a function get_packed_ref()
resolve_ref(): turn buffer into a proper string as soon as possible
resolve_ref(): only follow a symlink that contains a valid, normalized refname
resolve_ref(): use prefixcmp()
resolve_ref(): explicitly fail if a symlink is not readable
Change check_refname_format() to reject unnormalized refnames
Inline function refname_format_print()
Make collapse_slashes() allocate memory for its result
Do not allow ".lock" at the end of any refname component
Refactor check_refname_format()
Change check_ref_format() to take a flags argument
Change bad_ref_char() to return a boolean value
...

+425 -224
+40 -13
Documentation/git-check-ref-format.txt
··· 8 8 SYNOPSIS 9 9 -------- 10 10 [verse] 11 - 'git check-ref-format' <refname> 12 - 'git check-ref-format' --print <refname> 11 + 'git check-ref-format' [--normalize] 12 + [--[no-]allow-onelevel] [--refspec-pattern] 13 + <refname> 13 14 'git check-ref-format' --branch <branchname-shorthand> 14 15 15 16 DESCRIPTION ··· 28 29 29 30 . They can include slash `/` for hierarchical (directory) 30 31 grouping, but no slash-separated component can begin with a 31 - dot `.`. 32 + dot `.` or end with the sequence `.lock`. 32 33 33 34 . They must contain at least one `/`. This enforces the presence of a 34 35 category like `heads/`, `tags/` etc. but the actual names are not 35 - restricted. 36 + restricted. If the `--allow-onelevel` option is used, this rule 37 + is waived. 36 38 37 39 . They cannot have two consecutive dots `..` anywhere. 38 40 39 41 . They cannot have ASCII control characters (i.e. bytes whose 40 42 values are lower than \040, or \177 `DEL`), space, tilde `~`, 41 - caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`, 42 - or open bracket `[` anywhere. 43 + caret `{caret}`, or colon `:` anywhere. 44 + 45 + . They cannot have question-mark `?`, asterisk `{asterisk}`, or open 46 + bracket `[` anywhere. See the `--refspec-pattern` option below for 47 + an exception to this rule. 43 48 44 - . They cannot end with a slash `/` nor a dot `.`. 49 + . They cannot begin or end with a slash `/` or contain multiple 50 + consecutive slashes (see the `--normalize` option below for an 51 + exception to this rule) 45 52 46 - . They cannot end with the sequence `.lock`. 53 + . They cannot end with a dot `.`. 47 54 48 55 . They cannot contain a sequence `@{`. 49 56 ··· 67 74 'git cat-file': "git cat-file blob v1.3.3:refs.c". 68 75 69 76 . at-open-brace `@{` is used as a notation to access a reflog entry. 70 - 71 - With the `--print` option, if 'refname' is acceptable, it prints the 72 - canonicalized name of a hypothetical reference with that name. That is, 73 - it prints 'refname' with any extra `/` characters removed. 74 77 75 78 With the `--branch` option, it expands the ``previous branch syntax'' 76 79 `@{-n}`. For example, `@{-1}` is a way to refer the last branch you ··· 78 81 syntax anywhere a branch name is expected, so they can act as if you 79 82 typed the branch name. 80 83 84 + OPTIONS 85 + ------- 86 + --allow-onelevel:: 87 + --no-allow-onelevel:: 88 + Controls whether one-level refnames are accepted (i.e., 89 + refnames that do not contain multiple `/`-separated 90 + components). The default is `--no-allow-onelevel`. 91 + 92 + --refspec-pattern:: 93 + Interpret <refname> as a reference name pattern for a refspec 94 + (as used with remote repositories). If this option is 95 + enabled, <refname> is allowed to contain a single `{asterisk}` 96 + in place of a one full pathname component (e.g., 97 + `foo/{asterisk}/bar` but not `foo/bar{asterisk}`). 98 + 99 + --normalize:: 100 + Normalize 'refname' by removing any leading slash (`/`) 101 + characters and collapsing runs of adjacent slashes between 102 + name components into a single slash. Iff the normalized 103 + refname is valid then print it to standard output and exit 104 + with a status of 0. (`--print` is a deprecated way to spell 105 + `--normalize`.) 106 + 107 + 81 108 EXAMPLES 82 109 -------- 83 110 ··· 90 117 * Determine the reference name to use for a new branch: 91 118 + 92 119 ------------ 93 - $ ref=$(git check-ref-format --print "refs/heads/$newbranch") || 120 + $ ref=$(git check-ref-format --normalize "refs/heads/$newbranch") || 94 121 die "we do not like '$newbranch' as a branch name." 95 122 ------------ 96 123
+39 -22
builtin/check-ref-format.c
··· 8 8 #include "strbuf.h" 9 9 10 10 static const char builtin_check_ref_format_usage[] = 11 - "git check-ref-format [--print] <refname>\n" 11 + "git check-ref-format [--normalize] [options] <refname>\n" 12 12 " or: git check-ref-format --branch <branchname-shorthand>"; 13 13 14 14 /* 15 - * Remove leading slashes and replace each run of adjacent slashes in 16 - * src with a single slash, and write the result to dst. 15 + * Return a copy of refname but with leading slashes removed and runs 16 + * of adjacent slashes replaced with single slashes. 17 17 * 18 18 * This function is similar to normalize_path_copy(), but stripped down 19 19 * to meet check_ref_format's simpler needs. 20 20 */ 21 - static void collapse_slashes(char *dst, const char *src) 21 + static char *collapse_slashes(const char *refname) 22 22 { 23 + char *ret = xmalloc(strlen(refname) + 1); 23 24 char ch; 24 25 char prev = '/'; 26 + char *cp = ret; 25 27 26 - while ((ch = *src++) != '\0') { 28 + while ((ch = *refname++) != '\0') { 27 29 if (prev == '/' && ch == prev) 28 30 continue; 29 31 30 - *dst++ = ch; 32 + *cp++ = ch; 31 33 prev = ch; 32 34 } 33 - *dst = '\0'; 35 + *cp = '\0'; 36 + return ret; 34 37 } 35 38 36 39 static int check_ref_format_branch(const char *arg) ··· 45 48 return 0; 46 49 } 47 50 48 - static int check_ref_format_print(const char *arg) 51 + int cmd_check_ref_format(int argc, const char **argv, const char *prefix) 49 52 { 50 - char *refname = xmalloc(strlen(arg) + 1); 51 - 52 - if (check_ref_format(arg)) 53 - return 1; 54 - collapse_slashes(refname, arg); 55 - printf("%s\n", refname); 56 - return 0; 57 - } 53 + int i; 54 + int normalize = 0; 55 + int flags = 0; 56 + const char *refname; 58 57 59 - int cmd_check_ref_format(int argc, const char **argv, const char *prefix) 60 - { 61 58 if (argc == 2 && !strcmp(argv[1], "-h")) 62 59 usage(builtin_check_ref_format_usage); 63 60 64 61 if (argc == 3 && !strcmp(argv[1], "--branch")) 65 62 return check_ref_format_branch(argv[2]); 66 - if (argc == 3 && !strcmp(argv[1], "--print")) 67 - return check_ref_format_print(argv[2]); 68 - if (argc != 2) 63 + 64 + for (i = 1; i < argc && argv[i][0] == '-'; i++) { 65 + if (!strcmp(argv[i], "--normalize") || !strcmp(argv[i], "--print")) 66 + normalize = 1; 67 + else if (!strcmp(argv[i], "--allow-onelevel")) 68 + flags |= REFNAME_ALLOW_ONELEVEL; 69 + else if (!strcmp(argv[i], "--no-allow-onelevel")) 70 + flags &= ~REFNAME_ALLOW_ONELEVEL; 71 + else if (!strcmp(argv[i], "--refspec-pattern")) 72 + flags |= REFNAME_REFSPEC_PATTERN; 73 + else 74 + usage(builtin_check_ref_format_usage); 75 + } 76 + if (! (i == argc - 1)) 69 77 usage(builtin_check_ref_format_usage); 70 - return !!check_ref_format(argv[1]); 78 + 79 + refname = argv[i]; 80 + if (normalize) 81 + refname = collapse_slashes(refname); 82 + if (check_refname_format(refname, flags)) 83 + return 1; 84 + if (normalize) 85 + printf("%s\n", refname); 86 + 87 + return 0; 71 88 }
+1 -1
builtin/checkout.c
··· 871 871 new->name = arg; 872 872 setup_branch_path(new); 873 873 874 - if (check_ref_format(new->path) == CHECK_REF_FORMAT_OK && 874 + if (!check_refname_format(new->path, 0) && 875 875 resolve_ref(new->path, branch_rev, 1, NULL)) 876 876 hashcpy(rev, branch_rev); 877 877 else
+1 -1
builtin/fetch-pack.c
··· 546 546 for (ref = *refs; ref; ref = next) { 547 547 next = ref->next; 548 548 if (!memcmp(ref->name, "refs/", 5) && 549 - check_ref_format(ref->name + 5)) 549 + check_refname_format(ref->name + 5, 0)) 550 550 ; /* trash */ 551 551 else if (args.fetch_all && 552 552 (!args.depth || prefixcmp(ref->name, "refs/tags/") )) {
+1 -1
builtin/receive-pack.c
··· 396 396 struct ref_lock *lock; 397 397 398 398 /* only refs/... are allowed */ 399 - if (prefixcmp(name, "refs/") || check_ref_format(name + 5)) { 399 + if (prefixcmp(name, "refs/") || check_refname_format(name + 5, 0)) { 400 400 rp_error("refusing to create funny ref '%s' remotely", name); 401 401 return "funny refname"; 402 402 }
+1 -1
builtin/replace.c
··· 94 94 "refs/replace/%s", 95 95 sha1_to_hex(object)) > sizeof(ref) - 1) 96 96 die("replace ref name too long: %.*s...", 50, ref); 97 - if (check_ref_format(ref)) 97 + if (check_refname_format(ref, 0)) 98 98 die("'%s' is not a valid ref name.", ref); 99 99 100 100 if (!resolve_ref(ref, prev, 1, NULL))
+1 -1
builtin/show-ref.c
··· 145 145 if (strncmp(ref, match, matchlen)) 146 146 continue; 147 147 } 148 - if (check_ref_format(ref)) { 148 + if (check_refname_format(ref, 0)) { 149 149 warning("ref '%s' ignored", ref); 150 150 continue; 151 151 }
+2 -2
builtin/tag.c
··· 407 407 static int strbuf_check_tag_ref(struct strbuf *sb, const char *name) 408 408 { 409 409 if (name[0] == '-') 410 - return CHECK_REF_FORMAT_ERROR; 410 + return -1; 411 411 412 412 strbuf_reset(sb); 413 413 strbuf_addf(sb, "refs/tags/%s", name); 414 414 415 - return check_ref_format(sb->buf); 415 + return check_refname_format(sb->buf, 0); 416 416 } 417 417 418 418 int cmd_tag(int argc, const char **argv, const char *prefix)
+42 -1
cache.h
··· 819 819 { 820 820 return get_sha1_with_context_1(str, sha1, orc, 0, NULL); 821 821 } 822 + 823 + /* 824 + * Try to read a SHA1 in hexadecimal format from the 40 characters 825 + * starting at hex. Write the 20-byte result to sha1 in binary form. 826 + * Return 0 on success. Reading stops if a NUL is encountered in the 827 + * input, so it is safe to pass this function an arbitrary 828 + * null-terminated string. 829 + */ 822 830 extern int get_sha1_hex(const char *hex, unsigned char *sha1); 831 + 823 832 extern char *sha1_to_hex(const unsigned char *sha1); /* static buffer result! */ 824 833 extern int read_ref(const char *filename, unsigned char *sha1); 825 - extern const char *resolve_ref(const char *path, unsigned char *sha1, int, int *); 834 + 835 + /* 836 + * Resolve a reference, recursively following symbolic refererences. 837 + * 838 + * Store the referred-to object's name in sha1 and return the name of 839 + * the non-symbolic reference that ultimately pointed at it. The 840 + * return value, if not NULL, is a pointer into either a static buffer 841 + * or the input ref. 842 + * 843 + * If the reference cannot be resolved to an object, the behavior 844 + * depends on the "reading" argument: 845 + * 846 + * - If reading is set, return NULL. 847 + * 848 + * - If reading is not set, clear sha1 and return the name of the last 849 + * reference name in the chain, which will either be a non-symbolic 850 + * reference or an undefined reference. If this is a prelude to 851 + * "writing" to the ref, the return value is the name of the ref 852 + * that will actually be created or changed. 853 + * 854 + * If flag is non-NULL, set the value that it points to the 855 + * combination of REF_ISPACKED (if the reference was found among the 856 + * packed references) and REF_ISSYMREF (if the initial reference was a 857 + * symbolic reference). 858 + * 859 + * If ref is not a properly-formatted, normalized reference, return 860 + * NULL. If more than MAXDEPTH recursive symbolic lookups are needed, 861 + * give up and return NULL. 862 + * 863 + * errno is sometimes set on errors, but not always. 864 + */ 865 + extern const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag); 866 + 826 867 extern int dwim_ref(const char *str, int len, unsigned char *sha1, char **ref); 827 868 extern int dwim_log(const char *str, int len, unsigned char *sha1, char **ref); 828 869 extern int interpret_branch_name(const char *str, struct strbuf *);
+1 -1
connect.c
··· 22 22 len -= 5; 23 23 24 24 /* REF_NORMAL means that we don't want the magic fake tag refs */ 25 - if ((flags & REF_NORMAL) && check_ref_format(name) < 0) 25 + if ((flags & REF_NORMAL) && check_refname_format(name, 0)) 26 26 return 0; 27 27 28 28 /* REF_HEADS means that we want regular branch heads */
+1 -1
environment.c
··· 106 106 if (strcmp((*c)->buf, "/") != 0) 107 107 strbuf_addf(&buf, "refs/namespaces/%s", (*c)->buf); 108 108 strbuf_list_free(components); 109 - if (check_ref_format(buf.buf) != CHECK_REF_FORMAT_OK) 109 + if (check_refname_format(buf.buf, 0)) 110 110 die("bad git namespace path \"%s\"", raw_namespace); 111 111 strbuf_addch(&buf, '/'); 112 112 return strbuf_detach(&buf, NULL);
+1 -6
fast-import.c
··· 722 722 723 723 if (b) 724 724 die("Invalid attempt to create duplicate branch: %s", name); 725 - switch (check_ref_format(name)) { 726 - case 0: break; /* its valid */ 727 - case CHECK_REF_FORMAT_ONELEVEL: 728 - break; /* valid, but too few '/', allow anyway */ 729 - default: 725 + if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL)) 730 726 die("Branch name doesn't conform to GIT standards: %s", name); 731 - } 732 727 733 728 b = pool_calloc(1, sizeof(struct branch)); 734 729 b->name = pool_strdup(name);
+1 -1
git_remote_helpers/git/git.py
··· 54 54 # The following is a reimplementation of the git check-ref-format 55 55 # command. The rules were derived from the git check-ref-format(1) 56 56 # manual page. This code should be replaced by a call to 57 - # check_ref_format() in the git library, when such is available. 57 + # check_refname_format() in the git library, when such is available. 58 58 if ref_name.endswith('/') or \ 59 59 ref_name.startswith('.') or \ 60 60 ref_name.count('/.') or \
+9 -1
hex.c
··· 39 39 { 40 40 int i; 41 41 for (i = 0; i < 20; i++) { 42 - unsigned int val = (hexval(hex[0]) << 4) | hexval(hex[1]); 42 + unsigned int val; 43 + /* 44 + * hex[1]=='\0' is caught when val is checked below, 45 + * but if hex[0] is NUL we have to avoid reading 46 + * past the end of the string: 47 + */ 48 + if (!hex[0]) 49 + return -1; 50 + val = (hexval(hex[0]) << 4) | hexval(hex[1]); 43 51 if (val & ~0xff) 44 52 return -1; 45 53 *sha1++ = val;
+3 -2
notes-merge.c
··· 570 570 /* Dereference o->local_ref into local_sha1 */ 571 571 if (!resolve_ref(o->local_ref, local_sha1, 0, NULL)) 572 572 die("Failed to resolve local notes ref '%s'", o->local_ref); 573 - else if (!check_ref_format(o->local_ref) && is_null_sha1(local_sha1)) 573 + else if (!check_refname_format(o->local_ref, 0) && 574 + is_null_sha1(local_sha1)) 574 575 local = NULL; /* local_sha1 == null_sha1 indicates unborn ref */ 575 576 else if (!(local = lookup_commit_reference(local_sha1))) 576 577 die("Could not parse local commit %s (%s)", ··· 583 584 * Failed to get remote_sha1. If o->remote_ref looks like an 584 585 * unborn ref, perform the merge using an empty notes tree. 585 586 */ 586 - if (!check_ref_format(o->remote_ref)) { 587 + if (!check_refname_format(o->remote_ref, 0)) { 587 588 hashclr(remote_sha1); 588 589 remote = NULL; 589 590 } else {
+1 -1
pack-refs.c
··· 72 72 for (i = 0; i < 2; i++) { /* refs/{heads,tags,...}/ */ 73 73 while (*p && *p != '/') 74 74 p++; 75 - /* tolerate duplicate slashes; see check_ref_format() */ 75 + /* tolerate duplicate slashes; see check_refname_format() */ 76 76 while (*p == '/') 77 77 p++; 78 78 }
+129 -91
refs.c
··· 56 56 entry = xmalloc(sizeof(struct ref_list) + len); 57 57 hashcpy(entry->sha1, sha1); 58 58 hashclr(entry->peeled); 59 + if (check_refname_format(name, REFNAME_ALLOW_ONELEVEL|REFNAME_DOT_COMPONENT)) 60 + die("Reference has invalid format: '%s'", name); 59 61 memcpy(entry->name, name, len); 60 62 entry->flag = flag; 61 63 entry->next = list; ··· 508 510 } 509 511 510 512 /* 511 - * If the "reading" argument is set, this function finds out what _object_ 512 - * the ref points at by "reading" the ref. The ref, if it is not symbolic, 513 - * has to exist, and if it is symbolic, it has to point at an existing ref, 514 - * because the "read" goes through the symref to the ref it points at. 515 - * 516 - * The access that is not "reading" may often be "writing", but does not 517 - * have to; it can be merely checking _where it leads to_. If it is a 518 - * prelude to "writing" to the ref, a write to a symref that points at 519 - * yet-to-be-born ref will create the real ref pointed by the symref. 520 - * reading=0 allows the caller to check where such a symref leads to. 513 + * Try to read ref from the packed references. On success, set sha1 514 + * and return 0; otherwise, return -1. 521 515 */ 516 + static int get_packed_ref(const char *ref, unsigned char *sha1) 517 + { 518 + struct ref_list *list = get_packed_refs(NULL); 519 + while (list) { 520 + if (!strcmp(ref, list->name)) { 521 + hashcpy(sha1, list->sha1); 522 + return 0; 523 + } 524 + list = list->next; 525 + } 526 + return -1; 527 + } 528 + 522 529 const char *resolve_ref(const char *ref, unsigned char *sha1, int reading, int *flag) 523 530 { 524 531 int depth = MAXDEPTH; 525 532 ssize_t len; 526 533 char buffer[256]; 527 534 static char ref_buffer[256]; 535 + char path[PATH_MAX]; 528 536 529 537 if (flag) 530 538 *flag = 0; 531 539 540 + if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL)) 541 + return NULL; 542 + 532 543 for (;;) { 533 - char path[PATH_MAX]; 534 544 struct stat st; 535 545 char *buf; 536 546 int fd; ··· 539 549 return NULL; 540 550 541 551 git_snpath(path, sizeof(path), "%s", ref); 542 - /* Special case: non-existing file. */ 552 + 543 553 if (lstat(path, &st) < 0) { 544 - struct ref_list *list = get_packed_refs(NULL); 545 - while (list) { 546 - if (!strcmp(ref, list->name)) { 547 - hashcpy(sha1, list->sha1); 548 - if (flag) 549 - *flag |= REF_ISPACKED; 550 - return ref; 551 - } 552 - list = list->next; 554 + if (errno != ENOENT) 555 + return NULL; 556 + /* 557 + * The loose reference file does not exist; 558 + * check for a packed reference. 559 + */ 560 + if (!get_packed_ref(ref, sha1)) { 561 + if (flag) 562 + *flag |= REF_ISPACKED; 563 + return ref; 553 564 } 554 - if (reading || errno != ENOENT) 565 + /* The reference is not a packed reference, either. */ 566 + if (reading) { 555 567 return NULL; 556 - hashclr(sha1); 557 - return ref; 568 + } else { 569 + hashclr(sha1); 570 + return ref; 571 + } 558 572 } 559 573 560 574 /* Follow "normalized" - ie "refs/.." symlinks by hand */ 561 575 if (S_ISLNK(st.st_mode)) { 562 576 len = readlink(path, buffer, sizeof(buffer)-1); 563 - if (len >= 5 && !memcmp("refs/", buffer, 5)) { 564 - buffer[len] = 0; 577 + if (len < 0) 578 + return NULL; 579 + buffer[len] = 0; 580 + if (!prefixcmp(buffer, "refs/") && 581 + !check_refname_format(buffer, 0)) { 565 582 strcpy(ref_buffer, buffer); 566 583 ref = ref_buffer; 567 584 if (flag) ··· 585 602 return NULL; 586 603 len = read_in_full(fd, buffer, sizeof(buffer)-1); 587 604 close(fd); 605 + if (len < 0) 606 + return NULL; 607 + while (len && isspace(buffer[len-1])) 608 + len--; 609 + buffer[len] = '\0'; 588 610 589 611 /* 590 612 * Is it a symbolic ref? 591 613 */ 592 - if (len < 4 || memcmp("ref:", buffer, 4)) 614 + if (prefixcmp(buffer, "ref:")) 593 615 break; 594 616 buf = buffer + 4; 595 - len -= 4; 596 - while (len && isspace(*buf)) 597 - buf++, len--; 598 - while (len && isspace(buf[len-1])) 599 - len--; 600 - buf[len] = 0; 601 - memcpy(ref_buffer, buf, len + 1); 602 - ref = ref_buffer; 617 + while (isspace(*buf)) 618 + buf++; 619 + if (check_refname_format(buf, REFNAME_ALLOW_ONELEVEL)) { 620 + warning("symbolic reference in %s is formatted incorrectly", 621 + path); 622 + return NULL; 623 + } 624 + ref = strcpy(ref_buffer, buf); 603 625 if (flag) 604 626 *flag |= REF_ISSYMREF; 605 627 } 606 - if (len < 40 || get_sha1_hex(buffer, sha1)) 628 + /* Please note that FETCH_HEAD has a second line containing other data. */ 629 + if (get_sha1_hex(buffer, sha1) || (buffer[40] != '\0' && !isspace(buffer[40]))) { 630 + warning("reference in %s is formatted incorrectly", path); 607 631 return NULL; 632 + } 608 633 return ref; 609 634 } 610 635 ··· 902 927 * - it contains a "\" (backslash) 903 928 */ 904 929 930 + /* Return true iff ch is not allowed in reference names. */ 905 931 static inline int bad_ref_char(int ch) 906 932 { 907 933 if (((unsigned) ch) <= ' ' || ch == 0x7f || 908 934 ch == '~' || ch == '^' || ch == ':' || ch == '\\') 909 935 return 1; 910 936 /* 2.13 Pattern Matching Notation */ 911 - if (ch == '?' || ch == '[') /* Unsupported */ 937 + if (ch == '*' || ch == '?' || ch == '[') /* Unsupported */ 912 938 return 1; 913 - if (ch == '*') /* Supported at the end */ 914 - return 2; 915 939 return 0; 916 940 } 917 941 918 - int check_ref_format(const char *ref) 942 + /* 943 + * Try to read one refname component from the front of ref. Return 944 + * the length of the component found, or -1 if the component is not 945 + * legal. 946 + */ 947 + static int check_refname_component(const char *ref, int flags) 919 948 { 920 - int ch, level, bad_type, last; 921 - int ret = CHECK_REF_FORMAT_OK; 922 - const char *cp = ref; 949 + const char *cp; 950 + char last = '\0'; 923 951 924 - level = 0; 925 - while (1) { 926 - while ((ch = *cp++) == '/') 927 - ; /* tolerate duplicated slashes */ 928 - if (!ch) 929 - /* should not end with slashes */ 930 - return CHECK_REF_FORMAT_ERROR; 952 + for (cp = ref; ; cp++) { 953 + char ch = *cp; 954 + if (ch == '\0' || ch == '/') 955 + break; 956 + if (bad_ref_char(ch)) 957 + return -1; /* Illegal character in refname. */ 958 + if (last == '.' && ch == '.') 959 + return -1; /* Refname contains "..". */ 960 + if (last == '@' && ch == '{') 961 + return -1; /* Refname contains "@{". */ 962 + last = ch; 963 + } 964 + if (cp == ref) 965 + return -1; /* Component has zero length. */ 966 + if (ref[0] == '.') { 967 + if (!(flags & REFNAME_DOT_COMPONENT)) 968 + return -1; /* Component starts with '.'. */ 969 + /* 970 + * Even if leading dots are allowed, don't allow "." 971 + * as a component (".." is prevented by a rule above). 972 + */ 973 + if (ref[1] == '\0') 974 + return -1; /* Component equals ".". */ 975 + } 976 + if (cp - ref >= 5 && !memcmp(cp - 5, ".lock", 5)) 977 + return -1; /* Refname ends with ".lock". */ 978 + return cp - ref; 979 + } 931 980 932 - /* we are at the beginning of the path component */ 933 - if (ch == '.') 934 - return CHECK_REF_FORMAT_ERROR; 935 - bad_type = bad_ref_char(ch); 936 - if (bad_type) { 937 - if (bad_type == 2 && (!*cp || *cp == '/') && 938 - ret == CHECK_REF_FORMAT_OK) 939 - ret = CHECK_REF_FORMAT_WILDCARD; 940 - else 941 - return CHECK_REF_FORMAT_ERROR; 942 - } 981 + int check_refname_format(const char *ref, int flags) 982 + { 983 + int component_len, component_count = 0; 943 984 944 - last = ch; 945 - /* scan the rest of the path component */ 946 - while ((ch = *cp++) != 0) { 947 - bad_type = bad_ref_char(ch); 948 - if (bad_type) 949 - return CHECK_REF_FORMAT_ERROR; 950 - if (ch == '/') 951 - break; 952 - if (last == '.' && ch == '.') 953 - return CHECK_REF_FORMAT_ERROR; 954 - if (last == '@' && ch == '{') 955 - return CHECK_REF_FORMAT_ERROR; 956 - last = ch; 957 - } 958 - level++; 959 - if (!ch) { 960 - if (ref <= cp - 2 && cp[-2] == '.') 961 - return CHECK_REF_FORMAT_ERROR; 962 - if (level < 2) 963 - return CHECK_REF_FORMAT_ONELEVEL; 964 - if (has_extension(ref, ".lock")) 965 - return CHECK_REF_FORMAT_ERROR; 966 - return ret; 985 + while (1) { 986 + /* We are at the start of a path component. */ 987 + component_len = check_refname_component(ref, flags); 988 + if (component_len < 0) { 989 + if ((flags & REFNAME_REFSPEC_PATTERN) && 990 + ref[0] == '*' && 991 + (ref[1] == '\0' || ref[1] == '/')) { 992 + /* Accept one wildcard as a full refname component. */ 993 + flags &= ~REFNAME_REFSPEC_PATTERN; 994 + component_len = 1; 995 + } else { 996 + return -1; 997 + } 967 998 } 999 + component_count++; 1000 + if (ref[component_len] == '\0') 1001 + break; 1002 + /* Skip to next component. */ 1003 + ref += component_len + 1; 968 1004 } 1005 + 1006 + if (ref[component_len - 1] == '.') 1007 + return -1; /* Refname ends with '.'. */ 1008 + if (!(flags & REFNAME_ALLOW_ONELEVEL) && component_count < 2) 1009 + return -1; /* Refname has only one component. */ 1010 + return 0; 969 1011 } 970 1012 971 1013 const char *prettify_refname(const char *name) ··· 1148 1190 struct ref_lock *lock_ref_sha1(const char *ref, const unsigned char *old_sha1) 1149 1191 { 1150 1192 char refpath[PATH_MAX]; 1151 - if (check_ref_format(ref)) 1193 + if (check_refname_format(ref, 0)) 1152 1194 return NULL; 1153 1195 strcpy(refpath, mkpath("refs/%s", ref)); 1154 1196 return lock_ref_sha1_basic(refpath, old_sha1, 0, NULL); ··· 1156 1198 1157 1199 struct ref_lock *lock_any_ref_for_update(const char *ref, const unsigned char *old_sha1, int flags) 1158 1200 { 1159 - switch (check_ref_format(ref)) { 1160 - default: 1201 + if (check_refname_format(ref, REFNAME_ALLOW_ONELEVEL)) 1161 1202 return NULL; 1162 - case 0: 1163 - case CHECK_REF_FORMAT_ONELEVEL: 1164 - return lock_ref_sha1_basic(ref, old_sha1, flags, NULL); 1165 - } 1203 + return lock_ref_sha1_basic(ref, old_sha1, flags, NULL); 1166 1204 } 1167 1205 1168 1206 static struct lock_file packlock;
+16 -5
refs.h
··· 97 97 */ 98 98 extern int for_each_reflog(each_ref_fn, void *); 99 99 100 - #define CHECK_REF_FORMAT_OK 0 101 - #define CHECK_REF_FORMAT_ERROR (-1) 102 - #define CHECK_REF_FORMAT_ONELEVEL (-2) 103 - #define CHECK_REF_FORMAT_WILDCARD (-3) 104 - extern int check_ref_format(const char *target); 100 + #define REFNAME_ALLOW_ONELEVEL 1 101 + #define REFNAME_REFSPEC_PATTERN 2 102 + #define REFNAME_DOT_COMPONENT 4 103 + 104 + /* 105 + * Return 0 iff ref has the correct format for a refname according to 106 + * the rules described in Documentation/git-check-ref-format.txt. If 107 + * REFNAME_ALLOW_ONELEVEL is set in flags, then accept one-level 108 + * reference names. If REFNAME_REFSPEC_PATTERN is set in flags, then 109 + * allow a "*" wildcard character in place of one of the name 110 + * components. No leading or repeated slashes are accepted. If 111 + * REFNAME_DOT_COMPONENT is set in flags, then allow refname 112 + * components to start with "." (but not a whole component equal to 113 + * "." or ".."). 114 + */ 115 + extern int check_refname_format(const char *ref, int flags); 105 116 106 117 extern const char *prettify_refname(const char *refname); 107 118 extern char *shorten_unambiguous_ref(const char *ref, int strict);
+15 -40
remote.c
··· 493 493 } 494 494 495 495 /* 496 - * We need to make sure the remote-tracking branches are well formed, but a 497 - * wildcard refspec in "struct refspec" must have a trailing slash. We 498 - * temporarily drop the trailing '/' while calling check_ref_format(), 499 - * and put it back. The caller knows that a CHECK_REF_FORMAT_ONELEVEL 500 - * error return is Ok for a wildcard refspec. 501 - */ 502 - static int verify_refname(char *name, int is_glob) 503 - { 504 - int result; 505 - 506 - result = check_ref_format(name); 507 - if (is_glob && result == CHECK_REF_FORMAT_WILDCARD) 508 - result = CHECK_REF_FORMAT_OK; 509 - return result; 510 - } 511 - 512 - /* 513 496 * This function frees a refspec array. 514 497 * Warning: code paths should be checked to ensure that the src 515 498 * and dst pointers are always freeable pointers as well ··· 532 515 static struct refspec *parse_refspec_internal(int nr_refspec, const char **refspec, int fetch, int verify) 533 516 { 534 517 int i; 535 - int st; 536 518 struct refspec *rs = xcalloc(sizeof(*rs), nr_refspec); 537 519 538 520 for (i = 0; i < nr_refspec; i++) { 539 521 size_t llen; 540 522 int is_glob; 541 523 const char *lhs, *rhs; 524 + int flags; 542 525 543 526 is_glob = 0; 544 527 ··· 576 559 577 560 rs[i].pattern = is_glob; 578 561 rs[i].src = xstrndup(lhs, llen); 562 + flags = REFNAME_ALLOW_ONELEVEL | (is_glob ? REFNAME_REFSPEC_PATTERN : 0); 579 563 580 564 if (fetch) { 581 565 /* ··· 585 569 */ 586 570 if (!*rs[i].src) 587 571 ; /* empty is ok */ 588 - else { 589 - st = verify_refname(rs[i].src, is_glob); 590 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) 591 - goto invalid; 592 - } 572 + else if (check_refname_format(rs[i].src, flags)) 573 + goto invalid; 593 574 /* 594 575 * RHS 595 576 * - missing is ok, and is same as empty. 596 577 * - empty is ok; it means not to store. 597 578 * - otherwise it must be a valid looking ref. 598 579 */ 599 - if (!rs[i].dst) { 580 + if (!rs[i].dst) 600 581 ; /* ok */ 601 - } else if (!*rs[i].dst) { 582 + else if (!*rs[i].dst) 602 583 ; /* ok */ 603 - } else { 604 - st = verify_refname(rs[i].dst, is_glob); 605 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) 606 - goto invalid; 607 - } 584 + else if (check_refname_format(rs[i].dst, flags)) 585 + goto invalid; 608 586 } else { 609 587 /* 610 588 * LHS ··· 616 594 if (!*rs[i].src) 617 595 ; /* empty is ok */ 618 596 else if (is_glob) { 619 - st = verify_refname(rs[i].src, is_glob); 620 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) 597 + if (check_refname_format(rs[i].src, flags)) 621 598 goto invalid; 622 599 } 623 600 else ··· 630 607 * - otherwise it must be a valid looking ref. 631 608 */ 632 609 if (!rs[i].dst) { 633 - st = verify_refname(rs[i].src, is_glob); 634 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) 610 + if (check_refname_format(rs[i].src, flags)) 635 611 goto invalid; 636 612 } else if (!*rs[i].dst) { 637 613 goto invalid; 638 614 } else { 639 - st = verify_refname(rs[i].dst, is_glob); 640 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) 615 + if (check_refname_format(rs[i].dst, flags)) 641 616 goto invalid; 642 617 } 643 618 } ··· 840 815 refspec->dst, &ret)) 841 816 return ret; 842 817 } else if (!strcmp(refspec->src, name)) 843 - return strdup(refspec->dst); 818 + return xstrdup(refspec->dst); 844 819 } 845 820 return NULL; 846 821 } ··· 1427 1402 1428 1403 for (rmp = &ref_map; *rmp; ) { 1429 1404 if ((*rmp)->peer_ref) { 1430 - int st = check_ref_format((*rmp)->peer_ref->name + 5); 1431 - if (st && st != CHECK_REF_FORMAT_ONELEVEL) { 1405 + if (check_refname_format((*rmp)->peer_ref->name + 5, 1406 + REFNAME_ALLOW_ONELEVEL)) { 1432 1407 struct ref *ignore = *rmp; 1433 1408 error("* Ignoring funny ref '%s' locally", 1434 1409 (*rmp)->peer_ref->name); ··· 1620 1595 int len; 1621 1596 1622 1597 /* we already know it starts with refs/ to get here */ 1623 - if (check_ref_format(refname + 5)) 1598 + if (check_refname_format(refname + 5, 0)) 1624 1599 return 0; 1625 1600 1626 1601 len = strlen(refname) + 1;
+2 -2
sha1_name.c
··· 966 966 { 967 967 strbuf_branchname(sb, name); 968 968 if (name[0] == '-') 969 - return CHECK_REF_FORMAT_ERROR; 969 + return -1; 970 970 strbuf_splice(sb, 0, 0, "refs/heads/", 11); 971 - return check_ref_format(sb->buf); 971 + return check_refname_format(sb->buf, 0); 972 972 } 973 973 974 974 /*
+107 -13
t/t1402-check-ref-format.sh
··· 5 5 . ./test-lib.sh 6 6 7 7 valid_ref() { 8 - test_expect_success "ref name '$1' is valid" \ 9 - "git check-ref-format '$1'" 8 + if test "$#" = 1 9 + then 10 + test_expect_success "ref name '$1' is valid" \ 11 + "git check-ref-format '$1'" 12 + else 13 + test_expect_success "ref name '$1' is valid with options $2" \ 14 + "git check-ref-format $2 '$1'" 15 + fi 10 16 } 11 17 invalid_ref() { 12 - test_expect_success "ref name '$1' is not valid" \ 13 - "test_must_fail git check-ref-format '$1'" 18 + if test "$#" = 1 19 + then 20 + test_expect_success "ref name '$1' is invalid" \ 21 + "test_must_fail git check-ref-format '$1'" 22 + else 23 + test_expect_success "ref name '$1' is invalid with options $2" \ 24 + "test_must_fail git check-ref-format $2 '$1'" 25 + fi 14 26 } 15 27 16 - valid_ref 'heads/foo' 17 - invalid_ref 'foo' 28 + invalid_ref '' 29 + invalid_ref '/' 30 + invalid_ref '/' --allow-onelevel 31 + invalid_ref '/' --normalize 32 + invalid_ref '/' '--allow-onelevel --normalize' 18 33 valid_ref 'foo/bar/baz' 19 - valid_ref 'refs///heads/foo' 34 + valid_ref 'foo/bar/baz' --normalize 35 + invalid_ref 'refs///heads/foo' 36 + valid_ref 'refs///heads/foo' --normalize 20 37 invalid_ref 'heads/foo/' 21 - valid_ref '/heads/foo' 22 - valid_ref '///heads/foo' 23 - invalid_ref '/foo' 38 + invalid_ref '/heads/foo' 39 + valid_ref '/heads/foo' --normalize 40 + invalid_ref '///heads/foo' 41 + valid_ref '///heads/foo' --normalize 24 42 invalid_ref './foo' 43 + invalid_ref './foo/bar' 44 + invalid_ref 'foo/./bar' 45 + invalid_ref 'foo/bar/.' 25 46 invalid_ref '.refs/foo' 26 47 invalid_ref 'heads/foo..bar' 27 48 invalid_ref 'heads/foo?bar' 28 49 valid_ref 'foo./bar' 29 50 invalid_ref 'heads/foo.lock' 51 + invalid_ref 'heads///foo.lock' 52 + invalid_ref 'foo.lock/bar' 53 + invalid_ref 'foo.lock///bar' 30 54 valid_ref 'heads/foo@bar' 31 55 invalid_ref 'heads/v@{ation' 32 56 invalid_ref 'heads/foo\bar' 33 57 invalid_ref "$(printf 'heads/foo\t')" 34 58 invalid_ref "$(printf 'heads/foo\177')" 35 59 valid_ref "$(printf 'heads/fu\303\237')" 60 + invalid_ref 'heads/*foo/bar' --refspec-pattern 61 + invalid_ref 'heads/foo*/bar' --refspec-pattern 62 + invalid_ref 'heads/f*o/bar' --refspec-pattern 63 + 64 + ref='foo' 65 + invalid_ref "$ref" 66 + valid_ref "$ref" --allow-onelevel 67 + invalid_ref "$ref" --refspec-pattern 68 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 69 + invalid_ref "$ref" --normalize 70 + valid_ref "$ref" '--allow-onelevel --normalize' 71 + 72 + ref='foo/bar' 73 + valid_ref "$ref" 74 + valid_ref "$ref" --allow-onelevel 75 + valid_ref "$ref" --refspec-pattern 76 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 77 + valid_ref "$ref" --normalize 78 + 79 + ref='foo/*' 80 + invalid_ref "$ref" 81 + invalid_ref "$ref" --allow-onelevel 82 + valid_ref "$ref" --refspec-pattern 83 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 84 + 85 + ref='*/foo' 86 + invalid_ref "$ref" 87 + invalid_ref "$ref" --allow-onelevel 88 + valid_ref "$ref" --refspec-pattern 89 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 90 + invalid_ref "$ref" --normalize 91 + valid_ref "$ref" '--refspec-pattern --normalize' 92 + 93 + ref='foo/*/bar' 94 + invalid_ref "$ref" 95 + invalid_ref "$ref" --allow-onelevel 96 + valid_ref "$ref" --refspec-pattern 97 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 98 + 99 + ref='*' 100 + invalid_ref "$ref" 101 + invalid_ref "$ref" --allow-onelevel 102 + invalid_ref "$ref" --refspec-pattern 103 + valid_ref "$ref" '--refspec-pattern --allow-onelevel' 104 + 105 + ref='foo/*/*' 106 + invalid_ref "$ref" --refspec-pattern 107 + invalid_ref "$ref" '--refspec-pattern --allow-onelevel' 108 + 109 + ref='*/foo/*' 110 + invalid_ref "$ref" --refspec-pattern 111 + invalid_ref "$ref" '--refspec-pattern --allow-onelevel' 112 + 113 + ref='*/*/foo' 114 + invalid_ref "$ref" --refspec-pattern 115 + invalid_ref "$ref" '--refspec-pattern --allow-onelevel' 116 + 117 + ref='/foo' 118 + invalid_ref "$ref" 119 + invalid_ref "$ref" --allow-onelevel 120 + invalid_ref "$ref" --refspec-pattern 121 + invalid_ref "$ref" '--refspec-pattern --allow-onelevel' 122 + invalid_ref "$ref" --normalize 123 + valid_ref "$ref" '--allow-onelevel --normalize' 124 + invalid_ref "$ref" '--refspec-pattern --normalize' 125 + valid_ref "$ref" '--refspec-pattern --allow-onelevel --normalize' 36 126 37 127 test_expect_success "check-ref-format --branch @{-1}" ' 38 128 T=$(git write-tree) && ··· 66 156 67 157 valid_ref_normalized() { 68 158 test_expect_success "ref name '$1' simplifies to '$2'" " 69 - refname=\$(git check-ref-format --print '$1') && 159 + refname=\$(git check-ref-format --normalize '$1') && 70 160 test \"\$refname\" = '$2'" 71 161 } 72 162 invalid_ref_normalized() { 73 - test_expect_success "check-ref-format --print rejects '$1'" " 74 - test_must_fail git check-ref-format --print '$1'" 163 + test_expect_success "check-ref-format --normalize rejects '$1'" " 164 + test_must_fail git check-ref-format --normalize '$1'" 75 165 } 76 166 77 167 valid_ref_normalized 'heads/foo' 'heads/foo' ··· 83 173 invalid_ref_normalized 'heads/foo/../bar' 84 174 invalid_ref_normalized 'heads/./foo' 85 175 invalid_ref_normalized 'heads\foo' 176 + invalid_ref_normalized 'heads/foo.lock' 177 + invalid_ref_normalized 'heads///foo.lock' 178 + invalid_ref_normalized 'foo.lock/bar' 179 + invalid_ref_normalized 'foo.lock///bar' 86 180 87 181 test_done
+6 -4
transport-helper.c
··· 183 183 ALLOC_GROW(refspecs, 184 184 refspec_nr + 1, 185 185 refspec_alloc); 186 - refspecs[refspec_nr++] = strdup(capname + strlen("refspec ")); 186 + refspecs[refspec_nr++] = xstrdup(capname + strlen("refspec ")); 187 187 } else if (!strcmp(capname, "connect")) { 188 188 data->connect = 1; 189 189 } else if (!prefixcmp(capname, "export-marks ")) { ··· 445 445 if (data->refspecs) 446 446 private = apply_refspecs(data->refspecs, data->refspec_nr, posn->name); 447 447 else 448 - private = strdup(posn->name); 449 - read_ref(private, posn->old_sha1); 450 - free(private); 448 + private = xstrdup(posn->name); 449 + if (private) { 450 + read_ref(private, posn->old_sha1); 451 + free(private); 452 + } 451 453 } 452 454 strbuf_release(&buf); 453 455 return 0;
+4 -12
transport.c
··· 755 755 continue; 756 756 757 757 remote = remote ? (remote + 1) : local; 758 - switch (check_ref_format(remote)) { 759 - case 0: /* ok */ 760 - case CHECK_REF_FORMAT_ONELEVEL: 761 - /* ok but a single level -- that is fine for 762 - * a match pattern. 763 - */ 764 - case CHECK_REF_FORMAT_WILDCARD: 765 - /* ok but ends with a pattern-match character */ 766 - continue; 767 - } 768 - die("remote part of refspec is not a valid name in %s", 769 - heads[i]); 758 + if (check_refname_format(remote, 759 + REFNAME_ALLOW_ONELEVEL|REFNAME_REFSPEC_PATTERN)) 760 + die("remote part of refspec is not a valid name in %s", 761 + heads[i]); 770 762 } 771 763 } 772 764
+1 -1
walker.c
··· 190 190 { 191 191 if (!get_sha1_hex(target, sha1)) 192 192 return 0; 193 - if (!check_ref_format(target)) { 193 + if (!check_refname_format(target, 0)) { 194 194 struct ref *ref = alloc_ref(target); 195 195 if (!walker->fetch_ref(walker, ref)) { 196 196 hashcpy(sha1, ref->old_sha1);