github.com/hasnat/dolt/go@v0.0.0-20210628190320-9eb5d843fbb7/libraries/doltcore/merge/conflict_splitter.go (about) 1 // Copyright 2020 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 merge 16 17 import ( 18 "context" 19 20 "github.com/dolthub/dolt/go/libraries/doltcore/row" 21 "github.com/dolthub/dolt/go/libraries/doltcore/rowconv" 22 "github.com/dolthub/dolt/go/libraries/doltcore/schema" 23 "github.com/dolthub/dolt/go/libraries/doltcore/table/pipeline" 24 "github.com/dolthub/dolt/go/libraries/doltcore/table/untyped" 25 "github.com/dolthub/dolt/go/store/types" 26 ) 27 28 // MergeVersion defines which version a value of a row corresponds to 29 type MergeVersion int 30 31 const ( 32 // BaseVersion represents the state of a row at the most recent ancestor 33 BaseVersion MergeVersion = iota 34 // OurVersion represents the state of a row on our branch that is being merged into 35 OurVersion 36 // TheirVersion represents the state of a row on their branch which we are merging 37 TheirVersion 38 // Blank is used for displaying a row without a version label 39 Blank 40 ) 41 42 var typeToMergeVersion = map[string]MergeVersion{ 43 oursStr: OurVersion, 44 theirsStr: TheirVersion, 45 baseStr: BaseVersion, 46 } 47 48 // ConflictsSplitter splits a conflict into base, ours, and their version of a row 49 type ConflictSplitter struct { 50 joiner *rowconv.Joiner 51 sch schema.Schema 52 converters map[string]*rowconv.RowConverter 53 } 54 55 // NewConflictSplitter creates a new ConflictSplitter 56 func NewConflictSplitter(ctx context.Context, vrw types.ValueReadWriter, joiner *rowconv.Joiner) (ConflictSplitter, error) { 57 baseSch := joiner.SchemaForName(baseStr) 58 ourSch := joiner.SchemaForName(baseStr) 59 theirSch := joiner.SchemaForName(theirsStr) 60 61 sch, err := untyped.UntypedSchemaUnion(baseSch, ourSch, theirSch) 62 63 if err != nil { 64 return ConflictSplitter{}, err 65 } 66 67 converters := make(map[string]*rowconv.RowConverter) 68 converters[oursStr], err = tagMappingConverter(ctx, vrw, ourSch, sch) 69 70 if err != nil { 71 return ConflictSplitter{}, err 72 } 73 74 converters[theirsStr], err = tagMappingConverter(ctx, vrw, theirSch, sch) 75 76 if err != nil { 77 return ConflictSplitter{}, err 78 } 79 80 converters[baseStr], err = tagMappingConverter(ctx, vrw, baseSch, sch) 81 82 if err != nil { 83 return ConflictSplitter{}, err 84 } 85 86 return ConflictSplitter{joiner: joiner, sch: sch, converters: converters}, nil 87 } 88 89 // GetSchema returns the common schema which all rows will share 90 func (ds ConflictSplitter) GetSchema() schema.Schema { 91 return ds.sch 92 } 93 94 // SplitConflicts takes a conflict row and splits it into ours, theirs, and base versions and provides pipeline properties 95 // which can be used to distinguished which is which and what type of conflict occurred. 96 func (ds ConflictSplitter) SplitConflicts(inRow row.Row, _ pipeline.ReadableMap) (rowData []*pipeline.TransformedRowResult, badRowDetails string) { 97 rows, err := ds.joiner.Split(inRow) 98 if err != nil { 99 return nil, err.Error() 100 } 101 102 var baseRow row.Row 103 has := make(map[string]bool) 104 baseRow, has[baseStr] = rows[baseStr] 105 _, has[oursStr] = rows[oursStr] 106 _, has[theirsStr] = rows[theirsStr] 107 108 if has[baseStr] { 109 baseRow, err = ds.converters[baseStr].Convert(baseRow) 110 111 if err != nil { 112 return nil, err.Error() 113 } 114 } 115 116 rowData = make([]*pipeline.TransformedRowResult, 0, 3) 117 for _, rowType := range []string{baseStr, oursStr, theirsStr} { 118 row, ok := rows[rowType] 119 props := map[string]interface{}{mergeVersionProp: typeToMergeVersion[rowType]} 120 121 if ok { 122 converted, err := ds.converters[rowType].Convert(row) 123 124 if err != nil { 125 return nil, err.Error() 126 } 127 128 if !has[baseStr] { 129 props[mergeRowOperation] = types.DiffChangeAdded 130 } else { 131 props[mergeRowOperation] = types.DiffChangeModified 132 } 133 134 rowData = append(rowData, &pipeline.TransformedRowResult{RowData: converted, PropertyUpdates: props}) 135 } else if rowType != baseStr { 136 props[mergeRowOperation] = types.DiffChangeRemoved 137 rowData = append(rowData, &pipeline.TransformedRowResult{RowData: baseRow, PropertyUpdates: props}) 138 } 139 } 140 141 return rowData, "" 142 }