github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/ref/ref.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 ref 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 ) 22 23 // ErrUnknownRefType is the error returned when parsing a ref in the format refs/type/... where type is unknown 24 var ErrUnknownRefType = errors.New("unknown ref type") 25 26 const ( 27 refPrefix = "refs/" 28 remotesPrefix = "remotes/" 29 ) 30 31 // IsRef returns true if the string is a reference string (meanings it starts with the prefix refs/) 32 func IsRef(str string) bool { 33 return strings.HasPrefix(str, refPrefix) 34 } 35 36 // RefType is the type of the reference, and this value follows the refPrefix in a ref string. e.g. refs/type/... 37 type RefType string 38 39 const ( 40 // BranchRefType is a reference to a local branch in the format refs/heads/... 41 BranchRefType RefType = "heads" 42 43 // RemoteRefType is a reference to a local remote tracking branch 44 RemoteRefType RefType = "remotes" 45 46 // InternalRefType is a reference to a dolt internal commit 47 InternalRefType RefType = "internal" 48 49 // TagRefType is a reference to commit tag 50 TagRefType RefType = "tags" 51 52 // WorkspaceRefType is a reference to a workspace 53 WorkspaceRefType RefType = "workspaces" 54 55 // StashRefType is a reference to a stash list 56 StashRefType RefType = "stashes" 57 58 // StatsRefType is a reference to a statistics table 59 StatsRefType RefType = "statistics" 60 ) 61 62 // HeadRefTypes are the ref types that point to a HEAD and contain a Commit struct. These are the types that are 63 // returned by GetHeadRefs. Other ref types don't point to Commits necessarily, so aren't in this list and must be 64 // asked for explicitly. 65 var HeadRefTypes = map[RefType]struct{}{ 66 BranchRefType: {}, 67 RemoteRefType: {}, 68 InternalRefType: {}, 69 TagRefType: {}, 70 WorkspaceRefType: {}, 71 } 72 73 var StashRefTypes = map[RefType]struct{}{ 74 StashRefType: {}, 75 } 76 77 // StatsRefTypes point to a table address hash, not a commit hash. 78 var StatsRefTypes = map[RefType]struct{}{ 79 StatsRefType: {}, 80 } 81 82 // PrefixForType returns what a reference string for a given type should start with 83 func PrefixForType(refType RefType) string { 84 return refPrefix + string(refType) + "/" 85 } 86 87 type UpdateMode struct { 88 Force bool 89 Prune bool 90 } 91 92 var ForceUpdate = UpdateMode{true, false} 93 var FastForwardOnly = UpdateMode{false, false} 94 95 // DoltRef is a reference to a commit. 96 type DoltRef interface { 97 fmt.Stringer 98 99 // GetType returns the RefType of this ref 100 GetType() RefType 101 102 // GetPath returns the identifier for the reference 103 GetPath() string 104 } 105 106 // Equals returns true if two DoltRefs have the same Type and Path 107 func Equals(dr, other DoltRef) bool { 108 if dr == nil && other == nil { 109 return true 110 } else if dr == nil || other == nil { 111 return false 112 } 113 114 return dr.GetType() == other.GetType() && dr.GetPath() == other.GetPath() 115 } 116 117 // EqualsCaseInsensitive returns true if two DoltRefs have the same Type and Path, comparing the path case-insensitive 118 func EqualsCaseInsensitive(dr, other DoltRef) bool { 119 if dr == nil && other == nil { 120 return true 121 } else if dr == nil || other == nil { 122 return false 123 } 124 125 return dr.GetType() == other.GetType() && strings.ToLower(dr.GetPath()) == strings.ToLower(other.GetPath()) 126 } 127 128 // EqualsStr compares a DoltRef to a reference string to see if they are referring to the same thing 129 func EqualsStr(dr DoltRef, str string) bool { 130 other, err := Parse(str) 131 132 if err != nil { 133 return false 134 } 135 136 return Equals(dr, other) 137 } 138 139 // String converts the DoltRef to a reference string in the format refs/type/path 140 func String(dr DoltRef) string { 141 return PrefixForType(dr.GetType()) + dr.GetPath() 142 } 143 144 // MarshalJSON implements the json Marshaler interface to json encode DoltRefs as their string representation 145 func MarshalJSON(dr DoltRef) ([]byte, error) { 146 str := dr.String() 147 data := make([]byte, len(str)+2) 148 149 data[0] = '"' 150 data[len(str)+1] = '"' 151 152 for i, b := range str { 153 data[i+1] = byte(b) 154 } 155 156 return data, nil 157 } 158 159 // Parse will parse ref strings and return a DoltRef or an error for refs that can't be parsed. 160 // refs without a RefType prefix ("refs/heads/", "refs/tags/", etc) are assumed to be branches) 161 func Parse(str string) (DoltRef, error) { 162 if !IsRef(str) { 163 if strings.HasPrefix(str, remotesPrefix) { 164 return NewRemoteRefFromPathStr(str) 165 } else { 166 return NewBranchRef(str), nil 167 } 168 } 169 170 for rType := range HeadRefTypes { 171 prefix := PrefixForType(rType) 172 if strings.HasPrefix(str, prefix) { 173 str = str[len(prefix):] 174 switch rType { 175 case BranchRefType: 176 return NewBranchRef(str), nil 177 case RemoteRefType: 178 return NewRemoteRefFromPathStr(str) 179 case InternalRefType: 180 return NewInternalRef(str), nil 181 case TagRefType: 182 return NewTagRef(str), nil 183 case WorkspaceRefType: 184 return NewWorkspaceRef(str), nil 185 default: 186 panic("unknown type " + rType) 187 } 188 } 189 } 190 191 for rType := range StashRefTypes { 192 prefix := PrefixForType(rType) 193 if strings.HasPrefix(str, prefix) { 194 str = str[len(prefix):] 195 switch rType { 196 case StashRefType: 197 return NewStashRef(), nil 198 default: 199 panic("unknown type " + rType) 200 } 201 } 202 } 203 204 if prefix := PrefixForType(StatsRefType); strings.HasPrefix(str, prefix) { 205 return NewStatsRef(str[len(prefix):]), nil 206 } 207 208 return nil, ErrUnknownRefType 209 }