github.com/attic-labs/noms@v0.0.0-20210827224422-e5fa29d95e8b/go/datas/commit.go (about) 1 // Copyright 2016 Attic Labs, Inc. All rights reserved. 2 // Licensed under the Apache License, version 2.0: 3 // http://www.apache.org/licenses/LICENSE-2.0 4 5 package datas 6 7 import ( 8 "sort" 9 10 "github.com/attic-labs/noms/go/d" 11 "github.com/attic-labs/noms/go/hash" 12 "github.com/attic-labs/noms/go/nomdl" 13 "github.com/attic-labs/noms/go/types" 14 ) 15 16 const ( 17 ParentsField = "parents" 18 ValueField = "value" 19 MetaField = "meta" 20 commitName = "Commit" 21 ) 22 23 var commitTemplate = types.MakeStructTemplate(commitName, []string{MetaField, ParentsField, ValueField}) 24 25 var valueCommitType = nomdl.MustParseType(`Struct Commit { 26 meta: Struct {}, 27 parents: Set<Ref<Cycle<Commit>>>, 28 value: Value, 29 }`) 30 31 // NewCommit creates a new commit object. 32 // 33 // A commit has the following type: 34 // 35 // ``` 36 // struct Commit { 37 // meta: M, 38 // parents: Set<Ref<Cycle<Commit>>>, 39 // value: T, 40 // } 41 // ``` 42 // where M is a struct type and T is any type. 43 func NewCommit(value types.Value, parents types.Set, meta types.Struct) types.Struct { 44 return commitTemplate.NewStruct([]types.Value{meta, parents, value}) 45 } 46 47 // FindCommonAncestor returns the most recent common ancestor of c1 and c2, if 48 // one exists, setting ok to true. If there is no common ancestor, ok is set 49 // to false. 50 func FindCommonAncestor(c1, c2 types.Ref, vr types.ValueReader) (a types.Ref, ok bool) { 51 if !IsRefOfCommitType(types.TypeOf(c1)) { 52 d.Panic("FindCommonAncestor() called on %s", types.TypeOf(c1).Describe()) 53 } 54 if !IsRefOfCommitType(types.TypeOf(c2)) { 55 d.Panic("FindCommonAncestor() called on %s", types.TypeOf(c2).Describe()) 56 } 57 58 c1Q, c2Q := &types.RefByHeight{c1}, &types.RefByHeight{c2} 59 for !c1Q.Empty() && !c2Q.Empty() { 60 c1Ht, c2Ht := c1Q.MaxHeight(), c2Q.MaxHeight() 61 if c1Ht == c2Ht { 62 c1Parents, c2Parents := c1Q.PopRefsOfHeight(c1Ht), c2Q.PopRefsOfHeight(c2Ht) 63 if common, ok := findCommonRef(c1Parents, c2Parents); ok { 64 return common, true 65 } 66 parentsToQueue(c1Parents, c1Q, vr) 67 parentsToQueue(c2Parents, c2Q, vr) 68 } else if c1Ht > c2Ht { 69 parentsToQueue(c1Q.PopRefsOfHeight(c1Ht), c1Q, vr) 70 } else { 71 parentsToQueue(c2Q.PopRefsOfHeight(c2Ht), c2Q, vr) 72 } 73 } 74 return 75 } 76 77 func parentsToQueue(refs types.RefSlice, q *types.RefByHeight, vr types.ValueReader) { 78 for _, r := range refs { 79 c := r.TargetValue(vr).(types.Struct) 80 p := c.Get(ParentsField).(types.Set) 81 p.IterAll(func(v types.Value) { 82 q.PushBack(v.(types.Ref)) 83 }) 84 } 85 sort.Sort(q) 86 } 87 88 func findCommonRef(a, b types.RefSlice) (types.Ref, bool) { 89 toRefSet := func(s types.RefSlice) map[hash.Hash]types.Ref { 90 out := map[hash.Hash]types.Ref{} 91 for _, r := range s { 92 out[r.TargetHash()] = r 93 } 94 return out 95 } 96 97 aSet, bSet := toRefSet(a), toRefSet(b) 98 for s, r := range aSet { 99 if _, present := bSet[s]; present { 100 return r, true 101 } 102 } 103 return types.Ref{}, false 104 } 105 106 func makeCommitStructType(metaType, parentsType, valueType *types.Type) *types.Type { 107 return types.MakeStructType("Commit", 108 types.StructField{ 109 Name: MetaField, 110 Type: metaType, 111 }, 112 types.StructField{ 113 Name: ParentsField, 114 Type: parentsType, 115 }, 116 types.StructField{ 117 Name: ValueField, 118 Type: valueType, 119 }, 120 ) 121 } 122 123 func getRefElementType(t *types.Type) *types.Type { 124 d.PanicIfFalse(t.TargetKind() == types.RefKind) 125 return t.Desc.(types.CompoundDesc).ElemTypes[0] 126 } 127 128 func IsCommitType(t *types.Type) bool { 129 return types.IsSubtype(valueCommitType, t) 130 } 131 132 func IsCommit(v types.Value) bool { 133 return types.IsValueSubtypeOf(v, valueCommitType) 134 } 135 136 func IsRefOfCommitType(t *types.Type) bool { 137 return t.TargetKind() == types.RefKind && IsCommitType(getRefElementType(t)) 138 }