github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/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 type commitSpecType string 49 50 const ( 51 refCommitSpec commitSpecType = "ref" 52 hashCommitSpec commitSpecType = "hash" 53 headCommitSpec commitSpecType = "head" 54 ) 55 56 // CommitSpec handles three different types of string representations of commits. Commits can either be represented 57 // by the hash of the commit, a branch name, or using "head" to represent the latest commit of the current branch. 58 // An Ancestor spec can be appended to the end of any of these in order to reach commits that are in the ancestor tree 59 // of the referenced commit. 60 type CommitSpec struct { 61 baseSpec string 62 csType commitSpecType 63 aSpec *AncestorSpec 64 } 65 66 // NewCommitSpec parses a string specifying a commit using dolt commit spec 67 // syntax and returns a |*CommitSpec|. A commit spec has a base commit and an 68 // optional ancestor specification. The syntax admits three types of base 69 // commit references: 70 // * head -- the literal string HEAD specifies the HEAD reference of the 71 // current working set. 72 // * a commit hash, like 46m0aqr8c1vuv76ml33cdtr8722hsbhn -- a fully specified 73 // commit hash. 74 // * a ref -- referring to a branch or tag reference in the current dolt database. 75 // Examples of branch refs include `master`, `heads/master`, `refs/heads/master`, 76 // `origin/master`, `refs/remotes/origin/master`. 77 // Examples of tag refs include `v1.0`, `tags/v1.0`, `refs/tags/v1.0`, 78 // `origin/v1.0`, `refs/remotes/origin/v1.0`. 79 // 80 // A commit spec has an optional ancestor specification, which describes a 81 // traversal of commit parents, starting at the base commit, in order to arrive 82 // at the actually specified commit. See |AncestorSpec|. Examples of 83 // |CommitSpec|s: 84 // * HEAD 85 // * master 86 // * HEAD~ 87 // * remotes/origin/master~~ 88 // * refs/heads/my-feature-branch^2~ 89 // 90 // Constructing a |CommitSpec| does not mean the specified branch or commit 91 // exists. This carries a description of how to find the specified commit. See 92 // |doltdb.Resolve| for resolving a |CommitSpec| to a |Commit|. 93 func NewCommitSpec(cSpecStr string) (*CommitSpec, error) { 94 cSpecStrLwr := strings.TrimSpace(cSpecStr) 95 96 name, as, err := SplitAncestorSpec(cSpecStrLwr) 97 if err != nil { 98 return nil, err 99 } 100 101 if strings.ToLower(name) == head { 102 return &CommitSpec{head, headCommitSpec, as}, nil 103 } 104 if hashRegex.MatchString(name) { 105 return &CommitSpec{name, hashCommitSpec, as}, nil 106 } 107 if !ref.IsValidBranchName(name) { 108 return nil, ErrInvalidBranchOrHash 109 } 110 return &CommitSpec{name, refCommitSpec, as}, nil 111 }