mirror of
https://github.com/dolthub/dolt.git
synced 2026-02-09 10:38:10 -06:00
Added dolt revert
This commit is contained in:
committed by
Daylon Wilkins
parent
c4a0f9acac
commit
a6716c1490
@@ -100,17 +100,27 @@ type HiddenCommand interface {
|
||||
type SubCommandHandler struct {
|
||||
name string
|
||||
description string
|
||||
// Unspecified ONLY applies when no other command has been given. This is different from how a default command would
|
||||
// function, as a command that doesn't exist for this sub handler will result in an error.
|
||||
Unspecified Command
|
||||
Subcommands []Command
|
||||
hidden bool
|
||||
}
|
||||
|
||||
// NewSubCommandHandler returns a new SubCommandHandler instance
|
||||
func NewSubCommandHandler(name, description string, subcommands []Command) SubCommandHandler {
|
||||
return SubCommandHandler{name, description, subcommands, false}
|
||||
return SubCommandHandler{name, description, nil, subcommands, false}
|
||||
}
|
||||
|
||||
// NewHiddenSubCommandHandler returns a new SubCommandHandler instance that is hidden from display
|
||||
func NewHiddenSubCommandHandler(name, description string, subcommands []Command) SubCommandHandler {
|
||||
return SubCommandHandler{name, description, subcommands, true}
|
||||
return SubCommandHandler{name, description, nil, subcommands, true}
|
||||
}
|
||||
|
||||
// NewSubCommandHandlerWithUnspecified returns a new SubCommandHandler that will invoke the unspecified command ONLY if
|
||||
// no direct command is given.
|
||||
func NewSubCommandHandlerWithUnspecified(name, description string, hidden bool, unspecified Command, subcommands []Command) SubCommandHandler {
|
||||
return SubCommandHandler{name, description, unspecified, subcommands, hidden}
|
||||
}
|
||||
|
||||
func (hc SubCommandHandler) Name() string {
|
||||
@@ -134,43 +144,24 @@ func (hc SubCommandHandler) Hidden() bool {
|
||||
}
|
||||
|
||||
func (hc SubCommandHandler) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv) int {
|
||||
if len(args) < 1 {
|
||||
if len(args) < 1 && hc.Unspecified == nil {
|
||||
hc.printUsage(commandStr)
|
||||
return 1
|
||||
}
|
||||
|
||||
subCommandStr := strings.ToLower(strings.TrimSpace(args[0]))
|
||||
var subCommandStr string
|
||||
if len(args) > 0 {
|
||||
subCommandStr = strings.ToLower(strings.TrimSpace(args[0]))
|
||||
}
|
||||
for _, cmd := range hc.Subcommands {
|
||||
lwrName := strings.ToLower(cmd.Name())
|
||||
|
||||
if lwrName == subCommandStr {
|
||||
cmdRequiresRepo := true
|
||||
if rnrCmd, ok := cmd.(RepoNotRequiredCommand); ok {
|
||||
cmdRequiresRepo = rnrCmd.RequiresRepo()
|
||||
}
|
||||
|
||||
if cmdRequiresRepo && !hasHelpFlag(args) {
|
||||
isValid := CheckEnvIsValid(dEnv)
|
||||
if !isValid {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
var evt *events.Event
|
||||
if evtCmd, ok := cmd.(EventMonitoredCommand); ok {
|
||||
evt = events.NewEvent(evtCmd.EventType())
|
||||
ctx = events.NewContextForEvent(ctx, evt)
|
||||
}
|
||||
|
||||
ret := cmd.Exec(ctx, commandStr+" "+subCommandStr, args[1:], dEnv)
|
||||
|
||||
if evt != nil {
|
||||
events.GlobalCollector.CloseEventAndAdd(evt)
|
||||
}
|
||||
|
||||
return ret
|
||||
return hc.handleCommand(ctx, commandStr+" "+subCommandStr, cmd, args[1:], dEnv)
|
||||
}
|
||||
}
|
||||
if hc.Unspecified != nil {
|
||||
return hc.handleCommand(ctx, commandStr, hc.Unspecified, args, dEnv)
|
||||
}
|
||||
|
||||
if !isHelp(subCommandStr) {
|
||||
PrintErrln(color.RedString("Unknown Command " + subCommandStr))
|
||||
@@ -180,6 +171,34 @@ func (hc SubCommandHandler) Exec(ctx context.Context, commandStr string, args []
|
||||
return 1
|
||||
}
|
||||
|
||||
func (hc SubCommandHandler) handleCommand(ctx context.Context, commandStr string, cmd Command, args []string, dEnv *env.DoltEnv) int {
|
||||
cmdRequiresRepo := true
|
||||
if rnrCmd, ok := cmd.(RepoNotRequiredCommand); ok {
|
||||
cmdRequiresRepo = rnrCmd.RequiresRepo()
|
||||
}
|
||||
|
||||
if cmdRequiresRepo && !hasHelpFlag(args) {
|
||||
isValid := CheckEnvIsValid(dEnv)
|
||||
if !isValid {
|
||||
return 2
|
||||
}
|
||||
}
|
||||
|
||||
var evt *events.Event
|
||||
if evtCmd, ok := cmd.(EventMonitoredCommand); ok {
|
||||
evt = events.NewEvent(evtCmd.EventType())
|
||||
ctx = events.NewContextForEvent(ctx, evt)
|
||||
}
|
||||
|
||||
ret := cmd.Exec(ctx, commandStr, args, dEnv)
|
||||
|
||||
if evt != nil {
|
||||
events.GlobalCollector.CloseEventAndAdd(evt)
|
||||
}
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
// CheckEnvIsValid validates that a DoltEnv has been initialized properly and no errors occur during load, and prints
|
||||
// error messages to the user if there are issues with the environment or if errors were encountered while loading it.
|
||||
func CheckEnvIsValid(dEnv *env.DoltEnv) bool {
|
||||
|
||||
139
go/cmd/dolt/commands/revert.go
Normal file
139
go/cmd/dolt/commands/revert.go
Normal file
@@ -0,0 +1,139 @@
|
||||
// Copyright 2021 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 (
|
||||
"context"
|
||||
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
||||
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/env"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
||||
"github.com/dolthub/dolt/go/libraries/utils/filesys"
|
||||
)
|
||||
|
||||
var revertDocs = cli.CommandDocumentationContent{
|
||||
ShortDesc: "Undo the changes introduced in a commit",
|
||||
LongDesc: `Removes the changes made in a commit (or series of commits) from the working set, and then automatically commits the
|
||||
result. This is done by way of a three-way merge. Given a specific commit (e.g. HEAD~1), this is similar to applying the
|
||||
patch from HEAD~1..HEAD~2, giving us a patch of what to remove to effectively remove the influence of the specified
|
||||
commit. If multiple commits are specified, then this process is repeated for each commit in the order specified. This
|
||||
requires a clean working set.
|
||||
|
||||
For now, any conflicts or constraint violations that are brought by the merge cause the command to fail.`,
|
||||
Synopsis: []string{
|
||||
"<revision>...",
|
||||
},
|
||||
}
|
||||
|
||||
type RevertCmd struct{}
|
||||
|
||||
var _ cli.Command = RevertCmd{}
|
||||
|
||||
// Name implements the interface cli.Command.
|
||||
func (cmd RevertCmd) Name() string {
|
||||
return "revert"
|
||||
}
|
||||
|
||||
// Description implements the interface cli.Command.
|
||||
func (cmd RevertCmd) Description() string {
|
||||
return "Undo the changes introduced in a commit."
|
||||
}
|
||||
|
||||
// CreateMarkdown implements the interface cli.Command.
|
||||
func (cmd RevertCmd) CreateMarkdown(fs filesys.Filesys, path, commandStr string) error {
|
||||
ap := argparser.NewArgParser()
|
||||
return CreateMarkdown(fs, path, cli.GetCommandDocumentation(commandStr, revertDocs, ap))
|
||||
}
|
||||
|
||||
// createArgParser creates the argument parser for this command.
|
||||
func (cmd RevertCmd) createArgParser() *argparser.ArgParser {
|
||||
ap := argparser.NewArgParser()
|
||||
ap.ArgListHelp = append(ap.ArgListHelp, [2]string{"revision",
|
||||
"The commit revisions. If multiple revisions are given, they're applied in the order given."})
|
||||
return ap
|
||||
}
|
||||
|
||||
// Exec implements the interface cli.Command.
|
||||
func (cmd RevertCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv) int {
|
||||
ap := cmd.createArgParser()
|
||||
help, usage := cli.HelpAndUsagePrinters(cli.GetCommandDocumentation(commandStr, commitDocs, ap))
|
||||
apr := cli.ParseArgsOrDie(ap, args, help)
|
||||
|
||||
if apr.NArg() < 1 {
|
||||
usage()
|
||||
return 1
|
||||
}
|
||||
headRoot, err := dEnv.HeadRoot(ctx)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
workingRoot, err := dEnv.WorkingRoot(ctx)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
headHash, err := headRoot.HashOf()
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
workingHash, err := workingRoot.HashOf()
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
if !headHash.Equal(workingHash) {
|
||||
cli.PrintErrln("You must commit any changes before using revert.")
|
||||
return 1
|
||||
}
|
||||
|
||||
headRef := dEnv.RepoState.CWBHeadRef()
|
||||
commits := make([]*doltdb.Commit, apr.NArg())
|
||||
for i, arg := range apr.Args() {
|
||||
commitSpec, err := doltdb.NewCommitSpec(arg)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, headRef)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
commits[i] = commit
|
||||
}
|
||||
|
||||
workingRoot, revertMessage, err := merge.Revert(ctx, dEnv.DoltDB, workingRoot, commits)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
|
||||
workingHash, err = workingRoot.HashOf()
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
if headHash.Equal(workingHash) {
|
||||
cli.Println("No changes were made.")
|
||||
return 0
|
||||
}
|
||||
|
||||
err = dEnv.UpdateWorkingRoot(ctx, workingRoot)
|
||||
if err != nil {
|
||||
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
|
||||
}
|
||||
res := AddCmd{}.Exec(ctx, "add", []string{"-A"}, dEnv)
|
||||
if res != 0 {
|
||||
return res
|
||||
}
|
||||
return CommitCmd{}.Exec(ctx, "commit", []string{"-m", revertMessage}, dEnv)
|
||||
}
|
||||
@@ -76,6 +76,7 @@ var doltCommand = cli.NewSubCommandHandler("dolt", "it's git for data", []cli.Co
|
||||
commands.PullCmd{},
|
||||
commands.FetchCmd{},
|
||||
commands.CloneCmd{},
|
||||
commands.RevertCmd{},
|
||||
credcmds.Commands,
|
||||
commands.LoginCmd{},
|
||||
commands.VersionCmd{VersionStr: Version},
|
||||
@@ -320,6 +321,7 @@ func commandNeedsMigrationCheck(args []string) bool {
|
||||
for _, cmd := range []cli.Command{
|
||||
commands.ResetCmd{},
|
||||
commands.CommitCmd{},
|
||||
commands.RevertCmd{},
|
||||
commands.SqlCmd{},
|
||||
sqlserver.SqlServerCmd{},
|
||||
sqlserver.SqlClientCmd{},
|
||||
|
||||
85
go/libraries/doltcore/merge/revert.go
Normal file
85
go/libraries/doltcore/merge/revert.go
Normal file
@@ -0,0 +1,85 @@
|
||||
// Copyright 2021 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 merge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
)
|
||||
|
||||
// Revert is a convenience function for a three-way merge. In particular, given some root and a collection of commits
|
||||
// that are all parents of the root value, this applies a three-way merge with the following characteristics (assuming
|
||||
// a commit is HEAD~1):
|
||||
//
|
||||
// Base: HEAD~1
|
||||
// Ours: root
|
||||
// Theirs: HEAD~2
|
||||
//
|
||||
// The root is updated with the merged result, and this process is repeated for each commit given, in the order given.
|
||||
// Currently, we error on conflicts or constraint violations generated by the merge.
|
||||
func Revert(ctx context.Context, ddb *doltdb.DoltDB, root *doltdb.RootValue, commits []*doltdb.Commit) (*doltdb.RootValue, string, error) {
|
||||
revertMessage := "Revert"
|
||||
|
||||
for i, baseCommit := range commits {
|
||||
if i > 0 {
|
||||
revertMessage += " and"
|
||||
}
|
||||
baseRoot, err := baseCommit.GetRootValue()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
baseMeta, err := baseCommit.GetCommitMeta()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
revertMessage = fmt.Sprintf(`%s "%s"`, revertMessage, baseMeta.Description)
|
||||
|
||||
var theirRoot *doltdb.RootValue
|
||||
if len(baseCommit.ParentRefs()) > 0 {
|
||||
parentCM, err := ddb.ResolveParent(ctx, baseCommit, 0)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
theirRoot, err = parentCM.GetRootValue()
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
} else {
|
||||
theirRoot, err = doltdb.EmptyRootValue(ctx, ddb.ValueReadWriter())
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
}
|
||||
|
||||
root, _, err = MergeRoots(ctx, root, theirRoot, baseRoot)
|
||||
if err != nil {
|
||||
return nil, "", err
|
||||
}
|
||||
if ok, err := root.HasConflicts(ctx); err != nil {
|
||||
return nil, "", err
|
||||
} else if ok {
|
||||
return nil, "", fmt.Errorf("revert currently does not handle conflicts")
|
||||
}
|
||||
if ok, err := root.HasConstraintViolations(ctx); err != nil {
|
||||
return nil, "", err
|
||||
} else if ok {
|
||||
return nil, "", fmt.Errorf("revert currently does not handle constraint violations")
|
||||
}
|
||||
}
|
||||
|
||||
return root, revertMessage, nil
|
||||
}
|
||||
@@ -32,6 +32,7 @@ var DoltFunctions = []sql.Function{
|
||||
sql.Function2{Name: DoltMergeBaseFuncName, Fn: NewMergeBase},
|
||||
sql.FunctionN{Name: ConstraintsVerifyFuncName, Fn: NewConstraintsVerifyFunc},
|
||||
sql.FunctionN{Name: ConstraintsVerifyAllFuncName, Fn: NewConstraintsVerifyAllFunc},
|
||||
sql.FunctionN{Name: RevertFuncName, Fn: NewRevertFunc},
|
||||
}
|
||||
|
||||
// These are the DoltFunctions that get exposed to Dolthub Api.
|
||||
|
||||
166
go/libraries/doltcore/sqle/dfunctions/revert.go
Normal file
166
go/libraries/doltcore/sqle/dfunctions/revert.go
Normal file
@@ -0,0 +1,166 @@
|
||||
// Copyright 2021 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 dfunctions
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/dolthub/go-mysql-server/sql"
|
||||
"github.com/dolthub/go-mysql-server/sql/expression"
|
||||
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/doltdb"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/merge"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/schema/typeinfo"
|
||||
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
|
||||
)
|
||||
|
||||
const (
|
||||
RevertFuncName = "dolt_revert"
|
||||
)
|
||||
|
||||
// RevertFunc represents the dolt function "dolt revert".
|
||||
type RevertFunc struct {
|
||||
expression.NaryExpression
|
||||
}
|
||||
|
||||
var _ sql.Expression = (*RevertFunc)(nil)
|
||||
|
||||
// NewRevertFunc creates a new RevertFunc expression that reverts commits.
|
||||
func NewRevertFunc(ctx *sql.Context, args ...sql.Expression) (sql.Expression, error) {
|
||||
return &RevertFunc{expression.NaryExpression{ChildExpressions: args}}, nil
|
||||
}
|
||||
|
||||
// Eval implements the Expression interface.
|
||||
func (r *RevertFunc) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) {
|
||||
dbName := ctx.GetCurrentDatabase()
|
||||
dSess := dsess.DSessFromSess(ctx.Session)
|
||||
ddb, ok := dSess.GetDoltDB(ctx, dbName)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("dolt database could not be found")
|
||||
}
|
||||
workingSet, err := dSess.WorkingSet(ctx, dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workingRoot := workingSet.WorkingRoot()
|
||||
headCommit, err := dSess.GetHeadCommit(ctx, dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headRoot, err := headCommit.GetRootValue()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
headHash, err := headRoot.HashOf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workingHash, err := workingRoot.HashOf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !headHash.Equal(workingHash) {
|
||||
return nil, fmt.Errorf("you must commit any changes before using revert")
|
||||
}
|
||||
|
||||
headRef, err := dSess.CWBHeadRef(ctx, dbName)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits := make([]*doltdb.Commit, len(r.ChildExpressions))
|
||||
for i, expr := range r.ChildExpressions {
|
||||
res, err := expr.Eval(ctx, row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
revisionStr, ok := res.(string)
|
||||
if !ok {
|
||||
return nil, sql.ErrUnexpectedType.New(i, fmt.Sprintf("%T", res))
|
||||
}
|
||||
commitSpec, err := doltdb.NewCommitSpec(revisionStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commit, err := ddb.Resolve(ctx, commitSpec, headRef)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
commits[i] = commit
|
||||
}
|
||||
|
||||
workingRoot, revertMessage, err := merge.Revert(ctx, ddb, workingRoot, commits)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
workingHash, err = workingRoot.HashOf()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if !headHash.Equal(workingHash) {
|
||||
err = dSess.SetRoot(ctx, dbName, workingRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
stringType := typeinfo.StringDefaultType.ToSqlType()
|
||||
commitFunc, err := NewDoltCommitFunc(ctx,
|
||||
expression.NewLiteral("-a", stringType), expression.NewLiteral("-m", stringType), expression.NewLiteral(revertMessage, stringType))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, err = commitFunc.Eval(ctx, row)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
// String implements the Stringer interface.
|
||||
func (r *RevertFunc) String() string {
|
||||
return fmt.Sprint("DOLT_REVERT()")
|
||||
}
|
||||
|
||||
// IsNullable implements the Expression interface.
|
||||
func (r *RevertFunc) IsNullable() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// Resolved implements the Expression interface.
|
||||
func (r *RevertFunc) Resolved() bool {
|
||||
for _, expr := range r.ChildExpressions {
|
||||
if !expr.Resolved() {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (r *RevertFunc) Type() sql.Type {
|
||||
return sql.Int8
|
||||
}
|
||||
|
||||
// Children implements the Expression interface.
|
||||
func (r *RevertFunc) Children() []sql.Expression {
|
||||
exprs := make([]sql.Expression, len(r.ChildExpressions))
|
||||
for i := range exprs {
|
||||
exprs[i] = r.ChildExpressions[i]
|
||||
}
|
||||
return exprs
|
||||
}
|
||||
|
||||
// WithChildren implements the Expression interface.
|
||||
func (r *RevertFunc) WithChildren(ctx *sql.Context, children ...sql.Expression) (sql.Expression, error) {
|
||||
return NewRevertFunc(ctx, children...)
|
||||
}
|
||||
200
integration-tests/bats/revert.bats
Normal file
200
integration-tests/bats/revert.bats
Normal file
@@ -0,0 +1,200 @@
|
||||
#!/usr/bin/env bats
|
||||
load $BATS_TEST_DIRNAME/helper/common.bash
|
||||
|
||||
setup() {
|
||||
setup_common
|
||||
dolt sql -q "CREATE TABLE test(pk BIGINT PRIMARY KEY, v1 BIGINT)"
|
||||
dolt add -A
|
||||
dolt commit -m "Created table"
|
||||
dolt sql -q "INSERT INTO test VALUES (1, 1)"
|
||||
dolt add -A
|
||||
dolt commit -m "Inserted 1"
|
||||
dolt sql -q "INSERT INTO test VALUES (2, 2)"
|
||||
dolt add -A
|
||||
dolt commit -m "Inserted 2"
|
||||
dolt sql -q "INSERT INTO test VALUES (3, 3)"
|
||||
dolt add -A
|
||||
dolt commit -m "Inserted 3"
|
||||
}
|
||||
|
||||
teardown() {
|
||||
assert_feature_version
|
||||
teardown_common
|
||||
}
|
||||
|
||||
@test "revert: HEAD" {
|
||||
dolt revert HEAD
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "$output" =~ "2,2" ]] || false
|
||||
[[ "${#lines[@]}" = "3" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: HEAD~1" {
|
||||
dolt revert HEAD~1
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "$output" =~ "3,3" ]] || false
|
||||
[[ "${#lines[@]}" = "3" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: HEAD & HEAD~1" {
|
||||
dolt revert HEAD HEAD~1
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "${#lines[@]}" = "2" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: has changes in the working set" {
|
||||
dolt sql -q "INSERT INTO test VALUES (4, 4)"
|
||||
run dolt revert HEAD
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "changes" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: conflicts" {
|
||||
dolt sql -q "INSERT INTO test VALUES (4, 4)"
|
||||
dolt add -A
|
||||
dolt commit -m "Inserted 4"
|
||||
dolt sql -q "REPLACE INTO test VALUES (4, 5)"
|
||||
dolt add -A
|
||||
dolt commit -m "Updated 4"
|
||||
run dolt revert HEAD~1
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "conflict" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: constraint violations" {
|
||||
dolt sql <<"SQL"
|
||||
CREATE TABLE parent (pk BIGINT PRIMARY KEY, v1 BIGINT, INDEX(v1));
|
||||
CREATE TABLE child (pk BIGINT PRIMARY KEY, v1 BIGINT, CONSTRAINT fk_name FOREIGN KEY (v1) REFERENCES parent (v1));
|
||||
INSERT INTO parent VALUES (10, 1), (20, 2);
|
||||
INSERT INTO child VALUES (1, 1), (2, 2);
|
||||
SQL
|
||||
dolt add -A
|
||||
dolt commit -m "MC1"
|
||||
dolt sql -q "DELETE FROM child WHERE pk = 2"
|
||||
dolt add -A
|
||||
dolt commit -m "MC2"
|
||||
dolt sql -q "DELETE FROM parent WHERE pk = 20"
|
||||
dolt add -A
|
||||
dolt commit -m "MC3"
|
||||
run dolt revert HEAD~1
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "constraint violation" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: too far back" {
|
||||
run dolt revert HEAD~10
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "ancestor" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: no changes" {
|
||||
run dolt revert HEAD~4
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "No changes were made" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: invalid hash" {
|
||||
run dolt revert aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "hash" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL HEAD" {
|
||||
dolt sql -q "SELECT DOLT_REVERT('HEAD')"
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "$output" =~ "2,2" ]] || false
|
||||
[[ "${#lines[@]}" = "3" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL HEAD~1" {
|
||||
dolt sql -q "SELECT DOLT_REVERT('HEAD~1')"
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "$output" =~ "3,3" ]] || false
|
||||
[[ "${#lines[@]}" = "3" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL HEAD & HEAD~1" {
|
||||
dolt sql -q "SELECT DOLT_REVERT('HEAD', 'HEAD~1')"
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "${#lines[@]}" = "2" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL has changes in the working set" {
|
||||
dolt sql -q "INSERT INTO test VALUES (4, 4)"
|
||||
run dolt sql -q "SELECT DOLT_REVERT('HEAD')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "changes" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL conflicts" {
|
||||
dolt sql -q "INSERT INTO test VALUES (4, 4)"
|
||||
dolt add -A
|
||||
dolt commit -m "Inserted 4"
|
||||
dolt sql -q "REPLACE INTO test VALUES (4, 5)"
|
||||
dolt add -A
|
||||
dolt commit -m "Updated 4"
|
||||
run dolt sql -q "SELECT DOLT_REVERT('HEAD~1')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "conflict" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL constraint violations" {
|
||||
dolt sql <<"SQL"
|
||||
CREATE TABLE parent (pk BIGINT PRIMARY KEY, v1 BIGINT, INDEX(v1));
|
||||
CREATE TABLE child (pk BIGINT PRIMARY KEY, v1 BIGINT, CONSTRAINT fk_name FOREIGN KEY (v1) REFERENCES parent (v1));
|
||||
INSERT INTO parent VALUES (10, 1), (20, 2);
|
||||
INSERT INTO child VALUES (1, 1), (2, 2);
|
||||
SQL
|
||||
dolt add -A
|
||||
dolt commit -m "MC1"
|
||||
dolt sql -q "DELETE FROM child WHERE pk = 2"
|
||||
dolt add -A
|
||||
dolt commit -m "MC2"
|
||||
dolt sql -q "DELETE FROM parent WHERE pk = 20"
|
||||
dolt add -A
|
||||
dolt commit -m "MC3"
|
||||
run dolt sql -q "SELECT DOLT_REVERT('HEAD~1')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "constraint violation" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL too far back" {
|
||||
run dolt sql -q "SELECT DOLT_REVERT('HEAD~10')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "ancestor" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL no changes" {
|
||||
dolt sql -q "SELECT DOLT_REVERT('HEAD~4')"
|
||||
run dolt sql -q "SELECT * FROM test" -r=csv
|
||||
[ "$status" -eq "0" ]
|
||||
[[ "$output" =~ "pk,v1" ]] || false
|
||||
[[ "$output" =~ "1,1" ]] || false
|
||||
[[ "$output" =~ "2,2" ]] || false
|
||||
[[ "$output" =~ "3,3" ]] || false
|
||||
[[ "${#lines[@]}" = "4" ]] || false
|
||||
}
|
||||
|
||||
@test "revert: SQL invalid hash" {
|
||||
run dolt sql -q "SELECT DOLT_REVERT('aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa')"
|
||||
[ "$status" -eq "1" ]
|
||||
[[ "$output" =~ "hash" ]] || false
|
||||
}
|
||||
Reference in New Issue
Block a user