github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/doltdb/commit_spec.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package doltdb 16 17 import ( 18 "regexp" 19 "strings" 20 21 "github.com/dolthub/dolt/go/libraries/doltcore/ref" 22 ) 23 24 var hashRegex = regexp.MustCompile(`^[0-9a-v]{32}$`) 25 26 const head string = "head" 27 28 // IsValidUserBranchName returns true if name isn't a valid commit hash, it is not named "head" and 29 // it matches the regular expression `[0-9a-z]+[-_0-9a-z]*[0-9a-z]+$` 30 func IsValidUserBranchName(name string) bool { 31 return name != head && !hashRegex.MatchString(name) && ref.IsValidBranchName(name) 32 } 33 34 // IsValidBranchRef validates that a BranchRef doesn't violate naming constraints. 35 func IsValidBranchRef(dref ref.DoltRef) bool { 36 return dref.GetType() == ref.BranchRefType && IsValidUserBranchName(dref.GetPath()) 37 } 38 39 // IsValidTagRef validates that a TagRef doesn't violate naming constraints. 40 func IsValidTagRef(dref ref.DoltRef) bool { 41 s := dref.GetPath() 42 return dref.GetType() == ref.TagRefType && 43 s != head && 44 !hashRegex.MatchString(s) && 45 ref.IsValidTagName(s) 46 } 47 48 func IsValidCommitHash(s string) bool { 49 return hashRegex.MatchString(s) 50 } 51 52 type commitSpecType string 53 54 const ( 55 refCommitSpec commitSpecType = "ref" 56 hashCommitSpec commitSpecType = "hash" 57 headCommitSpec commitSpecType = "head" 58 ) 59 60 // CommitSpec handles three different types of string representations of commits. Commits can either be represented 61 // by the hash of the commit, a branch name, or using "head" to represent the latest commit of the current branch. 62 // An Ancestor spec can be appended to the end of any of these in order to reach commits that are in the ancestor tree 63 // of the referenced commit. 64 type CommitSpec struct { 65 baseSpec string 66 csType commitSpecType 67 aSpec *AncestorSpec 68 } 69 70 // NewCommitSpec parses a string specifying a commit using dolt commit spec 71 // syntax and returns a |*CommitSpec|. A commit spec has a base commit and an 72 // optional ancestor specification. The syntax admits three types of base 73 // commit references: 74 // * head -- the literal string HEAD specifies the HEAD reference of the 75 // current working set. 76 // * a commit hash, like 46m0aqr8c1vuv76ml33cdtr8722hsbhn -- a fully specified 77 // commit hash. 78 // * a ref -- referring to a branch or tag reference in the current dolt database. 79 // Examples of branch refs include `master`, `heads/master`, `refs/heads/master`, 80 // `origin/master`, `refs/remotes/origin/master`. 81 // Examples of tag refs include `v1.0`, `tags/v1.0`, `refs/tags/v1.0`, 82 // `origin/v1.0`, `refs/remotes/origin/v1.0`. 83 // 84 // A commit spec has an optional ancestor specification, which describes a 85 // traversal of commit parents, starting at the base commit, in order to arrive 86 // at the actually specified commit. See |AncestorSpec|. Examples of 87 // |CommitSpec|s: 88 // * HEAD 89 // * master 90 // * HEAD~ 91 // * remotes/origin/master~~ 92 // * refs/heads/my-feature-branch^2~ 93 // 94 // Constructing a |CommitSpec| does not mean the specified branch or commit 95 // exists. This carries a description of how to find the specified commit. See 96 // |doltdb.Resolve| for resolving a |CommitSpec| to a |Commit|. 97 func NewCommitSpec(cSpecStr string) (*CommitSpec, error) { 98 cSpecStrLwr := strings.TrimSpace(cSpecStr) 99 100 name, as, err := SplitAncestorSpec(cSpecStrLwr) 101 if err != nil { 102 return nil, err 103 } 104 105 if strings.ToLower(name) == head { 106 return &CommitSpec{head, headCommitSpec, as}, nil 107 } 108 if hashRegex.MatchString(name) { 109 return &CommitSpec{name, hashCommitSpec, as}, nil 110 } 111 if !ref.IsValidBranchName(name) { 112 return nil, ErrInvalidBranchOrHash 113 } 114 return &CommitSpec{name, refCommitSpec, as}, nil 115 }