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  }