mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-26 10:18:56 -06:00
allow init with empty working set (#4255)
This commit is contained in:
12
go/libraries/doltcore/env/actions/branch.go
vendored
12
go/libraries/doltcore/env/actions/branch.go
vendored
@@ -243,6 +243,12 @@ func createBranch(ctx context.Context, dbData env.DbData, newBranch, startingPoi
|
||||
// should be the pre-checkout head. The returned roots struct has |Head| set to |branchRoot|.
|
||||
func UpdateRootsForBranch(ctx context.Context, roots doltdb.Roots, branchRoot *doltdb.RootValue, force bool) (doltdb.Roots, error) {
|
||||
conflicts := set.NewStrSet([]string{})
|
||||
if roots.Head == nil {
|
||||
roots.Working = branchRoot
|
||||
roots.Staged = branchRoot
|
||||
roots.Head = branchRoot
|
||||
return roots, nil
|
||||
}
|
||||
|
||||
wrkTblHashes, err := moveModifiedTables(ctx, roots.Head, branchRoot, roots.Working, conflicts, force)
|
||||
if err != nil {
|
||||
@@ -314,10 +320,10 @@ func CheckoutBranch(ctx context.Context, dEnv *env.DoltEnv, brName string, force
|
||||
}
|
||||
|
||||
roots, err := dEnv.Roots(ctx)
|
||||
if errors.Is(err, doltdb.ErrBranchNotFound) {
|
||||
// roots will be empty/nil if the working set is not set (working set is not set if the current branch was deleted)
|
||||
if errors.Is(err, doltdb.ErrBranchNotFound) || errors.Is(err, doltdb.ErrWorkingSetNotFound) {
|
||||
roots, err = dEnv.RecoveryRoots(ctx)
|
||||
}
|
||||
if err != nil {
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
|
||||
7
go/libraries/doltcore/env/environment.go
vendored
7
go/libraries/doltcore/env/environment.go
vendored
@@ -175,11 +175,8 @@ func loadWithFormat(ctx context.Context, hdp HomeDirProvider, fs filesys.Filesys
|
||||
if rsErr == nil && dbLoadErr == nil && dbFormatErr == nil {
|
||||
// If the working set isn't present in the DB, create it from the repo state. This step can be removed post 1.0.
|
||||
_, err := dEnv.WorkingSet(ctx)
|
||||
if err == doltdb.ErrWorkingSetNotFound {
|
||||
err := dEnv.initWorkingSetFromRepoState(ctx)
|
||||
if err != nil {
|
||||
dEnv.RSLoadErr = err
|
||||
}
|
||||
if errors.Is(err, doltdb.ErrWorkingSetNotFound) {
|
||||
_ = dEnv.initWorkingSetFromRepoState(ctx)
|
||||
} else if err != nil {
|
||||
dEnv.RSLoadErr = err
|
||||
}
|
||||
|
||||
@@ -96,9 +96,9 @@ func DoDoltBranch(ctx *sql.Context, args []string) (int, error) {
|
||||
case apr.Contains(cli.CopyFlag):
|
||||
err = copyBranch(ctx, dbData, apr)
|
||||
case apr.Contains(cli.MoveFlag):
|
||||
err = renameBranch(ctx, dbData, apr)
|
||||
err = renameBranch(ctx, dbData, apr, dSess, dbName)
|
||||
case apr.Contains(cli.DeleteFlag), apr.Contains(cli.DeleteForceFlag):
|
||||
err = deleteBranches(ctx, apr, dbData)
|
||||
err = deleteBranches(ctx, dbData, apr, dSess, dbName)
|
||||
default:
|
||||
err = createNewBranch(ctx, dbData, apr)
|
||||
}
|
||||
@@ -110,7 +110,9 @@ func DoDoltBranch(ctx *sql.Context, args []string) (int, error) {
|
||||
}
|
||||
}
|
||||
|
||||
func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {
|
||||
// renameBranch takes DoltSession and database name to try accessing file system for dolt database.
|
||||
// If the oldBranch being renamed is the current branch on CLI, then RepoState head will be updated with the newBranch ref.
|
||||
func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults, sess *dsess.DoltSession, dbName string) error {
|
||||
if apr.NArg() != 2 {
|
||||
return InvalidArgErr
|
||||
}
|
||||
@@ -127,14 +129,46 @@ func renameBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseRe
|
||||
}
|
||||
}
|
||||
|
||||
return actions.RenameBranch(ctx, dbData, loadConfig(ctx), oldBranchName, newBranchName, force)
|
||||
err := actions.RenameBranch(ctx, dbData, loadConfig(ctx), oldBranchName, newBranchName, force)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// The current branch on CLI can be deleted as user can be on different branch on SQL and delete it from SQL session.
|
||||
// To update current head info on RepoState, we need DoltEnv to load CLI environment.
|
||||
if fs, err := sess.Provider().FileSystemForDatabase(dbName); err == nil {
|
||||
if repoState, err := env.LoadRepoState(fs); err == nil {
|
||||
if repoState.Head.Ref.GetPath() == oldBranchName {
|
||||
repoState.Head.Ref = ref.NewBranchRef(newBranchName)
|
||||
repoState.Save(fs)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteBranches(ctx *sql.Context, apr *argparser.ArgParseResults, dbData env.DbData) error {
|
||||
// deleteBranches takes DoltSession and database name to try accessing file system for dolt database.
|
||||
// If the database is not session state db and the branch being deleted is the current branch on CLI, it will update
|
||||
// the RepoState to set head as empty branchRef.
|
||||
func deleteBranches(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults, sess *dsess.DoltSession, dbName string) error {
|
||||
if apr.NArg() == 0 {
|
||||
return InvalidArgErr
|
||||
}
|
||||
|
||||
// The current branch on CLI can be deleted as user can be on different branch on SQL and delete it from SQL session.
|
||||
// To update current head info on RepoState, we need DoltEnv to load CLI environment.
|
||||
var rs *env.RepoState
|
||||
var headOnCLI string
|
||||
fs, err := sess.Provider().FileSystemForDatabase(dbName)
|
||||
if err == nil {
|
||||
if repoState, err := env.LoadRepoState(fs); err == nil {
|
||||
rs = repoState
|
||||
headOnCLI = repoState.Head.Ref.GetPath()
|
||||
}
|
||||
}
|
||||
|
||||
var updateFS = false
|
||||
for _, branchName := range apr.Args {
|
||||
if len(branchName) == 0 {
|
||||
return EmptyBranchNameErr
|
||||
@@ -142,19 +176,27 @@ func deleteBranches(ctx *sql.Context, apr *argparser.ArgParseResults, dbData env
|
||||
force := apr.Contains(cli.DeleteForceFlag) || apr.Contains(cli.ForceFlag)
|
||||
|
||||
if !force {
|
||||
err := validateBranchNotActiveInAnySession(ctx, branchName)
|
||||
err = validateBranchNotActiveInAnySession(ctx, branchName)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
err := actions.DeleteBranch(ctx, dbData, loadConfig(ctx), branchName, actions.DeleteOptions{
|
||||
err = actions.DeleteBranch(ctx, dbData, loadConfig(ctx), branchName, actions.DeleteOptions{
|
||||
Force: force,
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if headOnCLI == branchName {
|
||||
updateFS = true
|
||||
}
|
||||
}
|
||||
|
||||
if fs != nil && updateFS {
|
||||
rs.Head.Ref = ref.NewBranchRef("")
|
||||
rs.Save(fs)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -220,16 +262,23 @@ func loadConfig(ctx *sql.Context) *env.DoltCliConfig {
|
||||
}
|
||||
|
||||
func createNewBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {
|
||||
if apr.NArg() != 1 {
|
||||
return InvalidArgErr
|
||||
var branchName string
|
||||
var startPt = "HEAD"
|
||||
if apr.NArg() == 1 {
|
||||
branchName = apr.Arg(0)
|
||||
} else if apr.NArg() == 2 {
|
||||
branchName = apr.Arg(0)
|
||||
startPt = apr.Arg(1)
|
||||
if len(startPt) == 0 {
|
||||
return InvalidArgErr
|
||||
}
|
||||
}
|
||||
|
||||
branchName := apr.Arg(0)
|
||||
if len(branchName) == 0 {
|
||||
return EmptyBranchNameErr
|
||||
}
|
||||
|
||||
return actions.CreateBranchWithStartPt(ctx, dbData, branchName, "HEAD", apr.Contains(cli.ForceFlag))
|
||||
return actions.CreateBranchWithStartPt(ctx, dbData, branchName, startPt, apr.Contains(cli.ForceFlag))
|
||||
}
|
||||
|
||||
func copyBranch(ctx *sql.Context, dbData env.DbData, apr *argparser.ArgParseResults) error {
|
||||
|
||||
@@ -15,13 +15,12 @@
|
||||
package dtables
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/ref"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
|
||||
)
|
||||
|
||||
@@ -177,86 +176,23 @@ type branchWriter struct {
|
||||
bt *BranchesTable
|
||||
}
|
||||
|
||||
func branchAndHashFromRow(r sql.Row) (string, string, error) {
|
||||
branchName, ok := r[0].(string)
|
||||
|
||||
if !ok {
|
||||
return "", "", errors.New("invalid value type for branch")
|
||||
} else if !doltdb.IsValidUserBranchName(branchName) {
|
||||
return "", "", doltdb.ErrInvBranchName
|
||||
}
|
||||
|
||||
commitHash, ok := r[1].(string)
|
||||
|
||||
if !ok {
|
||||
return "", "", errors.New("invalid value type for hash")
|
||||
}
|
||||
|
||||
return branchName, commitHash, nil
|
||||
}
|
||||
|
||||
// Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process
|
||||
// for the insert operation, which may involve many rows. After all rows in an operation have been processed, Close
|
||||
// is called.
|
||||
func (bWr branchWriter) Insert(ctx *sql.Context, r sql.Row) error {
|
||||
branchName, commitHash, err := branchAndHashFromRow(r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
cs, err := doltdb.NewCommitSpec(commitHash)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
ddb := bWr.bt.ddb
|
||||
cm, err := ddb.Resolve(ctx, cs, nil)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branchRef := ref.NewBranchRef(branchName)
|
||||
|
||||
// TODO: this isn't safe in a SQL context, since we have to update the working set of the new branch and it's a
|
||||
// race. It needs to be able to retry the same as committing a transaction.
|
||||
err = ddb.NewBranchAtCommit(ctx, branchRef, cm)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return bWr.bt.ddb.ExecuteCommitHooks(ctx, branchRef.String())
|
||||
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
|
||||
}
|
||||
|
||||
// Update the given row. Provides both the old and new rows.
|
||||
func (bWr branchWriter) Update(ctx *sql.Context, old sql.Row, new sql.Row) error {
|
||||
return bWr.Insert(ctx, new)
|
||||
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
|
||||
}
|
||||
|
||||
// Delete deletes the given row. Returns ErrDeleteRowNotFound if the row was not found. Delete will be called once for
|
||||
// each row to process for the delete operation, which may involve many rows. After all rows have been processed,
|
||||
// Close is called.
|
||||
func (bWr branchWriter) Delete(ctx *sql.Context, r sql.Row) error {
|
||||
branchName, _, err := branchAndHashFromRow(r)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
brRef := ref.NewBranchRef(branchName)
|
||||
exists, err := bWr.bt.ddb.HasRef(ctx, brRef)
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !exists {
|
||||
return sql.ErrDeleteRowNotFound.New()
|
||||
}
|
||||
|
||||
return bWr.bt.ddb.DeleteBranch(ctx, brRef)
|
||||
return fmt.Errorf("the dolt_branches table is read-only; use the dolt_branch stored procedure to edit remotes")
|
||||
}
|
||||
|
||||
// StatementBegin implements the interface sql.TableEditor. Currently a no-op.
|
||||
|
||||
@@ -453,7 +453,7 @@ func (d *DoltHarness) SnapshotTable(db sql.VersionedDatabase, name string, asOf
|
||||
// TODO: there's a bug in test setup with transactions, where the HEAD session var gets overwritten on transaction
|
||||
// start, so we quote it here instead
|
||||
// query := "insert into dolt_branches (name, hash) values ('" + asOfString + "', @@" + dsess.HeadKey(ddb.Name()) + ")"
|
||||
query := "insert into dolt_branches (name, hash) values ('" + asOfString + "', '" + headHash.(string) + "')"
|
||||
query := "CALL dolt_branch('" + asOfString + "', '" + headHash.(string) + "')"
|
||||
|
||||
_, iter, err = e.Query(ctx,
|
||||
query)
|
||||
|
||||
@@ -2199,7 +2199,7 @@ var MergeScripts = []queries.ScriptTest{
|
||||
"CREATE TRIGGER trigger1 BEFORE INSERT ON x FOR EACH ROW SET new.a = new.a + 1",
|
||||
"CALL dolt_add('-A')",
|
||||
"CALL dolt_commit('-m', 'added table with trigger')",
|
||||
"INSERT INTO dolt_branches (name, hash) VALUES ('other',hashof('main'))",
|
||||
"CALL dolt_branch('-c', 'main', 'other')",
|
||||
// create trigger2 on main
|
||||
"CREATE TRIGGER trigger2 BEFORE INSERT ON x FOR EACH ROW SET new.a = (new.a * 2) + 10",
|
||||
"CALL dolt_commit('-am', 'created trigger2 on main')",
|
||||
|
||||
@@ -21,16 +21,45 @@ make_it() {
|
||||
dolt branch -c main to_keep
|
||||
}
|
||||
|
||||
@test "deleted-branches: can checkout existing branch after checked out branch is deleted" {
|
||||
@test "deleted-branches: can checkout existing branch after checked out branch on CLI is deleted" {
|
||||
make_it
|
||||
|
||||
dolt sql -q 'delete from dolt_branches where name = "main"'
|
||||
run dolt status
|
||||
[[ "$output" =~ "On branch main" ]] || false
|
||||
|
||||
run dolt sql -q 'call dolt_branch("-D", "main");'
|
||||
[ $status -eq 1 ]
|
||||
[[ "$output" =~ "attempted to delete checked out branch" ]] || false
|
||||
|
||||
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");'
|
||||
|
||||
dolt branch -av
|
||||
|
||||
dolt checkout to_keep
|
||||
}
|
||||
|
||||
@test "deleted-branches: attempt to delete the last branch when currently on no branch" {
|
||||
make_it
|
||||
|
||||
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");'
|
||||
|
||||
dolt branch -av
|
||||
|
||||
run dolt branch -D to_keep
|
||||
[[ "$output" =~ "cannot delete the last branch" ]] || false
|
||||
}
|
||||
|
||||
@test "deleted-branches: renaming current branch on CLI deletes that branch and sets the current branch to the new branch on CLI" {
|
||||
make_it
|
||||
|
||||
dolt sql -q 'call dolt_checkout("to_keep"); call dolt_branch("-m", "main", "master");'
|
||||
|
||||
dolt branch -av
|
||||
|
||||
run dolt status
|
||||
[[ "$output" =~ "On branch master" ]] || false
|
||||
}
|
||||
|
||||
@test "deleted-branches: can SQL connect with dolt_default_branch set to existing branch when checked out branch is deleted" {
|
||||
make_it
|
||||
|
||||
@@ -38,7 +67,7 @@ make_it() {
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" "SET @@GLOBAL.dolt_repo_$$_default_branch = 'to_keep'" ""
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" "SELECT * FROM test" "id\n" ""
|
||||
}
|
||||
@@ -48,7 +77,7 @@ make_it() {
|
||||
|
||||
start_sql_server "dolt_repo_$$"
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
|
||||
|
||||
# Against the default branch it fails
|
||||
run server_query "dolt_repo_$$" 1 "" dolt "" "SELECT * FROM test" "id\n" ""
|
||||
@@ -93,7 +122,7 @@ make_it() {
|
||||
|
||||
start_sql_server "dolt_repo_$$"
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
|
||||
|
||||
# We are able to use a database branch revision in the connection string
|
||||
server_query "dolt_repo_$$/to_keep" 1 dolt "" "SELECT * FROM test;"
|
||||
@@ -111,7 +140,7 @@ make_it() {
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" "SET @@GLOBAL.dolt_repo_$$_default_branch = 'to_keep'" ""
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'delete from dolt_branches where name = "main"' ""
|
||||
server_query "dolt_repo_$$" 1 dolt "" 'call dolt_checkout("to_keep"); call dolt_branch("-D", "main");' ""
|
||||
|
||||
server_query "dolt_repo_$$" 1 dolt "" "SELECT * FROM test" ""
|
||||
|
||||
|
||||
@@ -160,22 +160,6 @@ teardown() {
|
||||
[[ "$output" =~ "t1" ]] || false
|
||||
}
|
||||
|
||||
@test "replication: push on branch table update" {
|
||||
cd repo1
|
||||
dolt config --local --add sqlserver.global.dolt_replicate_to_remote backup1
|
||||
dolt sql -q "create table t1 (a int primary key)"
|
||||
dolt add -A
|
||||
dolt sql -q "UPDATE dolt_branches SET hash = COMMIT('--author', '{user_name} <{email_address}>','-m', 'cm') WHERE name = 'main' AND hash = @@repo1_head"
|
||||
|
||||
cd ..
|
||||
dolt clone file://./bac1 repo2
|
||||
cd repo2
|
||||
run dolt ls
|
||||
[ "$status" -eq 0 ]
|
||||
[ "${#lines[@]}" -eq 2 ]
|
||||
[[ "$output" =~ "t1" ]] || false
|
||||
}
|
||||
|
||||
@test "replication: push on call dolt_branch(..." {
|
||||
cd repo1
|
||||
dolt config --local --add sqlserver.global.dolt_replicate_to_remote backup1
|
||||
|
||||
@@ -119,7 +119,6 @@ SQL
|
||||
|
||||
dolt sql -b <<SQL
|
||||
SELECT dolt_reset('--hard');
|
||||
REPLACE INTO dolt_branches (hash,name) VALUES (@@test_head,'main');
|
||||
SQL
|
||||
|
||||
run dolt status
|
||||
@@ -129,7 +128,6 @@ SQL
|
||||
dolt sql -b <<SQL
|
||||
INSERT INTO test VALUES (1,1);
|
||||
SELECT dolt_reset('--hard');
|
||||
REPLACE INTO dolt_branches (hash,name) VALUES (@@test_head,'main');
|
||||
SQL
|
||||
|
||||
run dolt status
|
||||
|
||||
@@ -423,9 +423,7 @@ SQL
|
||||
[ "$status" -eq 0 ]
|
||||
[[ "$output" =~ "test" ]] || false
|
||||
|
||||
server_query repo1 1 dolt "" "
|
||||
SELECT DOLT_RESET('--hard');
|
||||
REPLACE INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
|
||||
server_query repo1 1 dolt "" "SELECT DOLT_RESET('--hard');"
|
||||
|
||||
run dolt status
|
||||
[ "$status" -eq 0 ]
|
||||
@@ -436,8 +434,7 @@ SQL
|
||||
|
||||
server_query repo1 1 dolt "" "
|
||||
INSERT INTO test VALUES (8,8);
|
||||
SELECT DOLT_RESET('--hard');
|
||||
REPLACE INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
|
||||
SELECT DOLT_RESET('--hard');"
|
||||
|
||||
run dolt status
|
||||
[ "$status" -eq 0 ]
|
||||
@@ -777,7 +774,7 @@ SQL
|
||||
INSERT INTO one_pk (pk,c1,c2) VALUES (2,2,2),(3,3,3);
|
||||
CALL DOLT_ADD('.');
|
||||
SELECT commit('-am', 'test commit message', '--author', 'John Doe <john@example.com>');
|
||||
INSERT INTO dolt_branches (name,hash) VALUES ('main', @@repo1_head);"
|
||||
CALL DOLT_BRANCH('main', @@repo1_head);"
|
||||
|
||||
server_query repo1 1 dolt "" "call dolt_add('.')" "status\n0"
|
||||
run dolt ls
|
||||
|
||||
@@ -528,9 +528,18 @@ SQL
|
||||
[[ "$output" =~ "1" ]] || false
|
||||
}
|
||||
|
||||
@test "system-tables: cannot delete last branch in dolt_branches" {
|
||||
@test "system-tables: dolt_branches is read-only" {
|
||||
run dolt sql -q "DELETE FROM dolt_branches"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "read-only" ]] || false
|
||||
|
||||
run dolt sql -q "INSERT INTO dolt_branches (name,hash) VALUES ('branch1', 'main');"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "read-only" ]] || false
|
||||
|
||||
run dolt sql -q "UPDATE dolt_branches SET name = 'branch1' WHERE name = 'main'"
|
||||
[ "$status" -ne 0 ]
|
||||
[[ "$output" =~ "read-only" ]] || false
|
||||
}
|
||||
|
||||
@test "system-tables: dolt diff includes changes from initial commit" {
|
||||
|
||||
Reference in New Issue
Block a user