diff --git a/go/cmd/dolt/commands/cnfcmds/cat.go b/go/cmd/dolt/commands/cnfcmds/cat.go index 5d20edfe2d..3e32fc9850 100644 --- a/go/cmd/dolt/commands/cnfcmds/cat.go +++ b/go/cmd/dolt/commands/cnfcmds/cat.go @@ -146,6 +146,11 @@ func printConflicts(queryist cli.Queryist, sqlCtx *sql.Context, tblNames []strin } } + // if there are no unmerged tables, return early + if len(mergeStatus.unmergedTables) == 0 { + return nil + } + // next print data conflicts for _, tblName := range mergeStatus.unmergedTables { shouldShowTable := isStringInArray(tblName, tblNames) @@ -294,6 +299,11 @@ func getMergeStatus(queryist cli.Queryist, sqlCtx *sql.Context) (mergeStatus, er return ms, errors.New("error: multiple rows in dolt_merge_status") } + // No active merge; return default merge status (isMerging=false) + if len(rows) == 0 { + return ms, nil + } + row := rows[0] ms.isMerging, err = commands.GetTinyIntColAsBool(row[0]) if err != nil { @@ -304,7 +314,11 @@ func getMergeStatus(queryist cli.Queryist, sqlCtx *sql.Context) (mergeStatus, er ms.sourceCommit = row[2].(string) ms.target = row[3].(string) unmergedTables := row[4].(string) - ms.unmergedTables = strings.Split(unmergedTables, ", ") + if unmergedTables == "" { + ms.unmergedTables = []string{} + } else { + ms.unmergedTables = strings.Split(unmergedTables, ", ") + } } return ms, nil diff --git a/go/cmd/dolt/commands/cnfcmds/resolve.go b/go/cmd/dolt/commands/cnfcmds/resolve.go index b5f78ef0f6..e9c21a2150 100644 --- a/go/cmd/dolt/commands/cnfcmds/resolve.go +++ b/go/cmd/dolt/commands/cnfcmds/resolve.go @@ -31,9 +31,9 @@ import ( var resDocumentation = cli.CommandDocumentationContent{ ShortDesc: "Automatically resolves all conflicts taking either ours or theirs for the given tables", LongDesc: ` - When a merge finds conflicting changes, it documents them in the dolt_conflicts table. A conflict is between two versions: ours (the rows at the destination branch head) and theirs (the rows at the source branch head). +When a merge finds conflicting changes, it documents them in the dolt_conflicts table. A conflict is between two versions: ours (the rows at the destination branch head) and theirs (the rows at the source branch head). - dolt conflicts resolve will automatically resolve the conflicts by taking either the ours or theirs versions for each row. +dolt conflicts resolve will automatically resolve the conflicts by taking either the ours or theirs versions for each row. `, Synopsis: []string{ `--ours|--theirs {{.LessThan}}table{{.GreaterThan}}...`, @@ -100,6 +100,13 @@ func (cmd ResolveCmd) Exec(ctx context.Context, commandStr string, args []string return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) } + // Allow committing transactions that still have unresolved conflicts. This mirrors behavior in other + // commands (e.g. rebase) and prevents autocommit from rolling back when resolving one table at a time + // while other tables remain conflicted. + if _, err = cli.GetRowsForSql(queryist.Queryist, queryist.Context, "set @@dolt_allow_commit_conflicts=1;"); err != nil { + return commands.HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + var verr errhand.VerboseError if apr.ContainsAny(autoResolverParams...) { verr = autoResolve(queryist.Queryist, queryist.Context, apr) diff --git a/integration-tests/bats/conflicts-resolve.bats b/integration-tests/bats/conflicts-resolve.bats index 40e7dd15f8..c461a3cbe8 100644 --- a/integration-tests/bats/conflicts-resolve.bats +++ b/integration-tests/bats/conflicts-resolve.bats @@ -17,6 +17,21 @@ basic_conflict() { dolt commit -am "main commit" } +two_conflicts() { + dolt sql -q "create table t (i int primary key, t text)" + dolt sql -q "create table t2 (i int primary key, t text)" + dolt add . + dolt commit -am "init commit" + dolt checkout -b other + dolt sql -q "insert into t values (1,'other')" + dolt sql -q "insert into t2 values (1,'other2')" + dolt commit -am "other commit" + dolt checkout main + dolt sql -q "insert into t values (1,'main')" + dolt sql -q "insert into t2 values (1,'main2')" + dolt commit -am "main commit" +} + teardown() { assert_feature_version teardown_common @@ -156,6 +171,77 @@ teardown() { [[ $output =~ "other" ]] || false } +@test "conflicts-resolve: two conflicted tables, resolve theirs all" { + two_conflicts + + run dolt sql -q "select * from t" + [ $status -eq 0 ] + [[ $output =~ "main" ]] || false + + run dolt sql -q "select * from t2" + [ $status -eq 0 ] + [[ $output =~ "main2" ]] || false + + run dolt merge other + [ $status -eq 1 ] + [[ $output =~ "Automatic merge failed" ]] || false + + run dolt conflicts cat . + [ $status -eq 0 ] + [[ "$output" =~ "i" ]] || false + + run dolt conflicts resolve --theirs . + [ $status -eq 0 ] + + run dolt conflicts cat . + [ $status -eq 0 ] + ! [[ "$output" =~ "i" ]] || false + + run dolt sql -q "select * from t" + [ $status -eq 0 ] + [[ $output =~ "other" ]] || false + run dolt sql -q "select * from t2" + [ $status -eq 0 ] + [[ $output =~ "other2" ]] || false +} + +@test "conflicts-resolve: two conflicted tables, resolve theirs one table, ours other table" { + two_conflicts + + run dolt sql -q "select * from t" + [ $status -eq 0 ] + [[ $output =~ "main" ]] || false + + run dolt sql -q "select * from t2" + [ $status -eq 0 ] + [[ $output =~ "main2" ]] || false + + run dolt merge other + [ $status -eq 1 ] + [[ $output =~ "Automatic merge failed" ]] || false + + run dolt conflicts cat . + [ $status -eq 0 ] + [[ "$output" =~ "i" ]] || false + + run dolt conflicts resolve --theirs t + [ $status -eq 0 ] + run dolt sql -q "select * from t" + [ $status -eq 0 ] + [[ $output =~ "other" ]] || false + + run dolt conflicts resolve --ours t2 + [ $status -eq 0 ] + run dolt sql -q "select * from t2" + [ $status -eq 0 ] + [[ $output =~ "main2" ]] || false + + run dolt conflicts cat . + [ $status -eq 0 ] + [ "$output" = "" ] + ! [[ "$output" =~ "i" ]] || false +} + @test "conflicts-resolve: merge main into other, resolve with ours" { basic_conflict