github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/store/datas/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  // This file incorporates work covered by the following copyright and
    16  // permission notice:
    17  //
    18  // Copyright 2016 Attic Labs, Inc. All rights reserved.
    19  // Licensed under the Apache License, version 2.0:
    20  // http://www.apache.org/licenses/LICENSE-2.0
    21  
    22  package datas
    23  
    24  import (
    25  	"context"
    26  	"fmt"
    27  	"sort"
    28  
    29  	"github.com/dolthub/dolt/go/store/d"
    30  	"github.com/dolthub/dolt/go/store/hash"
    31  	"github.com/dolthub/dolt/go/store/nomdl"
    32  	"github.com/dolthub/dolt/go/store/types"
    33  )
    34  
    35  const (
    36  	ParentsField = "parents"
    37  	// Added in July, 2020. Commits created with versions before this was
    38  	// added have only a Set of parents. Commits created after this was
    39  	// added carry a List of parents, because parent order can matter.
    40  	// `"parents"` is still written as a Set as well, so that commits
    41  	// created with newer versions of still usable by older versions.
    42  	ParentsListField = "parents_list"
    43  	ValueField       = "value"
    44  	CommitMetaField  = "meta"
    45  	CommitName       = "Commit"
    46  )
    47  
    48  var commitTemplate = types.MakeStructTemplate(CommitName, []string{CommitMetaField, ParentsField, ParentsListField, ValueField})
    49  
    50  var valueCommitType = nomdl.MustParseType(`Struct Commit {
    51          meta: Struct {},
    52          parents: Set<Ref<Cycle<Commit>>>,
    53          parents_list?: List<Ref<Cycle<Commit>>>,
    54          value: Value,
    55  }`)
    56  
    57  // NewCommit creates a new commit object.
    58  //
    59  // A commit has the following type:
    60  //
    61  // ```
    62  // struct Commit {
    63  //   meta: M,
    64  //   parents: Set<Ref<Cycle<Commit>>>,
    65  //   parentsList: List<Ref<Cycle<Commit>>>,
    66  //   value: T,
    67  // }
    68  // ```
    69  // where M is a struct type and T is any type.
    70  func NewCommit(ctx context.Context, value types.Value, parentsList types.List, meta types.Struct) (types.Struct, error) {
    71  	parentsSet, err := parentsList.ToSet(ctx)
    72  	if err != nil {
    73  		return types.EmptyStruct(meta.Format()), err
    74  	}
    75  	return commitTemplate.NewStruct(meta.Format(), []types.Value{meta, parentsSet, parentsList, value})
    76  }
    77  
    78  // FindCommonAncestor returns the most recent common ancestor of c1 and c2, if
    79  // one exists, setting ok to true. If there is no common ancestor, ok is set
    80  // to false. Refs of |c1| are dereferenced through |vr1|, while refs of |c2|
    81  // are dereference through |vr2|.
    82  func FindCommonAncestor(ctx context.Context, c1, c2 types.Ref, vr1, vr2 types.ValueReader) (a types.Ref, ok bool, err error) {
    83  	t1, err := types.TypeOf(c1)
    84  
    85  	if err != nil {
    86  		return types.Ref{}, false, err
    87  	}
    88  
    89  	// precondition checks
    90  	if !IsRefOfCommitType(c1.Format(), t1) {
    91  		d.Panic("first reference is not a commit")
    92  	}
    93  
    94  	t2, err := types.TypeOf(c2)
    95  
    96  	if err != nil {
    97  		return types.Ref{}, false, err
    98  	}
    99  
   100  	if !IsRefOfCommitType(c2.Format(), t2) {
   101  		d.Panic("second reference is not a commit")
   102  	}
   103  
   104  	c1Q, c2Q := &types.RefByHeight{c1}, &types.RefByHeight{c2}
   105  	for !c1Q.Empty() && !c2Q.Empty() {
   106  		c1Ht, c2Ht := c1Q.MaxHeight(), c2Q.MaxHeight()
   107  		if c1Ht == c2Ht {
   108  			c1Parents, c2Parents := c1Q.PopRefsOfHeight(c1Ht), c2Q.PopRefsOfHeight(c2Ht)
   109  			if common, ok := findCommonRef(c1Parents, c2Parents); ok {
   110  				return common, true, nil
   111  			}
   112  			err = parentsToQueue(ctx, c1Parents, c1Q, vr1)
   113  			if err != nil {
   114  				return types.Ref{}, false, err
   115  			}
   116  			err = parentsToQueue(ctx, c2Parents, c2Q, vr2)
   117  			if err != nil {
   118  				return types.Ref{}, false, err
   119  			}
   120  		} else if c1Ht > c2Ht {
   121  			err = parentsToQueue(ctx, c1Q.PopRefsOfHeight(c1Ht), c1Q, vr1)
   122  			if err != nil {
   123  				return types.Ref{}, false, err
   124  			}
   125  		} else {
   126  			err = parentsToQueue(ctx, c2Q.PopRefsOfHeight(c2Ht), c2Q, vr2)
   127  			if err != nil {
   128  				return types.Ref{}, false, err
   129  			}
   130  		}
   131  	}
   132  
   133  	return a, ok, nil
   134  }
   135  
   136  func parentsToQueue(ctx context.Context, refs types.RefSlice, q *types.RefByHeight, vr types.ValueReader) error {
   137  	seen := make(map[hash.Hash]bool)
   138  	for _, r := range refs {
   139  		if _, ok := seen[r.TargetHash()]; ok {
   140  			continue
   141  		}
   142  		seen[r.TargetHash()] = true
   143  
   144  		v, err := r.TargetValue(ctx, vr)
   145  		if err != nil {
   146  			return err
   147  		}
   148  		if v == nil {
   149  			return fmt.Errorf("target not found: %v", r.TargetHash())
   150  		}
   151  
   152  		c, ok := v.(types.Struct)
   153  		if !ok {
   154  			return fmt.Errorf("target ref is not struct: %v", v)
   155  		}
   156  		ps, ok, err := c.MaybeGet(ParentsListField)
   157  		if err != nil {
   158  			return err
   159  		}
   160  		if ok {
   161  			p := ps.(types.List)
   162  			err = p.Iter(ctx, func(v types.Value, _ uint64) (stop bool, err error) {
   163  				q.PushBack(v.(types.Ref))
   164  				return
   165  			})
   166  			if err != nil {
   167  				return err
   168  			}
   169  		} else {
   170  			ps, ok, err := c.MaybeGet(ParentsField)
   171  			if err != nil {
   172  				return err
   173  			}
   174  			if ok {
   175  				p := ps.(types.Set)
   176  				err = p.Iter(ctx, func(v types.Value) (stop bool, err error) {
   177  					q.PushBack(v.(types.Ref))
   178  					return
   179  				})
   180  				if err != nil {
   181  					return err
   182  				}
   183  			}
   184  		}
   185  	}
   186  
   187  	sort.Sort(q)
   188  	return nil
   189  }
   190  
   191  func findCommonRef(a, b types.RefSlice) (types.Ref, bool) {
   192  	toRefSet := func(s types.RefSlice) map[hash.Hash]types.Ref {
   193  		out := map[hash.Hash]types.Ref{}
   194  		for _, r := range s {
   195  			out[r.TargetHash()] = r
   196  		}
   197  		return out
   198  	}
   199  
   200  	aSet, bSet := toRefSet(a), toRefSet(b)
   201  	for s, r := range aSet {
   202  		if _, present := bSet[s]; present {
   203  			return r, true
   204  		}
   205  	}
   206  	return types.Ref{}, false
   207  }
   208  
   209  func makeCommitStructType(metaType, parentsType, parentsListType, valueType *types.Type) (*types.Type, error) {
   210  	return types.MakeStructType(CommitName,
   211  		types.StructField{
   212  			Name: CommitMetaField,
   213  			Type: metaType,
   214  		},
   215  		types.StructField{
   216  			Name: ParentsField,
   217  			Type: parentsType,
   218  		},
   219  		types.StructField{
   220  			Name: ParentsListField,
   221  			Type: parentsListType,
   222  		},
   223  		types.StructField{
   224  			Name: ValueField,
   225  			Type: valueType,
   226  		},
   227  	)
   228  }
   229  
   230  func getRefElementType(t *types.Type) *types.Type {
   231  	// precondition checks
   232  	d.PanicIfFalse(t.TargetKind() == types.RefKind)
   233  
   234  	return t.Desc.(types.CompoundDesc).ElemTypes[0]
   235  }
   236  
   237  func IsCommitType(nbf *types.NomsBinFormat, t *types.Type) bool {
   238  	return types.IsSubtype(nbf, valueCommitType, t)
   239  }
   240  
   241  func IsCommit(v types.Value) (bool, error) {
   242  	if s, ok := v.(types.Struct); !ok {
   243  		return false, nil
   244  	} else {
   245  		return types.IsValueSubtypeOf(s.Format(), v, valueCommitType)
   246  	}
   247  }
   248  
   249  func IsRefOfCommitType(nbf *types.NomsBinFormat, t *types.Type) bool {
   250  	return t.TargetKind() == types.RefKind && IsCommitType(nbf, getRefElementType(t))
   251  }