@matheusbsbs / matheus-bagliano / commits / cbdffe4093
Fork 0

check_ref_format(): tighten refname rules

This changes the rules for refnames to forbid: (1) a refname that contains "@{" in it. Some people and foreign SCM converter may have named their branches as frotz@24 and we still want to keep supporting it. However, "git branch frotz@{24}" is a disaster. It cannot even checked out because "git checkout frotz@{24}" will interpret it as "detach the HEAD at twenty-fourth reflog entry of the frotz branch". (2) a refname that ends with a dot. We already reject a path component that begins with a dot, primarily to avoid ambiguous range interpretation. If we allowed ".B" as a valid ref, it is unclear if "A...B" means "in dot-B but not in A" or "either in A or B but not in both". But for this to be complete, we need also to forbid "A." to avoid "in B but not in A-dot". This was not a problem in the original range notation, but we should have added this restriction when three-dot notation was introduced. Unlike "no dot at the beginning of any path component" rule, this rule does not have to be "no dot at the end of any path component", because you cannot abbreviate the tail end away, similar to you can say "dot-B" to mean "refs/heads/dot-B". For these reasons, it is not likely people created branches with these names on purpose, but we have allowed such names to be used for quite some time, and it is possible that people created such branches by mistake or by accident. To help people with branches with such unfortunate names to recover, we still allow "branch -d 'bad.'" to delete such branches, and also allow "branch -m bad. good" to rename them. Signed-off-by: Junio C Hamano <gitster@pobox.com>

Junio C Hamano committed Mar 21, 2009 at 13:27 UTC cbdffe4093be77bbb1408e54eead7865dd3bc33f
3 files changed +28 -7
Documentation/git-check-ref-format.txt
+5 -1
@@ -32,7 +32,9 @@ imposes the following rules on how refs are named:
32 caret `{caret}`, colon `:`, question-mark `?`, asterisk `*`,
33 or open bracket `[` anywhere;
34
35 -. It cannot end with a slash `/`.
35 +. They cannot end with a slash `/` nor a dot `.`.
36 +
37 +. They cannot contain a sequence `@{`.
38
39 These rules makes it easy for shell script based tools to parse
40 refnames, pathname expansion by the shell when a refname is used
@@ -51,6 +53,8 @@ refname expressions (see linkgit:git-rev-parse[1]). Namely:
53 It may also be used to select a specific object such as with
54 'git-cat-file': "git cat-file blob v1.3.3:refs.c".
55
56 +. at-open-brace `@{` is used as a notation to access a reflog entry.
57 +
58 With the `--branch` option, it expands a branch name shorthand and
59 prints the name of the branch the shorthand refers to.
60
builtin-branch.c
+14 -2
@@ -464,12 +464,21 @@ static void rename_branch(const char *oldname, const char *newname, int force)
464 struct strbuf oldref = STRBUF_INIT, newref = STRBUF_INIT, logmsg = STRBUF_INIT;
465 unsigned char sha1[20];
466 struct strbuf oldsection = STRBUF_INIT, newsection = STRBUF_INIT;
467 + int recovery = 0;
468
469 if (!oldname)
470 die("cannot rename the current branch while not on any.");
471
471 - if (strbuf_check_branch_ref(&oldref, oldname))
472 - die("Invalid branch name: '%s'", oldname);
472 + if (strbuf_check_branch_ref(&oldref, oldname)) {
473 + /*
474 + * Bad name --- this could be an attempt to rename a
475 + * ref that we used to allow to be created by accident.
476 + */
477 + if (resolve_ref(oldref.buf, sha1, 1, NULL))
478 + recovery = 1;
479 + else
480 + die("Invalid branch name: '%s'", oldname);
481 + }
482
483 if (strbuf_check_branch_ref(&newref, newname))
484 die("Invalid branch name: '%s'", newname);
@@ -484,6 +493,9 @@ static void rename_branch(const char *oldname, const char *newname, int force)
493 die("Branch rename failed");
494 strbuf_release(&logmsg);
495
496 + if (recovery)
497 + warning("Renamed a misnamed branch '%s' away", oldref.buf + 11);
498 +
499 /* no need to pass logmsg here as HEAD didn't really move */
500 if (!strcmp(oldname, head) && create_symref("HEAD", newref.buf, NULL))
501 die("Branch renamed to %s, but HEAD is not updated!", newname);
refs.c
+9 -4
@@ -693,7 +693,7 @@ static inline int bad_ref_char(int ch)
693
694 int check_ref_format(const char *ref)
695 {
696 - int ch, level, bad_type;
696 + int ch, level, bad_type, last;
697 int ret = CHECK_REF_FORMAT_OK;
698 const char *cp = ref;
699
@@ -717,19 +717,24 @@ int check_ref_format(const char *ref)
717 return CHECK_REF_FORMAT_ERROR;
718 }
719
720 + last = ch;
721 /* scan the rest of the path component */
722 while ((ch = *cp++) != 0) {
723 bad_type = bad_ref_char(ch);
723 - if (bad_type) {
724 + if (bad_type)
725 return CHECK_REF_FORMAT_ERROR;
725 - }
726 if (ch == '/')
727 break;
728 - if (ch == '.' && *cp == '.')
728 + if (last == '.' && ch == '.')
729 + return CHECK_REF_FORMAT_ERROR;
730 + if (last == '@' && ch == '{')
731 return CHECK_REF_FORMAT_ERROR;
732 + last = ch;
733 }
734 level++;
735 if (!ch) {
736 + if (ref <= cp - 2 && cp[-2] == '.')
737 + return CHECK_REF_FORMAT_ERROR;
738 if (level < 2)
739 return CHECK_REF_FORMAT_ONELEVEL;
740 return ret;