Merge pull request #7386 from dolthub/macneale4/clean-clone

Clean up unnecessary references when cloning

Fixes: https://github.com/dolthub/dolt/issues/7043
This commit is contained in:
Neil Macneale IV
2024-01-22 13:18:09 -08:00
committed by GitHub
3 changed files with 106 additions and 25 deletions
+21
View File
@@ -1254,6 +1254,27 @@ func (ddb *DoltDB) deleteRef(ctx context.Context, dref ref.DoltRef, replicationS
return err
}
// DeleteAllRefs Very destructive, use with caution. Not only does this drop all data, Dolt assume there is always
// a reference in the DB, so do not call this and walk away. The only use case for this method is the
// `dolt clone` command which strip everything from the remote's root object - dolt_clone stored procedure doesn't currently
// use this code path (TODO).
func (ddb *DoltDB) DeleteAllRefs(ctx context.Context) error {
dss, err := ddb.db.Datasets(ctx)
if err != nil {
return err
}
err = dss.IterAll(ctx, func(key string, addr hash.Hash) error {
ds, e := ddb.db.GetDataset(ctx, key)
if e != nil {
return e
}
_, e = ddb.db.Delete(ctx, ds, "")
return e
})
return err
}
// NewTagAtCommit create a new tag at the commit given.
func (ddb *DoltDB) NewTagAtCommit(ctx context.Context, tagRef ref.DoltRef, c *Commit, meta *datas.TagMeta) error {
if !IsValidTagRef(tagRef) {
+38 -25
View File
@@ -47,10 +47,10 @@ var ErrNoDataAtRemote = errors.New("remote at that url contains no Dolt data")
var ErrFailedToListBranches = errors.New("failed to list branches")
var ErrFailedToGetBranch = errors.New("could not get branch")
var ErrFailedToGetRootValue = errors.New("could not find root value")
var ErrFailedToResolveBranchRef = errors.New("could not resole branch ref")
var ErrFailedToCreateRemoteRef = errors.New("could not create remote ref")
var ErrFailedToCreateTagRef = errors.New("could not create tag ref")
var ErrFailedToCreateLocalBranch = errors.New("could not create local branch")
var ErrFailedToDeleteBranch = errors.New("could not delete local branch after clone")
var ErrFailedToUpdateDocs = errors.New("failed to update docs on the filesystem")
var ErrUserNotFound = errors.New("could not determine user name. run dolt config --global --add user.name")
var ErrEmailNotFound = errors.New("could not determine email. run dolt config --global --add user.email")
var ErrCloneFailed = errors.New("clone failed")
@@ -179,11 +179,20 @@ func CloneRemote(ctx context.Context, srcDB *doltdb.DoltDB, remoteName, branch s
return fmt.Errorf("%w; %s", ErrCloneFailed, err.Error())
}
branches, err := dEnv.DoltDB.GetBranches(ctx)
// Get all the refs from the remote. These branch refs will be translated to remote branch refs, tags will
// be preserved, and all other refs will be ignored.
srcRefHashes, err := dEnv.DoltDB.GetRefsWithHashes(ctx)
if err != nil {
return fmt.Errorf("%w; %s", ErrFailedToListBranches, err.Error())
return fmt.Errorf("%w; %s", ErrCloneFailed, err.Error())
}
branches := make([]ref.DoltRef, 0, len(srcRefHashes))
for _, refHash := range srcRefHashes {
if refHash.Ref.GetType() == ref.BranchRefType {
br := refHash.Ref.(ref.BranchRef)
branches = append(branches, br)
}
}
if branch == "" {
branch = env.GetDefaultBranch(dEnv, branches)
}
@@ -210,31 +219,35 @@ func CloneRemote(ctx context.Context, srcDB *doltdb.DoltDB, remoteName, branch s
return fmt.Errorf("%w: %s; %s", ErrFailedToGetRootValue, branch, err.Error())
}
// After actions.Clone, we have repository with a local branch for
// every branch in the remote. What we want is a remote branch ref for
// every branch in the remote. We iterate through local branches and
// create remote refs corresponding to each of them. We delete all of
// the local branches except for the one corresponding to |branch|.
for _, br := range branches {
if !singleBranch || br.GetPath() == branch {
cs, _ := doltdb.NewCommitSpec(br.GetPath())
cm, err := dEnv.DoltDB.Resolve(ctx, cs, nil)
if err != nil {
return fmt.Errorf("%w: %s; %s", ErrFailedToResolveBranchRef, br.String(), err.Error())
err = dEnv.DoltDB.DeleteAllRefs(ctx)
if err != nil {
return err
}
// Preserve only branch and tag references from the remote. Branches are translated into remote branches, tags are preserved.
for _, refHash := range srcRefHashes {
if refHash.Ref.GetType() == ref.BranchRefType {
br := refHash.Ref.(ref.BranchRef)
if !singleBranch || br.GetPath() == branch {
remoteRef := ref.NewRemoteRef(remoteName, br.GetPath())
err = dEnv.DoltDB.SetHead(ctx, remoteRef, refHash.Hash)
if err != nil {
return fmt.Errorf("%w: %s; %s", ErrFailedToCreateRemoteRef, remoteRef.String(), err.Error())
}
}
remoteRef := ref.NewRemoteRef(remoteName, br.GetPath())
err = dEnv.DoltDB.SetHeadToCommit(ctx, remoteRef, cm)
if err != nil {
return fmt.Errorf("%w: %s; %s", ErrFailedToCreateRemoteRef, remoteRef.String(), err.Error())
if br.GetPath() == branch {
// This is the only local branch after the clone is complete.
err = dEnv.DoltDB.SetHead(ctx, br, refHash.Hash)
if err != nil {
return fmt.Errorf("%w: %s; %s", ErrFailedToCreateLocalBranch, br.String(), err.Error())
}
}
}
if br.GetPath() != branch {
err := dEnv.DoltDB.DeleteBranch(ctx, br, nil)
} else if refHash.Ref.GetType() == ref.TagRefType {
tr := refHash.Ref.(ref.TagRef)
err = dEnv.DoltDB.SetHead(ctx, tr, refHash.Hash)
if err != nil {
return fmt.Errorf("%w: %s; %s", ErrFailedToDeleteBranch, br.String(), err.Error())
return fmt.Errorf("%w: %s; %s", ErrFailedToCreateTagRef, tr.String(), err.Error())
}
}
}
+47
View File
@@ -264,6 +264,53 @@ SQL
[[ ! "$output" =~ "README.md" ]] || false
}
@test "remotes: clone a complicated remote" {
dolt remote add test-remote http://localhost:50051/test-org/test-repo
dolt sql -q "CREATE TABLE test (pk int primary key)"
dolt sql -q "INSERT INTO test VALUES (1), (2), (3)"
dolt add test
dolt commit -m "test commit"
dolt push test-remote main
dolt push test-remote main:genesis-branch # In the beginning, there was a branch.
dolt tag customtag main
dolt push test-remote customtag
## Cloning from a remote which has tags and it's own remotes
## https://github.com/dolthub/dolt/issues/7043
cd "dolt-repo-clones"
dolt clone http://localhost:50051/test-org/test-repo clone-1
cd clone-1
# Assert that all the branches and tags are present
run dolt branch -a
[[ "$status" -eq 0 ]] || false
[[ "$output" =~ "remotes/origin/main" ]] || false
[[ "$output" =~ "remotes/origin/genesis-branch" ]] || false
run dolt tag
[[ "$status" -eq 0 ]] || false
[[ "$output" =~ "customtag" ]] || false
# Make a local branch, and backup to a different remote
dolt checkout -b clone-1-branch HEAD
dolt backup add mybackup http://localhost:50051/alternate-org/backup
dolt backup sync mybackup
cd ..
dolt clone http://localhost:50051/alternate-org/backup clone-2
cd clone-2
# Assert that the backup creates remote branches which are correct.
run dolt branch -a
[[ "$status" -eq 0 ]] || false
[[ "$output" =~ "remotes/origin/clone-1-branch" ]] || false
[[ "$output" =~ "remotes/origin/main" ]] || false
! [[ "$output" =~ "remotes/origin/genesis-branch" ]] || false
run dolt tag
[[ "$status" -eq 0 ]] || false
[[ "$output" =~ "customtag" ]] || false
}
@test "remotes: read tables test" {
# create table t1 and commit
dolt remote add test-remote http://localhost:50051/test-org/test-repo