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 }