github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/migrate/traverse.go (about) 1 // Copyright 2022 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 migrate 16 17 import ( 18 "context" 19 "fmt" 20 21 "github.com/dolthub/dolt/go/store/datas" 22 "github.com/dolthub/dolt/go/store/hash" 23 24 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 25 "github.com/dolthub/dolt/go/libraries/doltcore/ref" 26 ) 27 28 // TraverseDAG traverses |old|, migrating values to |new|. 29 func TraverseDAG(ctx context.Context, menv Environment, old, new *doltdb.DoltDB) (err error) { 30 var heads []ref.DoltRef 31 var prog *progress 32 33 heads, err = old.GetHeadRefs(ctx) 34 if err != nil { 35 return err 36 } 37 38 datasdb := doltdb.HackDatasDatabaseFromDoltDB(new) 39 cs := datas.ChunkStoreFromDatabase(datasdb) 40 41 prog, err = newProgress(ctx, cs) 42 if err != nil { 43 return err 44 } 45 46 for i := range heads { 47 if err = traverseRefHistory(ctx, menv, heads[i], old, new, prog); err != nil { 48 return err 49 } 50 } 51 52 if err = validateBranchMapping(ctx, old, new); err != nil { 53 return err 54 } 55 56 // write the migrated commit mapping to a special branch 57 m, err := prog.Finalize(ctx) 58 if err != nil { 59 return err 60 } 61 if err = persistMigratedCommitMapping(ctx, new, m); err != nil { 62 return err 63 } 64 65 if err = old.Close(); err != nil { 66 return err 67 } 68 if err = new.Close(); err != nil { 69 return err 70 } 71 return 72 } 73 74 func traverseRefHistory(ctx context.Context, menv Environment, r ref.DoltRef, old, new *doltdb.DoltDB, prog *progress) error { 75 switch r.GetType() { 76 case ref.BranchRefType: 77 if err := traverseBranchHistory(ctx, menv, r, old, new, prog); err != nil { 78 return err 79 } 80 wsRef, err := ref.WorkingSetRefForHead(r) 81 if err != nil { 82 return err 83 } 84 return migrateWorkingSet(ctx, menv, r.(ref.BranchRef), wsRef, old, new) 85 86 case ref.TagRefType: 87 return traverseTagHistory(ctx, menv, r.(ref.TagRef), old, new, prog) 88 89 case ref.RemoteRefType: 90 return traverseBranchHistory(ctx, menv, r, old, new, prog) 91 92 case ref.WorkspaceRefType, ref.InternalRefType: 93 return nil 94 95 default: 96 panic(fmt.Sprintf("unknown ref type %s", r.String())) 97 } 98 } 99 100 func traverseBranchHistory(ctx context.Context, menv Environment, r ref.DoltRef, old, new *doltdb.DoltDB, prog *progress) error { 101 cm, err := old.ResolveCommitRef(ctx, r) 102 if err != nil { 103 return err 104 } 105 if err = traverseCommitHistory(ctx, menv, cm, new, prog); err != nil { 106 return err 107 } 108 109 oldHash, err := cm.HashOf() 110 if err != nil { 111 return err 112 } 113 newHash, err := prog.Get(ctx, oldHash) 114 if err != nil { 115 return err 116 } 117 118 return new.SetHead(ctx, r, newHash) 119 } 120 121 func traverseTagHistory(ctx context.Context, menv Environment, r ref.TagRef, old, new *doltdb.DoltDB, prog *progress) error { 122 t, err := old.ResolveTag(ctx, r) 123 if err != nil { 124 return err 125 } 126 127 if err = traverseCommitHistory(ctx, menv, t.Commit, new, prog); err != nil { 128 return err 129 } 130 131 oldHash, err := t.Commit.HashOf() 132 if err != nil { 133 return err 134 } 135 newHash, err := prog.Get(ctx, oldHash) 136 if err != nil { 137 return err 138 } 139 optCmt, err := new.ReadCommit(ctx, newHash) 140 if err != nil { 141 return err 142 } 143 cm, ok := optCmt.ToCommit() 144 if !ok { 145 return doltdb.ErrGhostCommitEncountered 146 } 147 148 return new.NewTagAtCommit(ctx, r, cm, t.Meta) 149 } 150 151 func traverseCommitHistory(ctx context.Context, menv Environment, cm *doltdb.Commit, new *doltdb.DoltDB, prog *progress) error { 152 ch, err := cm.HashOf() 153 if err != nil { 154 return err 155 } 156 ok, err := prog.Has(ctx, ch) 157 if err != nil || ok { 158 return err 159 } 160 161 for { 162 ph, err := cm.ParentHashes(ctx) 163 if err != nil { 164 return err 165 } 166 167 idx, err := firstAbsent(ctx, prog, ph) 168 if err != nil { 169 return err 170 } 171 if idx < 0 { 172 // parents for |cm| are done, migrate |cm| 173 if err = migrateCommit(ctx, menv, cm, new, prog); err != nil { 174 return err 175 } 176 // pop the stack, traverse upwards 177 cm, err = prog.Pop(ctx) 178 if err != nil { 179 return err 180 } 181 if cm == nil { 182 return nil // done 183 } 184 continue 185 } 186 187 // push the stack, traverse downwards 188 if err = prog.Push(ctx, cm); err != nil { 189 return err 190 } 191 optCmt, err := cm.GetParent(ctx, idx) 192 if err != nil { 193 return err 194 } 195 cm, ok = optCmt.ToCommit() 196 if !ok { 197 return doltdb.ErrGhostCommitEncountered 198 } 199 } 200 } 201 202 func firstAbsent(ctx context.Context, p *progress, addrs []hash.Hash) (int, error) { 203 for i := range addrs { 204 ok, err := p.Has(ctx, addrs[i]) 205 if err != nil { 206 return -1, err 207 } 208 if !ok { 209 return i, nil 210 } 211 } 212 return -1, nil 213 }