diff --git a/go/cmd/dolt/commands/stash.go b/go/cmd/dolt/commands/stash.go new file mode 100644 index 00000000000..4293f041b7a --- /dev/null +++ b/go/cmd/dolt/commands/stash.go @@ -0,0 +1,319 @@ +// Copyright 2023 Dolthub, Inc. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package commands + +import ( + "bytes" + "context" + "fmt" + "strconv" + "strings" + + "github.com/dolthub/go-mysql-server/sql" + "github.com/gocraft/dbr/v2" + "github.com/gocraft/dbr/v2/dialect" + + "github.com/dolthub/dolt/go/cmd/dolt/cli" + "github.com/dolthub/dolt/go/cmd/dolt/errhand" + eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" + "github.com/dolthub/dolt/go/libraries/doltcore/env" + "github.com/dolthub/dolt/go/libraries/doltcore/ref" + "github.com/dolthub/dolt/go/libraries/utils/argparser" +) + +const ( + PushCmdRef = "push" + PopCmdRef = "pop" + DropCmdRef = "drop" + ClearCmdRef = "clear" + ListCmdRef = "list" +) + +var stashDocs = cli.CommandDocumentationContent{ + ShortDesc: "Stash the changes in a dirty workspace away.", + LongDesc: `Use dolt stash when you want to record the current state of the workspace and the index, but want to go back to a clean workspace. + +The command saves your local modifications away and reverts the workspace to match the HEAD commit. The stash entries that are saved away can be listed with 'dolt stash list'. +`, + Synopsis: []string{ + "", // this is for `dolt stash` itself. + "list", + "pop {{.LessThan}}stash{{.GreaterThan}}", + "clear", + "drop {{.LessThan}}stash{{.GreaterThan}}", + }, +} + +type StashCmd struct{} + +// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command +func (cmd StashCmd) Name() string { + return "stash" +} + +// Description returns a description of the command +func (cmd StashCmd) Description() string { + return "Stash the changes in a dirty workspace away." +} + +func (cmd StashCmd) Docs() *cli.CommandDocumentation { + ap := cmd.ArgParser() + return cli.NewCommandDocumentation(stashDocs, ap) +} + +func (cmd StashCmd) ArgParser() *argparser.ArgParser { + return cli.CreateStashArgParser() +} + +// EventType returns the type of the event to log +func (cmd StashCmd) EventType() eventsapi.ClientEventType { + return eventsapi.ClientEventType_STASH +} + +// Exec executes the command +func (cmd StashCmd) Exec(ctx context.Context, commandStr string, args []string, _ *env.DoltEnv, cliCtx cli.CliContext) int { + ap := cli.CreateStashArgParser() + + apr, _, terminate, status := ParseArgsOrPrintHelp(ap, commandStr, args, stashDocs) + if terminate { + return status + } + if len(apr.Args) > 2 { + cli.PrintErrln(fmt.Errorf("dolt stash takes 2 arguments, received %d", len(apr.Args))) + return 1 + } + + subcommand := "push" + if len(apr.Args) > 0 { + subcommand = strings.ToLower(apr.Arg(0)) + } + + queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx) + if err != nil { + cli.PrintErrln(errhand.VerboseErrorFromError(err)) + return 1 + } + if closeFunc != nil { + defer closeFunc() + } + + idx := 0 + if apr.NArg() > 1 { + idx, err = parseStashIndex(apr.Arg(1)) + if err != nil { + cli.PrintErrln(errhand.VerboseErrorFromError(err)) + return 1 + } + } + + switch subcommand { + case PushCmdRef: + err = stashPush(queryist, sqlCtx, apr, subcommand) + case PopCmdRef, DropCmdRef: + err = stashRemove(queryist, sqlCtx, cliCtx, apr, subcommand, idx) + case ListCmdRef: + err = stashList(ctx, cliCtx) + case ClearCmdRef: + err = stashClear(queryist, sqlCtx, apr, subcommand) + default: + err = fmt.Errorf("unknown stash subcommand %s", subcommand) + } + + if err != nil { + cli.PrintErrln(errhand.VerboseErrorFromError(err)) + if strings.Contains(err.Error(), "No local changes to save") { + return 0 + } + return 1 + } + return 0 +} + +func stashPush(queryist cli.Queryist, sqlCtx *sql.Context, apr *argparser.ArgParseResults, subcommand string) error { + rowIter, err := stashQuery(queryist, sqlCtx, apr, subcommand) + if err != nil { + return err + } + + stashes, err := getStashesSQL(sqlCtx, queryist, 1) + if err != nil { + return err + } + stash := stashes[0] + cli.Println(fmt.Sprintf("Saved working directory and index state WIP on %s: %s %s", stash.BranchReference, stash.CommitHash, stash.Description)) + _, err = sql.RowIterToRows(sqlCtx, rowIter) + return err +} + +func stashRemove(queryist cli.Queryist, sqlCtx *sql.Context, cliCtx cli.CliContext, apr *argparser.ArgParseResults, subcommand string, idx int) error { + stashes, err := getStashesSQL(sqlCtx, queryist, 0) + if err != nil { + return err + } + if len(stashes) == 0 { + return fmt.Errorf("No stash entries found.") + } + if len(stashes)-1 < idx { + return fmt.Errorf("stash index stash@{%d} does not exist", idx) + } + + interpolatedQuery, err := generateStashSql(apr, subcommand) + if err != nil { + return err + } + _, rowIter, _, err := queryist.Query(sqlCtx, interpolatedQuery) + if err != nil { + return err + } + + if subcommand == PopCmdRef { + ret := StatusCmd{}.Exec(sqlCtx, StatusCmd{}.Name(), []string{}, nil, cliCtx) + if ret != 0 { + cli.Println("The stash entry is kept in case you need it again.") + return err + } + } + + cli.Println(fmt.Sprintf("Dropped refs/stash@{%v} (%s)", idx, stashes[idx].CommitHash)) + _, err = sql.RowIterToRows(sqlCtx, rowIter) + return err +} + +func stashList(ctx context.Context, cliCtx cli.CliContext) error { + queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx) + if err != nil { + return err + } + if closeFunc != nil { + defer closeFunc() + } + + stashes, err := getStashesSQL(sqlCtx, queryist, 0) + if err != nil { + return err + } + for _, stash := range stashes { + cli.Println(fmt.Sprintf("%s: WIP on %s: %s %s", stash.Name, stash.BranchReference, stash.CommitHash, stash.Description)) + } + + return nil +} + +func stashClear(queryist cli.Queryist, sqlCtx *sql.Context, apr *argparser.ArgParseResults, subcommand string) error { + rowIter, err := stashQuery(queryist, sqlCtx, apr, subcommand) + if err != nil { + return err + } + _, err = sql.RowIterToRows(sqlCtx, rowIter) + return err +} + +// getStashesSQL queries the dolt_stashes system table to return the requested number of stashes. A limit of 0 will get all stashes +func getStashesSQL(sqlCtx *sql.Context, queryist cli.Queryist, limit int) ([]*doltdb.Stash, error) { + limitStr := fmt.Sprintf("limit %d", limit) + if limit == 0 { + limitStr = "" + } + + qry := fmt.Sprintf("select stash_id, branch, hash, commit_message from dolt_stashes where name = '%s' order by stash_id ASC %s;", doltdb.DoltCliRef, limitStr) + rows, err := GetRowsForSql(queryist, sqlCtx, qry) + if err != nil { + return nil, err + } + + var stashes []*doltdb.Stash + for _, s := range rows { + id, ok := s[0].(string) + if !ok { + return nil, fmt.Errorf("Invalid stash id") + } + + branch, ok := s[1].(string) + if !ok { + return nil, fmt.Errorf("invalid stash branch") + } + fullBranch := ref.NewBranchRef(branch).String() + + stashHash, ok := s[2].(string) + if !ok { + return nil, fmt.Errorf("invalid stash hash") + } + + msg, ok := s[3].(string) + if !ok { + return nil, fmt.Errorf("invalid stash message") + } + + stashes = append(stashes, &doltdb.Stash{ + Name: id, + BranchReference: fullBranch, + Description: msg, + CommitHash: stashHash, + }) + } + + return stashes, nil +} + +// generateStashSql returns the query that will call the `DOLT_STASH` stored procedure. +func generateStashSql(apr *argparser.ArgParseResults, subcommand string) (string, error) { + var buffer bytes.Buffer + var params []interface{} + buffer.WriteString("CALL DOLT_STASH(?, ?") + params = append(params, subcommand) + params = append(params, doltdb.DoltCliRef) + + if len(apr.Args) == 2 { + params = append(params, apr.Arg(1)) + buffer.WriteString(", ?") + } + + if apr.Contains(cli.AllFlag) { + buffer.WriteString(", '--all'") + } + if apr.Contains(cli.IncludeUntrackedFlag) { + buffer.WriteString(", '--include-untracked'") + } + + buffer.WriteString(")") + interpolatedQuery, err := dbr.InterpolateForDialect(buffer.String(), params, dialect.MySQL) + return interpolatedQuery, err +} + +func stashQuery(queryist cli.Queryist, sqlCtx *sql.Context, apr *argparser.ArgParseResults, subcommand string) (sql.RowIter, error) { + interpolatedQuery, err := generateStashSql(apr, subcommand) + if err != nil { + return nil, err + } + + _, rowIter, _, err := queryist.Query(sqlCtx, interpolatedQuery) + if err != nil { + return nil, err + } + + return rowIter, nil +} + +func parseStashIndex(stashID string) (int, error) { + var err error + stashID = strings.TrimSuffix(strings.TrimPrefix(stashID, "stash@{"), "}") + idx, err := strconv.Atoi(stashID) + if err != nil { + return 0, fmt.Errorf("error: %s is not a valid reference", stashID) + } + + return idx, nil +} diff --git a/go/cmd/dolt/commands/stashcmds/clear.go b/go/cmd/dolt/commands/stashcmds/clear.go deleted file mode 100644 index e11805f4b1e..00000000000 --- a/go/cmd/dolt/commands/stashcmds/clear.go +++ /dev/null @@ -1,87 +0,0 @@ -// Copyright 2023 Dolthub, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stashcmds - -import ( - "context" - - "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" - - "github.com/dolthub/dolt/go/cmd/dolt/cli" - "github.com/dolthub/dolt/go/cmd/dolt/commands" - "github.com/dolthub/dolt/go/cmd/dolt/errhand" - eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/env" - "github.com/dolthub/dolt/go/libraries/utils/argparser" -) - -var stashClearDocs = cli.CommandDocumentationContent{ - ShortDesc: "Remove all the stash entries.", - LongDesc: `Removes all the stash entries from the current stash list. This command cannot be reverted and stash entries may not be recoverable. - -This command does not apply the stash on current working directory, use 'dolt stash pop' to apply a stash on current working directory.`, - Synopsis: []string{ - "", - }, -} - -type StashClearCmd struct{} - -// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command -func (cmd StashClearCmd) Name() string { - return "clear" -} - -// Description returns a description of the command -func (cmd StashClearCmd) Description() string { - return "Remove all the stash entries." -} - -func (cmd StashClearCmd) Docs() *cli.CommandDocumentation { - ap := cmd.ArgParser() - return cli.NewCommandDocumentation(stashClearDocs, ap) -} - -func (cmd StashClearCmd) ArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 0) - return ap -} - -// EventType returns the type of the event to log -func (cmd StashClearCmd) EventType() eventsapi.ClientEventType { - return eventsapi.ClientEventType_STASH_CLEAR -} - -// Exec executes the command -func (cmd StashClearCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - if !dEnv.DoltDB(ctx).Format().UsesFlatbuffers() { - cli.PrintErrln(ErrStashNotSupportedForOldFormat.Error()) - return 1 - } - ap := cmd.ArgParser() - help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashClearDocs, ap)) - apr := cli.ParseArgsOrDie(ap, args, help) - - if apr.NArg() != 0 { - usage() - return 1 - } - - err := dEnv.DoltDB(ctx).RemoveAllStashes(ctx, doltdb.DoltCliRef) - if err != nil { - return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) - } - return 0 -} diff --git a/go/cmd/dolt/commands/stashcmds/drop.go b/go/cmd/dolt/commands/stashcmds/drop.go deleted file mode 100644 index 98f00621225..00000000000 --- a/go/cmd/dolt/commands/stashcmds/drop.go +++ /dev/null @@ -1,112 +0,0 @@ -// Copyright 2023 Dolthub, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stashcmds - -import ( - "context" - "fmt" - "strconv" - "strings" - - "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" - - "github.com/dolthub/dolt/go/cmd/dolt/cli" - "github.com/dolthub/dolt/go/cmd/dolt/commands" - "github.com/dolthub/dolt/go/cmd/dolt/errhand" - eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/env" - "github.com/dolthub/dolt/go/libraries/utils/argparser" -) - -var stashDropDocs = cli.CommandDocumentationContent{ - ShortDesc: "Remove a single stash entry.", - LongDesc: `Removes a single stash entry at given index from the list of stash entries (e.g. 'dolt stash drop stash@{1}' will drop the stash entry at index 1 in the stash list). - -This command does not apply the stash on current working directory, use 'dolt stash pop' to apply a stash on current working directory.`, - Synopsis: []string{ - "{{.LessThan}}stash{{.GreaterThan}}", - }, -} - -type StashDropCmd struct{} - -// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command -func (cmd StashDropCmd) Name() string { - return "drop" -} - -// Description returns a description of the command -func (cmd StashDropCmd) Description() string { - return "Remove a single stash entry." -} - -func (cmd StashDropCmd) Docs() *cli.CommandDocumentation { - ap := cmd.ArgParser() - return cli.NewCommandDocumentation(stashDropDocs, ap) -} - -func (cmd StashDropCmd) ArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 1) - return ap -} - -// EventType returns the type of the event to log -func (cmd StashDropCmd) EventType() eventsapi.ClientEventType { - return eventsapi.ClientEventType_STASH_DROP -} - -// Exec executes the command -func (cmd StashDropCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - if !dEnv.DoltDB(ctx).Format().UsesFlatbuffers() { - cli.PrintErrln(ErrStashNotSupportedForOldFormat.Error()) - return 1 - } - ap := cmd.ArgParser() - help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashDropDocs, ap)) - apr := cli.ParseArgsOrDie(ap, args, help) - - var idx = 0 - var err error - if apr.NArg() == 1 { - stashName := apr.Args[0] - stashName = strings.TrimSuffix(strings.TrimPrefix(stashName, "stash@{"), "}") - idx, err = strconv.Atoi(stashName) - if err != nil { - cli.Printf("error: %s is not a valid reference", stashName) - return 1 - } - } - - err = dropStashAtIdx(ctx, dEnv, idx) - if err != nil { - return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) - } - return 0 -} - -func dropStashAtIdx(ctx context.Context, dEnv *env.DoltEnv, idx int) error { - stashHash, err := dEnv.DoltDB(ctx).GetStashHashAtIdx(ctx, idx, doltdb.DoltCliRef) - if err != nil { - return err - } - - err = dEnv.DoltDB(ctx).RemoveStashAtIdx(ctx, idx, doltdb.DoltCliRef) - if err != nil { - return err - } - - cli.Println(fmt.Sprintf("Dropped refs/stash@{%v} (%s)", idx, stashHash.String())) - return nil -} diff --git a/go/cmd/dolt/commands/stashcmds/list.go b/go/cmd/dolt/commands/stashcmds/list.go deleted file mode 100644 index 97149c12a93..00000000000 --- a/go/cmd/dolt/commands/stashcmds/list.go +++ /dev/null @@ -1,96 +0,0 @@ -// Copyright 2023 Dolthub, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stashcmds - -import ( - "context" - "fmt" - - "github.com/dolthub/dolt/go/cmd/dolt/cli" - "github.com/dolthub/dolt/go/cmd/dolt/commands" - "github.com/dolthub/dolt/go/cmd/dolt/errhand" - eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/env" - "github.com/dolthub/dolt/go/libraries/utils/argparser" -) - -var stashListDocs = cli.CommandDocumentationContent{ - ShortDesc: "List the stash entries that you currently have.", - LongDesc: `Each stash entry is listed with its name (e.g. stash@{0} is the latest entry, stash@{1} is the one before, etc.), the name of the branch that was current when the entry was made, and a short description of the commit the entry was based on. -`, - Synopsis: []string{ - "", - }, -} - -type StashListCmd struct{} - -// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command -func (cmd StashListCmd) Name() string { - return "list" -} - -// Description returns a description of the command -func (cmd StashListCmd) Description() string { - return "List the stash entries that you currently have." -} - -func (cmd StashListCmd) Docs() *cli.CommandDocumentation { - ap := cmd.ArgParser() - return cli.NewCommandDocumentation(stashListDocs, ap) -} - -func (cmd StashListCmd) ArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 0) - return ap -} - -// EventType returns the type of the event to log -func (cmd StashListCmd) EventType() eventsapi.ClientEventType { - return eventsapi.ClientEventType_STASH_LIST -} - -// Exec executes the command -func (cmd StashListCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - if !dEnv.DoltDB(ctx).Format().UsesFlatbuffers() { - cli.PrintErrln(ErrStashNotSupportedForOldFormat.Error()) - return 1 - } - ap := cmd.ArgParser() - help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashListDocs, ap)) - cli.ParseArgsOrDie(ap, args, help) - - err := listStashes(ctx, dEnv) - if err != nil { - return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) - } - return 0 -} - -func listStashes(ctx context.Context, dEnv *env.DoltEnv) error { - stashes, err := dEnv.DoltDB(ctx).GetCommandLineStashes(ctx) - if err != nil { - return err - } - - for _, stash := range stashes { - commitHash, err := stash.HeadCommit.HashOf() - if err != nil { - return err - } - cli.Println(fmt.Sprintf("%s: WIP on %s: %s %s", stash.Name, stash.BranchReference, commitHash.String(), stash.Description)) - } - return nil -} diff --git a/go/cmd/dolt/commands/stashcmds/pop.go b/go/cmd/dolt/commands/stashcmds/pop.go deleted file mode 100644 index 18e2b0caa67..00000000000 --- a/go/cmd/dolt/commands/stashcmds/pop.go +++ /dev/null @@ -1,215 +0,0 @@ -// Copyright 2023 Dolthub, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stashcmds - -import ( - "context" - "strconv" - "strings" - - "github.com/dolthub/go-mysql-server/sql" - - "github.com/dolthub/dolt/go/cmd/dolt/cli" - "github.com/dolthub/dolt/go/cmd/dolt/commands" - "github.com/dolthub/dolt/go/cmd/dolt/errhand" - eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" - "github.com/dolthub/dolt/go/libraries/doltcore/env" - "github.com/dolthub/dolt/go/libraries/doltcore/env/actions" - "github.com/dolthub/dolt/go/libraries/doltcore/merge" - "github.com/dolthub/dolt/go/libraries/doltcore/table/editor" - "github.com/dolthub/dolt/go/libraries/utils/argparser" -) - -var stashPopDocs = cli.CommandDocumentationContent{ - ShortDesc: "Remove a single stash from the stash list and apply it on top of the current working set.", - LongDesc: `Apply a single stash at given index and drop that stash entry from the stash list (e.g. 'dolt stash pop stash@{1}' will apply and drop the stash entry at index 1 in the stash list). - -Applying the stash entry can fail with conflicts; in this case, the stash entry is not removed from the stash list. You need to resolve the conflicts by hand and call dolt stash drop manually afterwards. -`, - Synopsis: []string{ - "{{.LessThan}}stash{{.GreaterThan}}", - }, -} - -type StashPopCmd struct{} - -// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command -func (cmd StashPopCmd) Name() string { - return "pop" -} - -// Description returns a description of the command -func (cmd StashPopCmd) Description() string { - return "Remove a single stash from the stash list and apply it on top of the current working set." -} - -func (cmd StashPopCmd) Docs() *cli.CommandDocumentation { - ap := cmd.ArgParser() - return cli.NewCommandDocumentation(stashPopDocs, ap) -} - -func (cmd StashPopCmd) ArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 1) - return ap -} - -// EventType returns the type of the event to log -func (cmd StashPopCmd) EventType() eventsapi.ClientEventType { - return eventsapi.ClientEventType_STASH_POP -} - -// Exec executes the command -func (cmd StashPopCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - if !dEnv.DoltDB(ctx).Format().UsesFlatbuffers() { - cli.PrintErrln(ErrStashNotSupportedForOldFormat.Error()) - return 1 - } - ap := cmd.ArgParser() - help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashPopDocs, ap)) - apr := cli.ParseArgsOrDie(ap, args, help) - - _, sqlCtx, closer, err := cliCtx.QueryEngine(ctx) - if err != nil { - cli.PrintErrln(err.Error()) - return 1 - } - defer closer() - - var idx = 0 - if apr.NArg() == 1 { - stashName := apr.Args[0] - stashName = strings.TrimSuffix(strings.TrimPrefix(stashName, "stash@{"), "}") - idx, err = strconv.Atoi(stashName) - if err != nil { - cli.Printf("error: %s is not a valid reference", stashName) - return 1 - } - } - - workingRoot, err := dEnv.WorkingRoot(sqlCtx) - if err != nil { - return handleStashPopErr(usage, err) - } - - success, err := applyStashAtIdx(sqlCtx, dEnv, workingRoot, idx) - if err != nil { - return handleStashPopErr(usage, err) - } - - ret := commands.StatusCmd{}.Exec(sqlCtx, "status", []string{}, dEnv, cliCtx) - if ret != 0 || !success { - cli.Println("The stash entry is kept in case you need it again.") - return 1 - } - - cli.Println() - err = dropStashAtIdx(sqlCtx, dEnv, idx) - if err != nil { - return handleStashPopErr(usage, err) - } - - return 0 -} - -func applyStashAtIdx(ctx *sql.Context, dEnv *env.DoltEnv, curWorkingRoot doltdb.RootValue, idx int) (bool, error) { - stashRoot, headCommit, meta, err := dEnv.DoltDB(ctx).GetStashRootAndHeadCommitAtIdx(ctx, idx, doltdb.DoltCliRef) - if err != nil { - return false, err - } - - hch, err := headCommit.HashOf() - if err != nil { - return false, err - } - headCommitSpec, err := doltdb.NewCommitSpec(hch.String()) - if err != nil { - return false, err - } - headRef, err := dEnv.RepoStateReader().CWBHeadRef(ctx) - if err != nil { - return false, err - } - optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, headCommitSpec, headRef) - if err != nil { - return false, err - } - parentCommit, ok := optCmt.ToCommit() - if !ok { - // Should not be possible to get into this situation. The parent of the stashed commit - // Must have been present at the time it was created - return false, doltdb.ErrGhostCommitEncountered - } - - parentRoot, err := parentCommit.GetRootValue(ctx) - if err != nil { - return false, err - } - - tmpDir, err := dEnv.TempTableFilesDir() - if err != nil { - return false, err - } - - opts := editor.Options{Deaf: dEnv.BulkDbEaFactory(ctx), Tempdir: tmpDir} - result, err := merge.MergeRoots(ctx, curWorkingRoot, stashRoot, parentRoot, stashRoot, parentCommit, opts, merge.MergeOpts{IsCherryPick: false}) - if err != nil { - return false, err - } - - var tablesWithConflict []doltdb.TableName - for tbl, stats := range result.Stats { - if stats.HasConflicts() { - tablesWithConflict = append(tablesWithConflict, tbl) - } - } - - if len(tablesWithConflict) > 0 { - tblNames := strings.Join(doltdb.FlattenTableNames(tablesWithConflict), "', '") - cli.Printf("error: Your local changes to the following tables would be overwritten by applying stash %d:\n"+ - "\t{'%s'}\n"+ - "Please commit your changes or stash them before you merge.\nAborting\n", idx, tblNames) - return false, nil - } - - err = dEnv.UpdateWorkingRoot(ctx, result.Root) - if err != nil { - return false, err - } - - roots, err := dEnv.Roots(ctx) - if err != nil { - return false, err - } - - // added tables need to be staged - // since these tables are coming from a stash, don't filter for ignored table names. - roots, err = actions.StageTables(ctx, roots, doltdb.ToTableNames(meta.TablesToStage, doltdb.DefaultSchemaName), false) - if err != nil { - return false, err - } - - err = dEnv.UpdateRoots(ctx, roots) - if err != nil { - return false, err - } - - return true, nil -} - -func handleStashPopErr(usage cli.UsagePrinter, err error) int { - cli.Println("The stash entry is kept in case you need it again.") - return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) -} diff --git a/go/cmd/dolt/commands/stashcmds/stash.go b/go/cmd/dolt/commands/stashcmds/stash.go deleted file mode 100644 index f1f260ccdc4..00000000000 --- a/go/cmd/dolt/commands/stashcmds/stash.go +++ /dev/null @@ -1,306 +0,0 @@ -// Copyright 2023 Dolthub, Inc. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package stashcmds - -import ( - "context" - "errors" - "fmt" - - "github.com/dolthub/dolt/go/cmd/dolt/cli" - "github.com/dolthub/dolt/go/cmd/dolt/commands" - eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" - "github.com/dolthub/dolt/go/libraries/doltcore/diff" - "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" - "github.com/dolthub/dolt/go/libraries/doltcore/env" - "github.com/dolthub/dolt/go/libraries/doltcore/env/actions" - "github.com/dolthub/dolt/go/libraries/utils/argparser" - "github.com/dolthub/dolt/go/store/datas" -) - -var ErrStashNotSupportedForOldFormat = errors.New("stash is not supported for old storage format") - -var StashCommands = cli.NewSubCommandHandlerWithUnspecified("stash", "Stash the changes in a dirty working directory away.", false, StashCmd{}, []cli.Command{ - StashClearCmd{}, - StashDropCmd{}, - StashListCmd{}, - StashPopCmd{}, -}) - -var stashDocs = cli.CommandDocumentationContent{ - ShortDesc: "Stash the changes in a dirty working directory away.", - LongDesc: `Use dolt stash when you want to record the current state of the working directory and the index, but want to go back to a clean working directory. - -The command saves your local modifications away and reverts the working directory to match the HEAD commit. The stash entries that are saved away can be listed with 'dolt stash list'. -`, - Synopsis: []string{ - "", // this is for `dolt stash` itself. - "list", - "pop {{.LessThan}}stash{{.GreaterThan}}", - "clear", - "drop {{.LessThan}}stash{{.GreaterThan}}", - }, -} - -type StashCmd struct{} - -// Name returns the name of the Dolt cli command. This is what is used on the command line to invoke the command -func (cmd StashCmd) Name() string { - return "stash" -} - -// Description returns a description of the command -func (cmd StashCmd) Description() string { - return "Stash the changes in a dirty working directory away." -} - -func (cmd StashCmd) Docs() *cli.CommandDocumentation { - ap := cmd.ArgParser() - return cli.NewCommandDocumentation(stashDocs, ap) -} - -func (cmd StashCmd) ArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs(cmd.Name(), 0) - ap.SupportsFlag(cli.IncludeUntrackedFlag, "u", "Untracked tables are also stashed.") - ap.SupportsFlag(cli.AllFlag, "a", "All tables are stashed, including untracked and ignored tables.") - return ap -} - -// EventType returns the type of the event to log -func (cmd StashCmd) EventType() eventsapi.ClientEventType { - return eventsapi.ClientEventType_STASH -} - -// Exec executes the command -func (cmd StashCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { - ap := cmd.ArgParser() - help, _ := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, stashDocs, ap)) - apr := cli.ParseArgsOrDie(ap, args, help) - - if !dEnv.DoltDB(ctx).Format().UsesFlatbuffers() { - cli.PrintErrln(ErrStashNotSupportedForOldFormat.Error()) - return 1 - } - - err := stashChanges(ctx, dEnv, apr) - if err != nil { - commands.PrintStagingError(err) - return 1 - } - return 0 -} - -func hasLocalChanges(ctx context.Context, dEnv *env.DoltEnv, roots doltdb.Roots, apr *argparser.ArgParseResults) (bool, error) { - headRoot, err := dEnv.HeadRoot(ctx) - if err != nil { - return false, err - } - workingRoot, err := dEnv.WorkingRoot(ctx) - if err != nil { - return false, err - } - stagedRoot, err := dEnv.StagedRoot(ctx) - if err != nil { - return false, err - } - headHash, err := headRoot.HashOf() - if err != nil { - return false, err - } - workingHash, err := workingRoot.HashOf() - if err != nil { - return false, err - } - stagedHash, err := stagedRoot.HashOf() - if err != nil { - return false, err - } - - // Are there staged changes? If so, stash them. - if !headHash.Equal(stagedHash) { - return true, nil - } - - // No staged changes, but are there any unstaged changes? If not, no work is needed. - if headHash.Equal(workingHash) { - return false, nil - } - - // There are unstaged changes, is --all set? If so, nothing else matters. Stash them. - if apr.Contains(cli.AllFlag) { - return true, nil - } - - // --all was not set, so we can ignore tables. Is every table ignored? - allIgnored, err := diff.WorkingSetContainsOnlyIgnoredTables(ctx, roots) - if err != nil { - return false, err - } - - if allIgnored { - return false, nil - } - - // There are unignored, unstaged tables. Is --include-untracked set. If so, nothing else matters. Stash them. - if apr.Contains(cli.IncludeUntrackedFlag) { - return true, nil - } - - // --include-untracked was not set, so we can skip untracked tables. Is every table untracked? - allUntracked, err := workingSetContainsOnlyUntrackedTables(ctx, roots) - if err != nil { - return false, err - } - - if allUntracked { - return false, nil - } - - // There are changes to tracked tables. Stash them. - return true, nil -} - -func stashChanges(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgParseResults) error { - roots, err := dEnv.Roots(ctx) - if err != nil { - return fmt.Errorf("couldn't get working root, cause: %s", err.Error()) - } - - hasChanges, err := hasLocalChanges(ctx, dEnv, roots, apr) - if err != nil { - return err - } - if !hasChanges { - cli.Println("No local changes to save") - return nil - } - - roots, err = actions.StageModifiedAndDeletedTables(ctx, roots) - if err != nil { - return err - } - - // all tables with changes that are going to be stashed are staged at this point - - allTblsToBeStashed, addedTblsToStage, err := stashedTableSets(ctx, roots) - if err != nil { - return err - } - - // stage untracked files to include them in the stash, - // but do not include them in added table set, - // because they should not be staged when popped. - if apr.Contains(cli.IncludeUntrackedFlag) || apr.Contains(cli.AllFlag) { - allTblsToBeStashed, err = doltdb.UnionTableNames(ctx, roots.Staged, roots.Working) - if err != nil { - return err - } - - roots, err = actions.StageTables(ctx, roots, allTblsToBeStashed, !apr.Contains(cli.AllFlag)) - if err != nil { - return err - } - } - - curHeadRef, err := dEnv.RepoStateReader().CWBHeadRef(ctx) - if err != nil { - return err - } - curBranchName := curHeadRef.String() - commitSpec, err := doltdb.NewCommitSpec(curBranchName) - if err != nil { - return err - } - optCmt, err := dEnv.DoltDB(ctx).Resolve(ctx, commitSpec, curHeadRef) - if err != nil { - return err - } - commit, ok := optCmt.ToCommit() - if !ok { - return doltdb.ErrGhostCommitEncountered - } - - commitMeta, err := commit.GetCommitMeta(ctx) - if err != nil { - return err - } - - err = dEnv.DoltDB(ctx).AddStash(ctx, commit, roots.Staged, datas.NewStashMeta(curBranchName, commitMeta.Description, doltdb.FlattenTableNames(addedTblsToStage)), doltdb.DoltCliRef) - if err != nil { - return err - } - - // setting STAGED to current HEAD RootValue resets staged set of changed, so - // these changes are now in working set of changes, which needs to be checked out - roots.Staged = roots.Head - roots, err = actions.MoveTablesFromHeadToWorking(ctx, roots, allTblsToBeStashed) - if err != nil { - return err - } - - err = dEnv.UpdateRoots(ctx, roots) - if err != nil { - return err - } - - commitHash, err := commit.HashOf() - if err != nil { - return err - } - cli.Println(fmt.Sprintf("Saved working directory and index state WIP on %s: %s %s", curBranchName, commitHash.String(), commitMeta.Description)) - return nil -} - -// workingSetContainsOnlyUntrackedTables returns true if all changes in working set are untracked files/added tables. -// Untracked files are part of working set changes, but should not be stashed unless staged or --include-untracked flag is used. -func workingSetContainsOnlyUntrackedTables(ctx context.Context, roots doltdb.Roots) (bool, error) { - _, unstaged, err := diff.GetStagedUnstagedTableDeltas(ctx, roots) - if err != nil { - return false, err - } - - // All ignored files are also untracked files - for _, tableDelta := range unstaged { - if !tableDelta.IsAdd() { - return false, nil - } - } - - return true, nil -} - -// stashedTableSets returns array of table names for all tables that are being stashed and added tables in staged. -// These table names are determined from all tables in the staged set of changes as they are being stashed only. -func stashedTableSets(ctx context.Context, roots doltdb.Roots) ([]doltdb.TableName, []doltdb.TableName, error) { - var addedTblsInStaged []doltdb.TableName - var allTbls []doltdb.TableName - staged, _, err := diff.GetStagedUnstagedTableDeltas(ctx, roots) - if err != nil { - return nil, nil, err - } - - for _, tableDelta := range staged { - tblName := tableDelta.ToName - if tableDelta.IsAdd() { - addedTblsInStaged = append(addedTblsInStaged, tableDelta.ToName) - } - if tableDelta.IsDrop() { - tblName = tableDelta.FromName - } - allTbls = append(allTbls, tblName) - } - - return allTbls, addedTblsInStaged, nil -} diff --git a/go/cmd/dolt/doltcmd/doltcmd.go b/go/cmd/dolt/doltcmd/doltcmd.go index 22814d21903..1bf9808df6c 100644 --- a/go/cmd/dolt/doltcmd/doltcmd.go +++ b/go/cmd/dolt/doltcmd/doltcmd.go @@ -26,7 +26,6 @@ import ( "github.com/dolthub/dolt/go/cmd/dolt/commands/indexcmds" "github.com/dolthub/dolt/go/cmd/dolt/commands/schcmds" "github.com/dolthub/dolt/go/cmd/dolt/commands/sqlserver" - "github.com/dolthub/dolt/go/cmd/dolt/commands/stashcmds" "github.com/dolthub/dolt/go/cmd/dolt/commands/tblcmds" "github.com/dolthub/dolt/go/cmd/dolt/doltversion" ) @@ -83,7 +82,7 @@ var doltSubCommands = []cli.Command{ dumpDocsCommand, dumpZshCommand, docscmds.Commands, - stashcmds.StashCommands, + commands.StashCmd{}, &commands.Assist{}, commands.ProfileCmd{}, commands.QueryDiff{}, diff --git a/go/libraries/doltcore/doltdb/stash.go b/go/libraries/doltcore/doltdb/stash.go index a5be6c3b0d7..2c4a58466c0 100644 --- a/go/libraries/doltcore/doltdb/stash.go +++ b/go/libraries/doltcore/doltdb/stash.go @@ -29,7 +29,7 @@ type Stash struct { Name string BranchReference string Description string - HeadCommit *Commit + CommitHash string StashReference string } @@ -76,8 +76,12 @@ func getStashList(ctx context.Context, ds datas.Dataset, vrw types.ValueReadWrit if err != nil { return nil, err } + headCommitHash, err := headCommit.HashOf() + if err != nil { + return nil, err + } - s.HeadCommit = headCommit + s.CommitHash = headCommitHash.String() s.BranchReference = meta.BranchName s.Description = meta.Description s.StashReference = reference diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_stash.go b/go/libraries/doltcore/sqle/dprocedures/dolt_stash.go index eb212f6c49d..a72ff6310c9 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_stash.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_stash.go @@ -113,7 +113,7 @@ func doStashPush(ctx *sql.Context, dSess *dsess.DoltSession, dbData env.DbData[* return err } if !hasChanges { - return fmt.Errorf("no local changes to save") + return fmt.Errorf("No local changes to save") } roots, err = actions.StageModifiedAndDeletedTables(ctx, roots) @@ -197,6 +197,8 @@ func doStashClear(ctx *sql.Context, dbData env.DbData[*sql.Context], stashName s return dbData.Ddb.RemoveAllStashes(ctx, stashName) } +// stashedTableSets returns array of table names for all tables that are being stashed and added tables in staged. +// These table names are determined from all tables in the staged set of changes as they are being stashed only. func stashedTableSets(ctx context.Context, roots doltdb.Roots) ([]doltdb.TableName, []doltdb.TableName, error) { var addedTblsInStaged []doltdb.TableName var allTbls []doltdb.TableName @@ -288,6 +290,8 @@ func hasLocalChanges(ctx *sql.Context, dSess *dsess.DoltSession, roots doltdb.Ro return true, nil } +// workingSetContainsOnlyUntrackedTables returns true if all changes in working set are untracked files/added tables. +// Untracked files are part of working set changes, but should not be stashed unless staged or --include-untracked flag is used. func workingSetContainsOnlyUntrackedTables(ctx context.Context, roots doltdb.Roots) (bool, error) { _, unstaged, err := diff.GetStagedUnstagedTableDeltas(ctx, roots) if err != nil { @@ -500,7 +504,7 @@ func handleMerge(ctx *sql.Context, dbData env.DbData[*sql.Context], stashName st tblNames := strings.Join(doltdb.FlattenTableNames(tablesWithConflict), "', '") status := fmt.Errorf("error: Your local changes to the following tables would be overwritten by applying stash %d:\n"+ "\t{'%s'}\n"+ - "Please commit your changes or stash them before you merge.\nAborting\n", idx, tblNames) + "Please commit your changes or stash them before you merge.\nAborting\nThe stash entry is kept in case you need it again.\n", idx, tblNames) return nil, nil, nil, status } diff --git a/go/libraries/doltcore/sqle/dtables/stashes_table.go b/go/libraries/doltcore/sqle/dtables/stashes_table.go index 59fcd01fdd0..c7175854c10 100644 --- a/go/libraries/doltcore/sqle/dtables/stashes_table.go +++ b/go/libraries/doltcore/sqle/dtables/stashes_table.go @@ -144,17 +144,13 @@ func (itr *StashItr) Next(*sql.Context) (sql.Row, error) { }() stash := itr.stashes[itr.idx] - commitHash, err := stash.HeadCommit.HashOf() - if err != nil { - return nil, err - } // BranchName and StashReference are of the form refs/heads/name // or refs/stashes/name, so we need to parse them to get names branch := ref.NewBranchRef(stash.BranchReference).GetPath() stashRef := ref.NewStashRef(stash.StashReference).GetPath() - return sql.NewRow(stashRef, stash.Name, branch, commitHash.String(), stash.Description), nil + return sql.NewRow(stashRef, stash.Name, branch, stash.CommitHash, stash.Description), nil } // Close closes the iterator. diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries_help.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries_help.go index 6862ec1a5dc..1030b7a68dc 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries_help.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries_help.go @@ -57,6 +57,7 @@ var DoltHelpScripts = []queries.ScriptTest{ {"dolt_backup"}, {"dolt_tag"}, {"dolt_gc"}, + {"dolt_stash"}, {"dolt_rebase"}, }, }, diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries_stash.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries_stash.go index cb984efdc32..b0e748a4031 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries_stash.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries_stash.go @@ -26,7 +26,7 @@ var DoltStashTests = []queries.ScriptTest{ Assertions: []queries.ScriptTestAssertion{ { Query: "CALL DOLT_STASH('push', 'myStash');", - ExpectedErrStr: "no local changes to save", + ExpectedErrStr: "No local changes to save", }, { Query: "CREATE TABLE test (i int)", @@ -34,7 +34,7 @@ var DoltStashTests = []queries.ScriptTest{ }, { Query: "CALL DOLT_STASH('push', 'myStash');", - ExpectedErrStr: "no local changes to save", + ExpectedErrStr: "No local changes to save", }, { Query: "CALL DOLT_STASH('pop', 'myStash');", @@ -292,7 +292,7 @@ var DoltStashTests = []queries.ScriptTest{ { Query: "CALL DOLT_STASH('pop', 'myStash');", ExpectedErrStr: "error: Your local changes to the following tables would be overwritten by applying stash 0:\n\t{'test'}\n" + - "Please commit your changes or stash them before you merge.\nAborting\n", + "Please commit your changes or stash them before you merge.\nAborting\nThe stash entry is kept in case you need it again.\n", }, { Query: "SELECT * FROM DOLT_STASHES;", diff --git a/integration-tests/bats/helper/local-remote.bash b/integration-tests/bats/helper/local-remote.bash index 268b80915a3..0a27df843c9 100644 --- a/integration-tests/bats/helper/local-remote.bash +++ b/integration-tests/bats/helper/local-remote.bash @@ -128,7 +128,6 @@ SKIP_SERVER_TESTS=$(cat <<-EOM ~doltpy.bats~ ~sql-batch.bats~ ~send-metrics.bats~ -~stash.bats~ ~commit.bats~ ~sql-commit.bats~ ~reset.bats~ diff --git a/integration-tests/bats/stash.bats b/integration-tests/bats/stash.bats index b87cd788394..eaa016eb6ab 100644 --- a/integration-tests/bats/stash.bats +++ b/integration-tests/bats/stash.bats @@ -196,16 +196,16 @@ teardown() { [ "$status" -eq 0 ] [[ "$output" =~ "3,c" ]] || false - dolt checkout test - run dolt stash list + dolt branch test + run dolt --branch test stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] - run dolt stash pop stash@{1} + run dolt --branch test stash pop stash@{1} [ "$status" -eq 0 ] [[ "$output" =~ "Dropped refs/stash@{1}" ]] || false - run dolt sql -q "SELECT * FROM test" -r csv + run dolt --branch test sql -q "SELECT * FROM test" -r csv [ "$status" -eq 0 ] [[ "$output" =~ "1,a" ]] || false } @@ -215,9 +215,9 @@ teardown() { run dolt stash [ "$status" -eq 0 ] - dolt checkout -b newbranch - dolt sql -q "INSERT INTO test VALUES (1, 'b')" - run dolt stash + dolt branch newbranch + dolt --branch newbranch sql -q "INSERT INTO test VALUES (1, 'b')" + run dolt --branch newbranch stash [ "$status" -eq 0 ] run dolt stash list @@ -235,11 +235,11 @@ teardown() { [ "$status" -eq 0 ] [[ "$output" =~ "stash@{0}: WIP on refs/heads/main:" ]] || false - dolt checkout -b newbranch - run dolt stash pop + dolt branch newbranch + run dolt --branch newbranch stash pop [ "$status" -eq 0 ] - run dolt sql -q "SELECT * FROM test" -r csv + run dolt --branch newbranch sql -q "SELECT * FROM test" -r csv [[ "$output" =~ "1,a" ]] || false } @@ -538,59 +538,55 @@ teardown() { } @test "stash: popping stash with deleted table that is deleted already on current head" { - dolt branch branch1 - dolt checkout -b branch2 - dolt sql -q "DROP TABLE test;" - dolt commit -am "table 'test' is dropped" + dolt branch br1 + dolt branch br2 + dolt --branch br2 sql -q "DROP TABLE test;" + dolt --branch br2 commit -am "table 'test' is dropped" - dolt checkout branch1 - run dolt ls + run dolt --branch br1 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - dolt sql -q "DROP TABLE test;" - run dolt stash + dolt --branch br1 sql -q "DROP TABLE test;" + run dolt --branch br1 stash [ "$status" -eq 0 ] [[ "$output" =~ "Saved working directory and index state" ]] || false - dolt checkout branch2 - run dolt stash list + run dolt --branch br2 stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] [[ "$output" =~ "stash@{0}" ]] || false - run dolt stash pop + run dolt --branch br2 stash pop [ "$status" -eq 0 ] [[ "$output" =~ "nothing to commit, working tree clean" ]] || false [[ "$output" =~ "Dropped refs/stash@{0}" ]] || false } @test "stash: popping stash with deleted table that the same table exists on current head" { - dolt branch branch1 - dolt branch branch2 + dolt branch br1 + dolt branch br2 - dolt checkout branch1 - run dolt ls + run dolt --branch br1 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - dolt sql -q "DROP TABLE test;" - run dolt stash + dolt --branch br1 sql -q "DROP TABLE test;" + run dolt --branch br1 stash [ "$status" -eq 0 ] [[ "$output" =~ "Saved working directory and index state" ]] || false - dolt checkout branch2 - run dolt ls + run dolt --branch br2 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - run dolt stash list + run dolt --branch br2 stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] [[ "$output" =~ "stash@{0}" ]] || false # if the table is the same, it's dropped - run dolt stash pop + run dolt --branch br2 stash pop [ "$status" -eq 0 ] [[ "$output" =~ "Changes not staged for commit:" ]] || false [[ "$output" =~ "deleted: test" ]] || false @@ -598,88 +594,83 @@ teardown() { } @test "stash: popping stash with deleted table that different table with same name on current head gives conflict" { - dolt branch branch1 - dolt branch branch2 + dolt branch br1 + dolt branch br2 - dolt checkout branch1 - run dolt ls + run dolt --branch br1 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - dolt sql -q "DROP TABLE test;" - run dolt stash + dolt --branch br1 sql -q "DROP TABLE test;" + run dolt --branch br1 stash [ "$status" -eq 0 ] [[ "$output" =~ "Saved working directory and index state" ]] || false - dolt checkout branch2 - dolt sql -q "DROP TABLE test;" - dolt sql -q "CREATE TABLE test (id BIGINT PRIMARY KEY);" + dolt --branch br2 sql -q "DROP TABLE test;" + dolt --branch br2 sql -q "CREATE TABLE test (id BIGINT PRIMARY KEY);" - run dolt stash list + run dolt --branch br2 stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] [[ "$output" =~ "stash@{0}" ]] || false # if the table is different with the same name, it gives conflict - run dolt stash pop + run dolt --branch br2 stash pop [ "$status" -eq 1 ] [[ "$output" =~ "table was modified in one branch and deleted in the other" ]] || false } @test "stash: popping stash with added table with PK on current head with the exact same table is added already" { - dolt branch branch1 - dolt checkout -b branch2 - dolt sql -q "CREATE TABLE new_test(id INT PRIMARY KEY); INSERT INTO new_test VALUES (1);" - dolt commit -Am "new table 'new_test' is created" + dolt branch br1 + dolt branch br2 - dolt checkout branch1 - run dolt ls + dolt --branch br2 sql -q "CREATE TABLE new_test(id INT PRIMARY KEY); INSERT INTO new_test VALUES (1);" + dolt --branch br2 commit -Am "new table 'new_test' is created" + + run dolt --branch br1 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - dolt sql -q "CREATE TABLE new_test(id INT PRIMARY KEY); INSERT INTO new_test VALUES (1);" - dolt add . - run dolt stash + dolt --branch br1 sql -q "CREATE TABLE new_test(id INT PRIMARY KEY); INSERT INTO new_test VALUES (1);" + dolt --branch br1 add . + run dolt --branch br1 stash [ "$status" -eq 0 ] [[ "$output" =~ "Saved working directory and index state" ]] || false - dolt checkout branch2 - run dolt stash list + run dolt --branch br2 stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] [[ "$output" =~ "stash@{0}" ]] || false - run dolt stash pop + run dolt --branch br2 stash pop [ "$status" -eq 0 ] [[ "$output" =~ "nothing to commit, working tree clean" ]] || false [[ "$output" =~ "Dropped refs/stash@{0}" ]] || false } @test "stash: popping stash with added keyless table on current head with the exact same table is added already" { - dolt branch branch1 - dolt checkout -b branch2 - dolt sql -q "CREATE TABLE new_test(id INT); INSERT INTO new_test VALUES (1);" - dolt commit -Am "new table 'new_test' is created" + dolt branch br1 + dolt branch br2 + dolt --branch br2 sql -q "CREATE TABLE new_test(id INT); INSERT INTO new_test VALUES (1);" + dolt --branch br2 commit -Am "new table 'new_test' is created" - dolt checkout branch1 - run dolt ls + run dolt --branch br1 ls [ "$status" -eq 0 ] [[ "$output" =~ "test" ]] || false - dolt sql -q "CREATE TABLE new_test(id INT); INSERT INTO new_test VALUES (1);" - dolt add . - run dolt stash + dolt --branch br1 sql -q "CREATE TABLE new_test(id INT); INSERT INTO new_test VALUES (1);" + dolt --branch br1 add . + run dolt --branch br1 stash [ "$status" -eq 0 ] [[ "$output" =~ "Saved working directory and index state" ]] || false - dolt checkout branch2 - run dolt stash list + run dolt --branch br2 stash list [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] [[ "$output" =~ "stash@{0}" ]] || false skip # stash of the exact copy of keyless table causes merge conflict, where it should not - run dolt stash pop + run dolt --branch br2 stash pop [ "$status" -eq 0 ] [[ "$output" =~ "nothing to commit, working tree clean" ]] || false [[ "$output" =~ "Dropped refs/stash@{0}" ]] || false