github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/doltdb/commit.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  	"context"
    19  	"errors"
    20  
    21  	"github.com/dolthub/dolt/go/store/datas"
    22  	"github.com/dolthub/dolt/go/store/hash"
    23  	"github.com/dolthub/dolt/go/store/types"
    24  )
    25  
    26  const (
    27  	metaField        = datas.CommitMetaField
    28  	parentsField     = datas.ParentsField
    29  	parentsListField = datas.ParentsListField
    30  	rootValueField   = datas.ValueField
    31  )
    32  
    33  var errCommitHasNoMeta = errors.New("commit has no metadata")
    34  var errHasNoRootValue = errors.New("no root value")
    35  
    36  // Commit contains information on a commit that was written to noms
    37  type Commit struct {
    38  	vrw      types.ValueReadWriter
    39  	commitSt types.Struct
    40  	parents  []types.Ref
    41  }
    42  
    43  func NewCommit(vrw types.ValueReadWriter, commitSt types.Struct) *Commit {
    44  	parents, err := readParents(vrw, commitSt)
    45  	if err != nil {
    46  		panic(err)
    47  	}
    48  	return &Commit{vrw, commitSt, parents}
    49  }
    50  
    51  func readParents(vrw types.ValueReadWriter, commitSt types.Struct) ([]types.Ref, error) {
    52  	if l, found, err := commitSt.MaybeGet(parentsListField); err != nil {
    53  		return nil, err
    54  	} else if found && l != nil {
    55  		l := l.(types.List)
    56  		parents := make([]types.Ref, 0, l.Len())
    57  		i, err := l.Iterator(context.TODO())
    58  		if err != nil {
    59  			return nil, err
    60  		}
    61  		for {
    62  			v, err := i.Next(context.TODO())
    63  			if err != nil {
    64  				return nil, err
    65  			}
    66  			if v == nil {
    67  				break
    68  			}
    69  			parents = append(parents, v.(types.Ref))
    70  		}
    71  		return parents, nil
    72  	}
    73  	if s, found, err := commitSt.MaybeGet(parentsField); err != nil {
    74  		return nil, err
    75  	} else if found && s != nil {
    76  		s := s.(types.Set)
    77  		parents := make([]types.Ref, 0, s.Len())
    78  		i, err := s.Iterator(context.TODO())
    79  		if err != nil {
    80  			return nil, err
    81  		}
    82  		for {
    83  			v, err := i.Next(context.TODO())
    84  			if err != nil {
    85  				return nil, err
    86  			}
    87  			if v == nil {
    88  				break
    89  			}
    90  			parents = append(parents, v.(types.Ref))
    91  		}
    92  		return parents, nil
    93  	}
    94  	return nil, nil
    95  }
    96  
    97  // HashOf returns the hash of the commit
    98  func (c *Commit) HashOf() (hash.Hash, error) {
    99  	return c.commitSt.Hash(c.vrw.Format())
   100  }
   101  
   102  // GetCommitMeta gets the metadata associated with the commit
   103  func (c *Commit) GetCommitMeta() (*CommitMeta, error) {
   104  	metaVal, found, err := c.commitSt.MaybeGet(metaField)
   105  
   106  	if err != nil {
   107  		return nil, err
   108  	}
   109  
   110  	if !found {
   111  		return nil, errCommitHasNoMeta
   112  	}
   113  
   114  	if metaVal != nil {
   115  		if metaSt, ok := metaVal.(types.Struct); ok {
   116  			cm, err := commitMetaFromNomsSt(metaSt)
   117  
   118  			if err == nil {
   119  				return cm, nil
   120  			}
   121  		}
   122  	}
   123  
   124  	h, err := c.HashOf()
   125  
   126  	if err != nil {
   127  		return nil, errCommitHasNoMeta
   128  	}
   129  
   130  	return nil, errors.New(h.String() + " is a commit without the required metadata.")
   131  }
   132  
   133  // ParentHashes returns the commit hashes for all parent commits.
   134  func (c *Commit) ParentHashes(ctx context.Context) ([]hash.Hash, error) {
   135  	hashes := make([]hash.Hash, len(c.parents))
   136  	for i, pr := range c.parents {
   137  		hashes[i] = pr.TargetHash()
   138  	}
   139  	return hashes, nil
   140  }
   141  
   142  // NumParents gets the number of parents a commit has.
   143  func (c *Commit) NumParents() (int, error) {
   144  	return len(c.parents), nil
   145  }
   146  
   147  func (c *Commit) Height() (uint64, error) {
   148  	ref, err := types.NewRef(c.commitSt, c.vrw.Format())
   149  	if err != nil {
   150  		return 0, err
   151  	}
   152  	return ref.Height(), nil
   153  }
   154  
   155  func (c *Commit) getParent(ctx context.Context, idx int) (*types.Struct, error) {
   156  	parentRef := c.parents[idx]
   157  	targVal, err := parentRef.TargetValue(ctx, c.vrw)
   158  	if err != nil {
   159  		return nil, err
   160  	}
   161  	parentSt := targVal.(types.Struct)
   162  	return &parentSt, nil
   163  }
   164  
   165  // GetRootValue gets the RootValue of the commit.
   166  func (c *Commit) GetRootValue() (*RootValue, error) {
   167  	rootVal, _, err := c.commitSt.MaybeGet(rootValueField)
   168  
   169  	if err != nil {
   170  		return nil, err
   171  	}
   172  
   173  	if rootVal != nil {
   174  		if rootSt, ok := rootVal.(types.Struct); ok {
   175  			return newRootValue(c.vrw, rootSt)
   176  		}
   177  	}
   178  
   179  	return nil, errHasNoRootValue
   180  }
   181  
   182  // GetStRef returns a Noms Ref for this Commit's Noms commit Struct.
   183  func (c *Commit) GetStRef() (types.Ref, error) {
   184  	return types.NewRef(c.commitSt, c.vrw.Format())
   185  }
   186  
   187  var ErrNoCommonAncestor = errors.New("no common ancestor")
   188  
   189  func GetCommitAncestor(ctx context.Context, cm1, cm2 *Commit) (*Commit, error) {
   190  	ref1, err := types.NewRef(cm1.commitSt, cm1.vrw.Format())
   191  
   192  	if err != nil {
   193  		return nil, err
   194  	}
   195  
   196  	ref2, err := types.NewRef(cm2.commitSt, cm2.vrw.Format())
   197  
   198  	if err != nil {
   199  		return nil, err
   200  	}
   201  
   202  	ref, err := getCommitAncestorRef(ctx, ref1, ref2, cm1.vrw, cm2.vrw)
   203  
   204  	if err != nil {
   205  		return nil, err
   206  	}
   207  
   208  	targetVal, err := ref.TargetValue(ctx, cm1.vrw)
   209  
   210  	if err != nil {
   211  		return nil, err
   212  	}
   213  
   214  	ancestorSt := targetVal.(types.Struct)
   215  	return NewCommit(cm1.vrw, ancestorSt), nil
   216  }
   217  
   218  func getCommitAncestorRef(ctx context.Context, ref1, ref2 types.Ref, vrw1, vrw2 types.ValueReadWriter) (types.Ref, error) {
   219  	ancestorRef, ok, err := datas.FindCommonAncestor(ctx, ref1, ref2, vrw1, vrw2)
   220  
   221  	if err != nil {
   222  		return types.Ref{}, err
   223  	}
   224  
   225  	if !ok {
   226  		return types.Ref{}, ErrNoCommonAncestor
   227  	}
   228  
   229  	return ancestorRef, nil
   230  }
   231  
   232  func (c *Commit) CanFastForwardTo(ctx context.Context, new *Commit) (bool, error) {
   233  	ancestor, err := GetCommitAncestor(ctx, c, new)
   234  
   235  	if err != nil {
   236  		return false, err
   237  	} else if ancestor == nil {
   238  		return false, errors.New("cannot perform fast forward merge; commits have no common ancestor")
   239  	} else if ancestor.commitSt.Equals(c.commitSt) {
   240  		if ancestor.commitSt.Equals(new.commitSt) {
   241  			return true, ErrUpToDate
   242  		}
   243  		return true, nil
   244  	} else if ancestor.commitSt.Equals(new.commitSt) {
   245  		return false, ErrIsAhead
   246  	}
   247  
   248  	return false, nil
   249  }
   250  
   251  func (c *Commit) CanFastReverseTo(ctx context.Context, new *Commit) (bool, error) {
   252  	ancestor, err := GetCommitAncestor(ctx, c, new)
   253  
   254  	if err != nil {
   255  		return false, err
   256  	} else if ancestor == nil {
   257  		return false, errors.New("cannot perform fast forward merge; commits have no common ancestor")
   258  	} else if ancestor.commitSt.Equals(new.commitSt) {
   259  		if ancestor.commitSt.Equals(c.commitSt) {
   260  			return true, ErrUpToDate
   261  		}
   262  		return true, nil
   263  	} else if ancestor.commitSt.Equals(c.commitSt) {
   264  		return false, ErrIsBehind
   265  	}
   266  
   267  	return false, nil
   268  }
   269  
   270  func (c *Commit) GetAncestor(ctx context.Context, as *AncestorSpec) (*Commit, error) {
   271  	ancestorSt, err := getAncestor(ctx, c.vrw, c.commitSt, as)
   272  
   273  	if err != nil {
   274  		return nil, err
   275  	}
   276  
   277  	return NewCommit(c.vrw, ancestorSt), nil
   278  }