Files
dolt/go/libraries/doltcore/ref/ref.go
2023-03-03 16:25:24 -08:00

172 lines
4.6 KiB
Go

// Copyright 2019 Dolthub, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package ref
import (
"errors"
"fmt"
"strings"
)
// ErrUnknownRefType is the error returned when parsing a ref in the format refs/type/... where type is unknown
var ErrUnknownRefType = errors.New("unknown ref type")
const (
refPrefix = "refs/"
remotesPrefix = "remotes/"
)
// IsRef returns true if the string is a reference string (meanings it starts with the prefix refs/)
func IsRef(str string) bool {
return strings.HasPrefix(str, refPrefix)
}
// RefType is the type of the reference, and this value follows the refPrefix in a ref string. e.g. refs/type/...
type RefType string
const (
// BranchRefType is a reference to a local branch in the format refs/heads/...
BranchRefType RefType = "heads"
// RemoteRefType is a reference to a local remote tracking branch
RemoteRefType RefType = "remotes"
// InternalRefType is a reference to a dolt internal commit
InternalRefType RefType = "internal"
// TagRefType is a reference to commit tag
TagRefType RefType = "tags"
// WorkspaceRefType is a reference to a workspace
WorkspaceRefType RefType = "workspaces"
// StashRefType is a reference to a stashes
StashRefType RefType = "stashes"
)
// HeadRefTypes are the ref types that point to a HEAD and contain a Commit struct. These are the types that are
// returned by GetHeadRefs. Other ref types don't point to Commits necessarily, so aren't in this list and must be
// asked for explicitly.
var HeadRefTypes = map[RefType]struct{}{
BranchRefType: {},
RemoteRefType: {},
InternalRefType: {},
TagRefType: {},
WorkspaceRefType: {},
StashRefType: {},
}
// PrefixForType returns what a reference string for a given type should start with
func PrefixForType(refType RefType) string {
return refPrefix + string(refType) + "/"
}
type UpdateMode struct {
Force bool
}
var ForceUpdate = UpdateMode{true}
var FastForwardOnly = UpdateMode{false}
// DoltRef is a reference to a commit.
type DoltRef interface {
fmt.Stringer
// GetType returns the RefType of this ref
GetType() RefType
// GetPath returns the identifier for the reference
GetPath() string
}
// Equals returns true if two DoltRefs have the same Type and Path
func Equals(dr, other DoltRef) bool {
if dr == nil && other == nil {
return true
} else if dr == nil || other == nil {
return false
}
return dr.GetType() == other.GetType() && dr.GetPath() == other.GetPath()
}
// EqualsStr compares a DoltRef to a reference string to see if they are referring to the same thing
func EqualsStr(dr DoltRef, str string) bool {
other, err := Parse(str)
if err != nil {
return false
}
return Equals(dr, other)
}
// String converts the DoltRef to a reference string in the format refs/type/path
func String(dr DoltRef) string {
return PrefixForType(dr.GetType()) + dr.GetPath()
}
// MarshalJSON implements the json Marshaler interface to json encode DoltRefs as their string representation
func MarshalJSON(dr DoltRef) ([]byte, error) {
str := dr.String()
data := make([]byte, len(str)+2)
data[0] = '"'
data[len(str)+1] = '"'
for i, b := range str {
data[i+1] = byte(b)
}
return data, nil
}
// Parse will parse ref strings and return a DoltRef or an error for refs that can't be parsed.
// refs without a RefType prefix ("refs/heads/", "refs/tags/", etc) are assumed to be branches)
func Parse(str string) (DoltRef, error) {
if !IsRef(str) {
if strings.HasPrefix(str, remotesPrefix) {
return NewRemoteRefFromPathStr(str)
} else {
return NewBranchRef(str), nil
}
}
for rType := range HeadRefTypes {
prefix := PrefixForType(rType)
if strings.HasPrefix(str, prefix) {
str = str[len(prefix):]
switch rType {
case BranchRefType:
return NewBranchRef(str), nil
case RemoteRefType:
return NewRemoteRefFromPathStr(str)
case InternalRefType:
return NewInternalRef(str), nil
case TagRefType:
return NewTagRef(str), nil
case WorkspaceRefType:
return NewWorkspaceRef(str), nil
case StashRefType:
return NewStashRef(), nil
default:
panic("unknown type " + rType)
}
}
}
return nil, ErrUnknownRefType
}