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  }