Add dolt reset HEAD~ (#1512)

This pr adds `dolt reset HEAD~`
This commit is contained in:
Vinai Rachakonda
2021-04-08 11:42:26 -04:00
committed by GitHub
parent 941748dcf0
commit eefa7abe3b
6 changed files with 272 additions and 6 deletions

View File

@@ -101,7 +101,22 @@ func (cmd ResetCmd) Exec(ctx context.Context, commandStr string, args []string,
err = actions.ResetHard(ctx, dEnv, arg, workingRoot, stagedRoot, headRoot)
} else {
stagedRoot, err = actions.ResetSoft(ctx, dEnv.DbData(), apr, stagedRoot, headRoot)
// Check whether the input argument is a ref.
if apr.NArg() == 1 {
argToCheck := apr.Arg(0)
ok := actions.ValidateIsRef(ctx, argToCheck, dEnv.DoltDB, dEnv.RepoStateReader())
// This is a valid ref
if ok {
err = actions.ResetSoftToRef(ctx, dEnv.DbData(), apr.Arg(0))
return handleResetError(err, usage)
}
}
tables := apr.Args()
stagedRoot, err = actions.ResetSoft(ctx, dEnv.DbData(), tables, stagedRoot, headRoot)
if err != nil {
return handleResetError(err, usage)
@@ -171,7 +186,12 @@ func printNotStaged(ctx context.Context, dEnv *env.DoltEnv, staged *doltdb.RootV
func handleResetError(err error, usage cli.UsagePrinter) int {
if actions.IsTblNotExist(err) {
tbls := actions.GetTablesForError(err)
bdr := errhand.BuildDError("Invalid Table(s):")
// In case the ref does not exist.
bdr := errhand.BuildDError("Invalid Ref or Table:")
if len(tbls) > 1 {
bdr = errhand.BuildDError("Invalid Table(s):")
}
for _, tbl := range tbls {
bdr.AddDetails("\t" + tbl)

View File

@@ -87,7 +87,7 @@ type CommitSpec struct {
// * remotes/origin/master~~
// * refs/heads/my-feature-branch^2~
//
// Constructing a |CommitSpec| does not mean the sepcified branch or commit
// Constructing a |CommitSpec| does not mean the specified branch or commit
// exists. This carries a description of how to find the specified commit. See
// |doltdb.Resolve| for resolving a |CommitSpec| to a |Commit|.
func NewCommitSpec(cSpecStr string) (*CommitSpec, error) {

View File

@@ -175,8 +175,8 @@ func ResetSoftTables(ctx context.Context, dbData env.DbData, apr *argparser.ArgP
return stagedRoot, nil
}
func ResetSoft(ctx context.Context, dbData env.DbData, apr *argparser.ArgParseResults, stagedRoot, headRoot *doltdb.RootValue) (*doltdb.RootValue, error) {
tables, err := getUnionedTables(ctx, apr.Args(), stagedRoot, headRoot)
func ResetSoft(ctx context.Context, dbData env.DbData, tables []string, stagedRoot, headRoot *doltdb.RootValue) (*doltdb.RootValue, error) {
tables, err := getUnionedTables(ctx, tables, stagedRoot, headRoot)
if err != nil {
return nil, err
@@ -211,6 +211,38 @@ func ResetSoft(ctx context.Context, dbData env.DbData, apr *argparser.ArgParseRe
return stagedRoot, nil
}
// ResetSoftToRef matches the `git reset --soft <REF>` pattern. It resets both staged and head to the previous ref
// and leaves the working unset. The user can then choose to create a commit that contains all changes since the ref.
func ResetSoftToRef(ctx context.Context, dbData env.DbData, cSpecStr string) error {
cs, err := doltdb.NewCommitSpec(cSpecStr)
if err != nil {
return err
}
newHead, err := dbData.Ddb.Resolve(ctx, cs, dbData.Rsr.CWBHeadRef())
if err != nil {
return err
}
foundRoot, err := newHead.GetRootValue()
if err != nil {
return err
}
// Changed the stage to old the root. Leave the working as is.
_, err = env.UpdateStagedRoot(ctx, dbData.Ddb, dbData.Rsw, foundRoot)
if err != nil {
return err
}
// Update the head to this commit
if err = dbData.Ddb.SetHeadToCommit(ctx, dbData.Rsr.CWBHeadRef(), newHead); err != nil {
return err
}
return err
}
func getUnionedTables(ctx context.Context, tables []string, stagedRoot, headRoot *doltdb.RootValue) ([]string, error) {
if len(tables) == 0 || (len(tables) == 1 && tables[0] == ".") {
var err error
@@ -258,3 +290,18 @@ func resetStaged(ctx context.Context, ddb *doltdb.DoltDB, rsw env.RepoStateWrite
return updatedRoot, env.UpdateStagedRootWithVErr(ddb, rsw, updatedRoot)
}
// ValidateIsRef validates whether the input parameter is a valid cString
func ValidateIsRef(ctx context.Context, cSpecStr string, ddb *doltdb.DoltDB, rsr env.RepoStateReader) bool {
cs, err := doltdb.NewCommitSpec(cSpecStr)
if err != nil {
return false
}
_, err = ddb.Resolve(ctx, cs, rsr.CWBHeadRef())
if err != nil {
return false
}
return true
}

View File

@@ -205,6 +205,8 @@ func TestJSONStructuralSharing(t *testing.T) {
err = db.Flush(ctx)
require.NoError(t, err)
err = db.(datas.GarbageCollector).GC(ctx)
require.NoError(t, err)
after := ts.Len()
// flush creates a single chunk

View File

@@ -328,7 +328,7 @@ SQL
dolt add .
run dolt reset LICENSE.md invalid
[ "$status" -eq 1 ]
[[ "$output" =~ "Invalid Table(s)" ]] || false
[[ "$output" =~ "Invalid Ref or Table" ]] || false
[[ "$output" =~ "invalid" ]] || false
run dolt status
[ "$status" -eq 0 ]

View File

@@ -204,4 +204,201 @@ SQL
@test "status: dolt reset --hard with more than one additional arg throws an error " {
run dolt reset --hard HEAD HEAD2
[ "$status" -eq 1 ]
}
@test "status: dolt reset hard with ~ works" {
dolt sql -q "CREATE TABLE test (pk int PRIMARY KEY);"
dolt commit -am "cm1"
dolt sql -q "INSERT INTO test values (1);"
dolt commit -am "cm2"
dolt sql -q "INSERT INTO test VALUES (2);"
dolt commit -am "cm3"
# Do a hard reset back one commit and confirm the appropriate values.
run dolt reset --hard HEAD~1
[ "$status" -eq 0 ]
run dolt sql -q "SELECT sum(pk) FROM test;"
[ "$status" -eq 0 ]
[[ "$output" =~ "1" ]] || false
# Since this is a hard reset double check the status
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "On branch master" ]] || false
[[ "$output" =~ "nothing to commit, working tree clean" ]] || false
# Run again with ~2 this time
dolt sql -q "INSERT INTO test VALUES (2);"
dolt commit -am "cm3"
# Do a hard reset back two commits and confirm the appropriate values.
run dolt reset --hard HEAD~2
[ "$status" -eq 0 ]
run dolt sql -q "SELECT sum(pk) FROM test;"
[ "$status" -eq 0 ]
[[ "$output" =~ "NULL" ]] || false
# Since this is a hard reset double check the status
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "On branch master" ]] || false
[[ "$output" =~ "nothing to commit, working tree clean" ]] || false
}
@test "status: dolt reset soft with ~ works" {
dolt sql -q "CREATE TABLE test (pk int PRIMARY KEY);"
dolt commit -am "cm1"
dolt sql -q "INSERT INTO test values (1);"
dolt commit -am "cm2"
# Make a dirty change
dolt sql -q "INSERT INTO test values (2)"
run dolt reset HEAD~
[ "$status" -eq 0 ]
# Verify that the changes are still there
run dolt sql -q "SELECT sum(pk) FROM test;"
[ "$status" -eq 0 ]
[[ "$output" =~ "3" ]] || false
# Now verify that commit log has changes
run dolt sql -q "SELECT count(*) from dolt_log"
[[ "$output" =~ "2" ]] || false
run dolt reset HEAD~1
[ "$status" -eq 0 ]
# Verify that the changes are still there
run dolt sql -q "SELECT sum(pk) FROM test;"
[ "$status" -eq 0 ]
[[ "$output" =~ "3" ]] || false
run dolt status
[[ "$output" =~ "Untracked files:" ]] || false
[[ "$output" =~ " (use \"dolt add <table|doc>\" to include in what will be committed)" ]] || false
[[ "$output" =~ " new table: test" ]] || false
# Now verify that commit log has changes
run dolt sql -q "SELECT count(*) from dolt_log"
[[ "$output" =~ "1" ]] || false
}
@test "status: dolt reset works with commit hash ref" {
dolt sql -q "CREATE TABLE tb1 (pk int PRIMARY KEY);"
dolt sql -q "INSERT INTO tb1 values (1);"
dolt commit -am "cm1"
cm1=$(get_head_commit)
dolt sql -q "CREATE TABLE tb2 (pk int PRIMARY KEY);"
dolt sql -q "INSERT INTO tb2 values (11);"
dolt commit -am "cm2"
cm2=$(get_head_commit)
dolt sql -q "CREATE TABLE tb3 (pk int PRIMARY KEY);"
dolt sql -q "INSERT INTO tb3 values (11);"
dolt commit -am "cm3"
cm3=$(get_head_commit)
# Try a soft reset to commit 3. Nothing should change
dolt reset $cm3
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "On branch master" ]] || false
[[ "$output" =~ "nothing to commit, working tree clean" ]] || false
# Do a soft reset to commit 2.
dolt reset $cm2
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "Untracked files:" ]] || false
[[ "$output" =~ " (use \"dolt add <table|doc>\" to include in what will be committed)" ]] || false
[[ "$output" =~ " new table: tb3" ]] || false
! [[ "$output" =~ " new table: tb2" ]] || false
run dolt sql -q "SELECT COUNT(*) FROM tb3"
[[ "$output" =~ "1" ]] || false
run dolt sql -q "SELECT COUNT(*) FROM dolt_log"
[[ "$output" =~ "3" ]] || false # includes init commit
dolt commit -am "commit 3"
# Do a soft reset to commit 1
dolt reset $cm1
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "Untracked files:" ]] || false
[[ "$output" =~ " (use \"dolt add <table|doc>\" to include in what will be committed)" ]] || false
[[ "$output" =~ " new table: tb3" ]] || false
[[ "$output" =~ " new table: tb2" ]] || false
! [[ "$output" =~ " new table: tb1" ]] || false
run dolt sql -q "SELECT COUNT(*) FROM dolt_log"
[[ "$output" =~ "2" ]] || false # includes init commit
}
@test "status: dolt reset works with branch ref" {
dolt sql -q "CREATE TABLE tbl(pk int);"
dolt sql -q "INSERT into tbl VALUES (1)"
dolt commit -am "cm1"
# create a new branch and make a change
dolt checkout -b test
dolt sql -q "INSERT INTO tbl VALUES (2),(3)"
dolt sql -q "CREATE TABLE tbl2(pk int);"
dolt commit -am "test cm1"
# go back to master and merge
dolt checkout master
dolt merge test
dolt sql -q "INSERT INTO tbl VALUES (4)"
dolt commit -am "cm2"
# execute the reset
dolt reset test
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "Changes not staged for commit:" ]] || false
[[ "$output" =~ " (use \"dolt add <table>\" to update what will be committed)" ]] || false
[[ "$output" =~ " (use \"dolt checkout <table>\" to discard changes in working directory)" ]] || false
[[ "$output" =~ " modified: tbl" ]] || false
! [[ "$output" =~ " new table: tb2" ]] || false
}
@test "status: dolt reset ref properly manages staged changes as well" {
dolt sql -q "CREATE TABLE tbl(pk int);"
dolt sql -q "INSERT into tbl VALUES (1)"
dolt commit -am "cm1"
dolt sql -q "INSERT INTO tbl VALUES (2)"
dolt add .
dolt reset HEAD
run dolt status
[ "$status" -eq 0 ]
[[ "$output" =~ "Changes not staged for commit:" ]] || false
[[ "$output" =~ " (use \"dolt add <table>\" to update what will be committed)" ]] || false
[[ "$output" =~ " (use \"dolt checkout <table>\" to discard changes in working directory)" ]] || false
[[ "$output" =~ " modified: tbl" ]] || false
}
@test "status: dolt reset throws errors for unknown ref/table" {
run dolt reset test
[ "$status" -eq 1 ]
[[ "$output" =~ "Invalid Ref or Table" ]] || false
[[ "$output" =~ "test" ]] || false
}
get_head_commit() {
dolt log -n 1 | grep -m 1 commit | cut -c 8-
}