diff --git a/go/cmd/dolt/commands/diff.go b/go/cmd/dolt/commands/diff.go index 7038c078a9c..e1ef2b9d507 100644 --- a/go/cmd/dolt/commands/diff.go +++ b/go/cmd/dolt/commands/diff.go @@ -676,6 +676,10 @@ func (dArgs *diffArgs) applyDotRevisions(queryist cli.Queryist, sqlCtx *sql.Cont if strings.Contains(args[0], "...") { refs := strings.Split(args[0], "...") + if doltdb.IsWorkingSetRef(refs[0]) && doltdb.IsWorkingSetRef(refs[1]) { + return fmt.Errorf("ambiguous three dot range '%s': at least one side must be a commit", args[0]) + } + if len(refs[0]) > 0 { right := refs[1] // Use current HEAD if right side of `...` does not exist diff --git a/go/libraries/doltcore/doltdb/doltdb.go b/go/libraries/doltcore/doltdb/doltdb.go index ee1205f3a99..a37841a375d 100644 --- a/go/libraries/doltcore/doltdb/doltdb.go +++ b/go/libraries/doltcore/doltdb/doltdb.go @@ -642,6 +642,46 @@ func (ddb *DoltDB) ResolveCommitRef(ctx context.Context, doltRef ref.DoltRef) (* return NewCommit(ctx, ddb.vrw, ddb.ns, commitVal) } +// ResolveCommitSpecStr resolves the commit spec string |spec| to a commit, using |headRef| to +// anchor relative specs such as HEAD. The pseudo-refs WORKING and STAGED name uncommitted roots +// rather than commits and are not valid here. Callers that need to treat them as a commit for the +// purpose of a merge base should use [DoltDB.ResolveCommitSpecStrForMergeBase] instead. +func (ddb *DoltDB) ResolveCommitSpecStr(ctx context.Context, spec string, headRef ref.DoltRef) (*Commit, error) { + cs, err := NewCommitSpec(spec) + if err != nil { + return nil, err + } + + optCmt, err := ddb.Resolve(ctx, cs, headRef) + if err != nil { + return nil, err + } + commit, ok := optCmt.ToCommit() + if !ok { + return nil, ErrGhostCommitEncountered + } + + return commit, nil +} + +// ResolveCommitSpecStrForMergeBase resolves the commit spec string |spec| to the commit that +// stands in for it when computing a merge base, using |headRef| to anchor relative specs. It is +// the merge base variant of [DoltDB.ResolveCommitSpecStr]. The pseudo-refs WORKING and STAGED name +// uncommitted roots rather than commits, so they resolve to the commit at |headRef| that those +// roots are built on top of, which is the fork point a three dot diff or log anchors against. A +// working set exists only relative to a branch, so |headRef| must be non-nil for WORKING or +// STAGED, otherwise it returns [ErrOperationNotSupportedInDetachedHead]. +func (ddb *DoltDB) ResolveCommitSpecStrForMergeBase(ctx context.Context, spec string, headRef ref.DoltRef) (*Commit, error) { + if IsWorkingSetRef(spec) { + if headRef == nil { + return nil, ErrOperationNotSupportedInDetachedHead + } + return ddb.ResolveCommitRef(ctx, headRef) + } + + return ddb.ResolveCommitSpecStr(ctx, spec, headRef) +} + // ResolveCommitRefAtRoot takes a DoltRef and returns a Commit, or an error if the commit cannot be found. The ref given must // point to a Commit. func (ddb *DoltDB) ResolveCommitRefAtRoot(ctx context.Context, ref ref.DoltRef, nomsRoot hash.Hash) (*Commit, error) { diff --git a/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go b/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go index 78977177537..60ac6e0b6a3 100644 --- a/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go +++ b/go/libraries/doltcore/sqle/dfunctions/has_ancestor.go @@ -69,7 +69,7 @@ func (a *HasAncestor) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if err != nil { return nil, err } - headCommit, err = ResolveRefSpec(ctx, headRef, ddb, headStr.(string)) + headCommit, err = ddb.ResolveCommitSpecStr(ctx, headStr.(string), headRef) if err != nil { return nil, err } @@ -85,7 +85,7 @@ func (a *HasAncestor) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { if err != nil { return nil, err } - ancCommit, err = ResolveRefSpec(ctx, headRef, ddb, ancStr.(string)) + ancCommit, err = ddb.ResolveCommitSpecStr(ctx, ancStr.(string), headRef) if err != nil { return nil, err } diff --git a/go/libraries/doltcore/sqle/dfunctions/merge_base.go b/go/libraries/doltcore/sqle/dfunctions/merge_base.go index 75bfec748b5..f141d40e12c 100644 --- a/go/libraries/doltcore/sqle/dfunctions/merge_base.go +++ b/go/libraries/doltcore/sqle/dfunctions/merge_base.go @@ -22,9 +22,7 @@ import ( "github.com/dolthub/go-mysql-server/sql/expression" "github.com/dolthub/go-mysql-server/sql/types" - "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/merge" - "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" ) @@ -77,11 +75,11 @@ func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return nil, errors.New("right value is not a string") } - left, err := ResolveRefSpec(ctx, headRef, doltDB, leftStr) + left, err := doltDB.ResolveCommitSpecStrForMergeBase(ctx, leftStr, headRef) if err != nil { return nil, err } - right, err := ResolveRefSpec(ctx, headRef, doltDB, rightStr) + right, err := doltDB.ResolveCommitSpecStrForMergeBase(ctx, rightStr, headRef) if err != nil { return nil, err } @@ -94,23 +92,6 @@ func (d MergeBase) Eval(ctx *sql.Context, row sql.Row) (interface{}, error) { return mergeBase.String(), nil } -// ResolveRefSpec resolves a commit spec string to a Commit object using the provided headRef and doltDB. -func ResolveRefSpec(ctx *sql.Context, headRef ref.DoltRef, doltDB *doltdb.DoltDB, spec string) (*doltdb.Commit, error) { - cs, err := doltdb.NewCommitSpec(spec) - if err != nil { - return nil, err - } - optCmt, err := doltDB.Resolve(ctx, cs, headRef) - if err != nil { - return nil, err - } - commit, ok := optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } - return commit, err -} - // String implements the sql.Expression interface. func (d MergeBase) String() string { return fmt.Sprintf("DOLT_MERGE_BASE(%s,%s)", d.Left().String(), d.Right().String()) diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_rebase.go b/go/libraries/doltcore/sqle/dprocedures/dolt_rebase.go index c42eb4eb9c8..0d8ebd5e239 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_rebase.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_rebase.go @@ -32,7 +32,6 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/merge" "github.com/dolthub/dolt/go/libraries/doltcore/rebase" "github.com/dolthub/dolt/go/libraries/doltcore/ref" - "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dfunctions" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/libraries/utils/argparser" ) @@ -875,7 +874,7 @@ func commitManuallyStagedChangesForStep(ctx *sql.Context, step rebase.RebasePlan return err } - originalCommit, err := dfunctions.ResolveRefSpec(ctx, headRef, doltDB, step.CommitHash) + originalCommit, err := doltDB.ResolveCommitSpecStr(ctx, step.CommitHash, headRef) if err != nil { return err } diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go index 28af324890f..c10f639acfa 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_diff.go @@ -29,7 +29,6 @@ import ( "github.com/dolthub/dolt/go/libraries/doltcore/diff" "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/merge" - "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/schema" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dtables" @@ -45,6 +44,10 @@ const diffTableDefaultRowCount = 1000 var ErrInvalidNonLiteralArgument = errors.NewKind("Invalid argument to %s: %s – only literal values supported") var ErrInvalidTableName = errors.NewKind("Invalid table name %s.") +// ErrAmbiguousThreeDotRange is returned for a three dot range whose sides are both working set +// refs, which has no commit fork point to anchor a merge base. +var ErrAmbiguousThreeDotRange = errors.NewKind("ambiguous three dot range '%s': at least one side must be a commit") + var _ sql.TableFunction = (*DiffTableFunction)(nil) var _ sql.IndexedTable = (*DiffTableFunction)(nil) var _ sql.TableNode = (*DiffTableFunction)(nil) @@ -340,22 +343,26 @@ func resolveCommitStrings(ctx *sql.Context, fromRef, toRef, dotRef interface{}, if strings.Contains(dotStr, "...") { refs := strings.Split(dotStr, "...") + if doltdb.IsWorkingSetRef(refs[0]) && doltdb.IsWorkingSetRef(refs[1]) { + return "", "", ErrAmbiguousThreeDotRange.New(dotStr) + } + headRef, err := sess.CWBHeadRef(ctx, db.Name()) if err != nil { return "", "", err } - rightCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, refs[0]) + leftCm, err := db.DbData().Ddb.ResolveCommitSpecStrForMergeBase(ctx, refs[0], headRef) if err != nil { return "", "", err } - leftCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, refs[1]) + rightCm, err := db.DbData().Ddb.ResolveCommitSpecStrForMergeBase(ctx, refs[1], headRef) if err != nil { return "", "", err } - mergeBase, err := merge.MergeBase(ctx, rightCm, leftCm) + mergeBase, err := merge.MergeBase(ctx, leftCm, rightCm) if err != nil { return "", "", err } @@ -404,24 +411,6 @@ func interfaceToString(r interface{}) (string, error) { return str, nil } -func resolveCommit(ctx *sql.Context, ddb *doltdb.DoltDB, headRef ref.DoltRef, cSpecStr string) (*doltdb.Commit, error) { - cs, err := doltdb.NewCommitSpec(cSpecStr) - if err != nil { - return nil, err - } - - optCmt, err := ddb.Resolve(ctx, cs, headRef) - if err != nil { - return nil, err - } - cm, ok := optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } - - return cm, nil -} - // WithChildren implements the sql.Node interface func (dtf *DiffTableFunction) WithChildren(ctx *sql.Context, node ...sql.Node) (sql.Node, error) { if len(node) != 0 { diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go index 5cda9235637..f3d58cb676a 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_log.go @@ -495,39 +495,30 @@ func (ltf *LogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter return nil, err } - for _, revisionStr := range revisionValStrs { - cs, err := doltdb.NewCommitSpec(revisionStr) - if err != nil { - return nil, err - } + if threeDot && doltdb.IsWorkingSetRef(revisionValStrs[0]) && doltdb.IsWorkingSetRef(revisionValStrs[1]) { + return nil, ErrAmbiguousThreeDotRange.New(revisionValStrs[0] + "..." + revisionValStrs[1]) + } - optCmt, err := sqledb.DbData().Ddb.Resolve(ctx, cs, headRef) + // Only a three dot range accepts WORKING and STAGED, where they anchor the merge base. + resolve := sqledb.DbData().Ddb.ResolveCommitSpecStr + if threeDot { + resolve = sqledb.DbData().Ddb.ResolveCommitSpecStrForMergeBase + } + for _, revisionStr := range revisionValStrs { + commit, err := resolve(ctx, revisionStr, headRef) if err != nil { return nil, err } - commit, ok = optCmt.ToCommit() - if err != nil { - return nil, doltdb.ErrGhostCommitEncountered - } commits = append(commits, commit) } var notCommits []*doltdb.Commit for _, notRevisionStr := range notRevisionValStrs { - cs, err := doltdb.NewCommitSpec(notRevisionStr) - if err != nil { - return nil, err - } - - optCmt, err := sqledb.DbData().Ddb.Resolve(ctx, cs, headRef) + notCommit, err := sqledb.DbData().Ddb.ResolveCommitSpecStr(ctx, notRevisionStr, headRef) if err != nil { return nil, err } - notCommit, ok := optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } notCommits = append(notCommits, notCommit) } @@ -538,20 +529,11 @@ func (ltf *LogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.RowIter return nil, err } - mergeCs, err := doltdb.NewCommitSpec(mergeBase.String()) - if err != nil { - return nil, err - } - // Use merge base as excluding commit - optCmt, err := sqledb.DbData().Ddb.Resolve(ctx, mergeCs, nil) + mergeCommit, err := sqledb.DbData().Ddb.ResolveCommitSpecStr(ctx, mergeBase.String(), nil) if err != nil { return nil, err } - mergeCommit, ok := optCmt.ToCommit() - if !ok { - return nil, doltdb.ErrGhostCommitEncountered - } notCommits = append(notCommits, mergeCommit) diff --git a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go index 30726e49ab3..fedc3ade269 100644 --- a/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go +++ b/go/libraries/doltcore/sqle/dtablefunctions/dolt_preview_merge_conflicts_summary.go @@ -297,12 +297,12 @@ func resolveBranchesToRoots(ctx *sql.Context, db dsess.SqlDatabase, leftBranch, return rootInfo{}, err } - leftCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, leftBranch) + leftCm, err := db.DbData().Ddb.ResolveCommitSpecStr(ctx, leftBranch, headRef) if err != nil { return rootInfo{}, err } - rightCm, err := resolveCommit(ctx, db.DbData().Ddb, headRef, rightBranch) + rightCm, err := db.DbData().Ddb.ResolveCommitSpecStr(ctx, rightBranch, headRef) if err != nil { return rootInfo{}, err } diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 9255ae36314..4ec791cbe52 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -899,6 +899,16 @@ var DoltScripts = []queries.ScriptTest{ Query: "select has_ancestor('tag_btwo1', 'HEAD'), has_ancestor('tag_btwo1', @bone2), has_ancestor('tag_btwo1', @onetwo1), has_ancestor('tag_btwo1', @btwo1), has_ancestor('tag_btwo1', @main2), has_ancestor('tag_btwo1', @main1)", Expected: []sql.Row{{false, false, false, true, false, true}}, }, + { + // WORKING and STAGED name uncommitted roots, not commits, so ancestry against + // them is rejected. + Query: "select has_ancestor('HEAD', 'WORKING')", + ExpectedErrStr: "branch not found: WORKING", + }, + { + Query: "select has_ancestor('STAGED', 'HEAD')", + ExpectedErrStr: "branch not found: STAGED", + }, { Query: "use `mydb/onetwo`;", }, @@ -5714,6 +5724,60 @@ var LogTableFunctionScriptTests = []queries.ScriptTest{ }, }, }, + { + // See https://github.com/dolthub/dolt/issues/11204 + Name: "dolt_log: three dot diff accepts WORKING and STAGED", + SetUpScript: []string{ + "create table t (pk int primary key);", + "call dolt_add('.');", + "call dolt_commit('-m', 'commit 1');", + "call dolt_checkout('-b', 'new-branch');", + "insert into t values (1);", + "call dolt_commit('-am', 'commit 2');", + "insert into t values (2);", + "call dolt_commit('-am', 'commit 3');", + "call dolt_checkout('main');", + "insert into t values (99);", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT message from dolt_log('main...new-branch') ORDER BY date DESC;", + Expected: []sql.Row{{"commit 3"}, {"commit 2"}}, + }, + { + Query: "SELECT message from dolt_log('WORKING...new-branch') ORDER BY date DESC;", + // WORKING is not a commit, so it resolves to the current branch HEAD and + // the merge base excludes exactly the commits main...new-branch does. + Expected: []sql.Row{{"commit 3"}, {"commit 2"}}, + }, + { + Query: "SELECT message from dolt_log('STAGED...new-branch') ORDER BY date DESC;", + Expected: []sql.Row{{"commit 3"}, {"commit 2"}}, + }, + }, + }, + { + // See https://github.com/dolthub/dolt/issues/11204 + Name: "dolt_log: three dot WORKING on a detached head", + SetUpScript: []string{ + "create table t (pk int primary key);", + "call dolt_add('.');", + "call dolt_commit('-m', 'commit 1');", + "call dolt_tag('v1');", + "call dolt_checkout('-b', 'new-branch');", + "insert into t values (1);", + "call dolt_commit('-am', 'commit 2');", + "use `mydb/v1`;", + }, + Assertions: []queries.ScriptTestAssertion{ + { + // TODO(elianddb): unlike git, dolt cannot resolve WORKING for a merge base on a + // detached head, so this errors today instead of listing what new-branch added. + Query: "SELECT message from dolt_log('WORKING...new-branch') ORDER BY date DESC;", + ExpectedErrStr: "this operation is not supported while in a detached head state", + }, + }, + }, } var JsonDiffTableFunctionScriptTests = []queries.ScriptTest{ diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries_diff.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries_diff.go index 449b32aedf8..bfeb36ead00 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries_diff.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries_diff.go @@ -2016,6 +2016,93 @@ on a.to_pk = b.to_pk;`, }, }, }, + { + // See https://github.com/dolthub/dolt/issues/11204 + Name: "dolt_diff: three dot diff supports WORKING and STAGED", + SetUpScript: []string{ + "CREATE TABLE t (pk int primary key, c1 varchar(20));", + "INSERT INTO t VALUES (1, 'one');", + "CALL DOLT_ADD('.');", + "CALL DOLT_COMMIT('-m', 'init');", + + "CALL DOLT_BRANCH('bar');", + "CALL DOLT_CHECKOUT('bar');", + "INSERT INTO t VALUES (2, 'two');", + "CALL DOLT_COMMIT('-am', 'bar change');", + + // Row 3 is staged and row 4 is left in the working tree so that the staged + // root and the working root differ from each other and from HEAD. + "CALL DOLT_CHECKOUT('main');", + "INSERT INTO t VALUES (3, 'three');", + "CALL DOLT_ADD('.');", + "INSERT INTO t VALUES (4, 'four');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('HEAD...bar', 't') ORDER BY COALESCE(from_pk, to_pk);", + Expected: []sql.Row{ + {nil, 2, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('WORKING...bar', 't') ORDER BY COALESCE(from_pk, to_pk);", + // A revision to the left of the three dots only chooses the merge base, so + // the uncommitted change on the current branch is not part of the diff and + // the result is the same as for HEAD. + Expected: []sql.Row{ + {nil, 2, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('STAGED...bar', 't') ORDER BY COALESCE(from_pk, to_pk);", + Expected: []sql.Row{ + {nil, 2, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('bar...WORKING', 't') ORDER BY COALESCE(from_pk, to_pk);", + // A revision to the right of the three dots is the diff target, so the + // working root contributes both of its uncommitted rows. + Expected: []sql.Row{ + {nil, 3, "added"}, + {nil, 4, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('bar...STAGED', 't') ORDER BY COALESCE(from_pk, to_pk);", + // The staged root contains row 3 but not row 4, which was never added. + Expected: []sql.Row{ + {nil, 3, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('WORKING...STAGED', 't') ORDER BY COALESCE(from_pk, to_pk);", + ExpectedErr: dtablefunctions.ErrAmbiguousThreeDotRange, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('STAGED...WORKING', 't') ORDER BY COALESCE(from_pk, to_pk);", + ExpectedErr: dtablefunctions.ErrAmbiguousThreeDotRange, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('HEAD...WORKING', 't') ORDER BY COALESCE(from_pk, to_pk);", + // A real commit on the left keeps the working set as the diff target, so this + // surfaces both uncommitted rows. + Expected: []sql.Row{ + {nil, 3, "added"}, + {nil, 4, "added"}, + }, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('WORKING...HEAD', 't') ORDER BY COALESCE(from_pk, to_pk);", + // The merge base and the target are both HEAD, so there is no diff. + Expected: []sql.Row{}, + }, + { + Query: "SELECT from_pk, to_pk, diff_type FROM DOLT_DIFF('bar...nope', 't');", + ExpectedErrStr: "branch not found: nope", + }, + }, + }, } var DiffStatTableFunctionScriptTests = []queries.ScriptTest{ @@ -2718,6 +2805,38 @@ inner join t as of @Commit3 on rows_unmodified = t.pk;`, }, }, }, + { + // See https://github.com/dolthub/dolt/issues/11204 + Name: "dolt_diff_stat: three dot diff supports WORKING and STAGED", + SetUpScript: []string{ + "CREATE TABLE t (pk int primary key, c1 varchar(20));", + "INSERT INTO t VALUES (1, 'one');", + "CALL DOLT_ADD('.');", + "CALL DOLT_COMMIT('-m', 'init');", + "CALL DOLT_BRANCH('bar');", + "CALL DOLT_CHECKOUT('bar');", + "INSERT INTO t VALUES (2, 'two');", + "CALL DOLT_COMMIT('-am', 'bar change');", + "CALL DOLT_CHECKOUT('main');", + "INSERT INTO t VALUES (3, 'three');", + "CALL DOLT_ADD('.');", + "INSERT INTO t VALUES (4, 'four');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT table_name, rows_added FROM dolt_diff_stat('WORKING...bar', 't');", + Expected: []sql.Row{{"t", 1}}, + }, + { + Query: "SELECT table_name, rows_added FROM dolt_diff_stat('bar...WORKING', 't');", + Expected: []sql.Row{{"t", 2}}, + }, + { + Query: "SELECT table_name, rows_added FROM dolt_diff_stat('bar...STAGED', 't');", + Expected: []sql.Row{{"t", 1}}, + }, + }, + }, } var DiffSummaryTableFunctionScriptTests = []queries.ScriptTest{ @@ -3493,6 +3612,38 @@ var DiffSummaryTableFunctionScriptTests = []queries.ScriptTest{ }, }, }, + { + // See https://github.com/dolthub/dolt/issues/11204 + Name: "dolt_diff_summary: three dot diff supports WORKING and STAGED", + SetUpScript: []string{ + "CREATE TABLE t (pk int primary key, c1 varchar(20));", + "INSERT INTO t VALUES (1, 'one');", + "CALL DOLT_ADD('.');", + "CALL DOLT_COMMIT('-m', 'init');", + "CALL DOLT_BRANCH('bar');", + "CALL DOLT_CHECKOUT('bar');", + "INSERT INTO t VALUES (2, 'two');", + "CALL DOLT_COMMIT('-am', 'bar change');", + "CALL DOLT_CHECKOUT('main');", + "INSERT INTO t VALUES (3, 'three');", + "CALL DOLT_ADD('.');", + "INSERT INTO t VALUES (4, 'four');", + }, + Assertions: []queries.ScriptTestAssertion{ + { + Query: "SELECT from_table_name, to_table_name, diff_type, data_change, schema_change from dolt_diff_summary('WORKING...bar', 't');", + Expected: []sql.Row{{"t", "t", "modified", true, false}}, + }, + { + Query: "SELECT from_table_name, to_table_name, diff_type, data_change, schema_change from dolt_diff_summary('bar...WORKING', 't');", + Expected: []sql.Row{{"t", "t", "modified", true, false}}, + }, + { + Query: "SELECT from_table_name, to_table_name, diff_type, data_change, schema_change from dolt_diff_summary('bar...STAGED', 't');", + Expected: []sql.Row{{"t", "t", "modified", true, false}}, + }, + }, + }, } var PatchTableFunctionScriptTests = []queries.ScriptTest{ diff --git a/integration-tests/bats/diff.bats b/integration-tests/bats/diff.bats index 6ec1ee7c37a..85b9c2b83c1 100644 --- a/integration-tests/bats/diff.bats +++ b/integration-tests/bats/diff.bats @@ -491,6 +491,50 @@ SQL [[ ! "$output" =~ "- | 2" ]] || false } +@test "diff: three dot diff supports WORKING and STAGED" { + # See https://github.com/dolthub/dolt/issues/11204 + dolt sql -q 'insert into test values (0,0,0,0,0,0)' + dolt add . + dolt commit -m table + dolt sql -q "call dolt_branch('branch1')" + dolt --branch branch1 sql -q "insert into test values (1,1,1,1,1,1); call dolt_add('.'); call dolt_commit('-m', 'row');" + # Stage row 2 and leave row 3 in the working set so STAGED and WORKING differ. + dolt sql -q 'insert into test values (2,2,2,2,2,2)' + dolt add . + dolt sql -q 'insert into test values (3,3,3,3,3,3)' + + run dolt diff WORKING...branch1 + [ "$status" -eq 0 ] + [[ "$output" =~ "+ | 1" ]] || false + [[ ! "$output" =~ "+ | 2" ]] || false + [[ ! "$output" =~ "+ | 3" ]] || false + + run dolt diff STAGED...branch1 + [ "$status" -eq 0 ] + [[ "$output" =~ "+ | 1" ]] || false + [[ ! "$output" =~ "+ | 3" ]] || false + + run dolt diff branch1...WORKING + [ "$status" -eq 0 ] + [[ "$output" =~ "+ | 2" ]] || false + [[ "$output" =~ "+ | 3" ]] || false + [[ ! "$output" =~ "+ | 1" ]] || false + + run dolt diff branch1...STAGED + [ "$status" -eq 0 ] + [[ "$output" =~ "+ | 2" ]] || false + [[ ! "$output" =~ "+ | 3" ]] || false + + run dolt diff STAGED...WORKING + [ "$status" -eq 1 ] + [[ "$output" =~ "at least one side must be a commit" ]] || false + + working_base=$(dolt sql -r csv -q "SELECT dolt_merge_base('branch1', 'WORKING')" | tail -n 1) + head_base=$(dolt sql -r csv -q "SELECT dolt_merge_base('branch1', 'HEAD')" | tail -n 1) + [ -n "$working_base" ] + [ "$working_base" = "$head_base" ] +} + @test "diff: data and schema changes" { dolt sql <