From c107df2bc2bf6b59e217c10d877fdf40932fa234 Mon Sep 17 00:00:00 2001 From: jennifersp <44716627+jennifersp@users.noreply.github.com> Date: Sat, 14 Oct 2023 12:36:25 -0700 Subject: [PATCH] add `--all` flag for dolt push (#6787) --- go/cmd/dolt/cli/arg_parser_helpers.go | 3 +- go/cmd/dolt/commands/pull.go | 20 + go/cmd/dolt/commands/push.go | 126 +-- .../dtestutils/testcommands/multienv.go | 18 +- go/libraries/doltcore/env/actions/remotes.go | 91 +- go/libraries/doltcore/env/environment.go | 2 +- go/libraries/doltcore/env/remotes.go | 289 ++++-- .../doltcore/sqle/dprocedures/dolt_push.go | 43 +- integration-tests/bats/events.bats | 24 +- .../bats/helper/local-remote.bash | 1 + integration-tests/bats/pull.bats | 2 +- integration-tests/bats/push.bats | 20 +- .../bats/remotes-file-system.bats | 2 +- integration-tests/bats/remotes-localbs.bats | 2 +- integration-tests/bats/remotes-push-pull.bats | 853 ++++++++++++++++++ .../bats/remotes-sql-server.bats | 2 +- integration-tests/bats/remotes.bats | 643 +------------ integration-tests/bats/sql-pull.bats | 2 +- integration-tests/bats/sql-push.bats | 120 +-- integration-tests/bats/sql-server.bats | 2 +- 20 files changed, 1251 insertions(+), 1014 deletions(-) create mode 100644 integration-tests/bats/remotes-push-pull.bats diff --git a/go/cmd/dolt/cli/arg_parser_helpers.go b/go/cmd/dolt/cli/arg_parser_helpers.go index 0f9fa4465f..a2a6e1badd 100644 --- a/go/cmd/dolt/cli/arg_parser_helpers.go +++ b/go/cmd/dolt/cli/arg_parser_helpers.go @@ -102,9 +102,10 @@ func CreateMergeArgParser() *argparser.ArgParser { } func CreatePushArgParser() *argparser.ArgParser { - ap := argparser.NewArgParserWithMaxArgs("push", 2) + ap := argparser.NewArgParserWithVariableArgs("push") ap.SupportsFlag(SetUpstreamFlag, "u", "For every branch that is up to date or successfully pushed, add upstream (tracking) reference, used by argument-less {{.EmphasisLeft}}dolt pull{{.EmphasisRight}} and other commands.") ap.SupportsFlag(ForceFlag, "f", "Update the remote with local history, overwriting any conflicting history in the remote.") + ap.SupportsFlag(AllFlag, "", "Push all branches.") return ap } diff --git a/go/cmd/dolt/commands/pull.go b/go/cmd/dolt/commands/pull.go index 6f6a80d65f..a25a6fca38 100644 --- a/go/cmd/dolt/commands/pull.go +++ b/go/cmd/dolt/commands/pull.go @@ -300,3 +300,23 @@ func getRemoteHashForPull(apr *argparser.ArgParseResults, sqlCtx *sql.Context, q } return remoteHash, remote + "/" + branch, nil } + +// getDefaultRemote gets the name of the default remote. +func getDefaultRemote(sqlCtx *sql.Context, queryist cli.Queryist) (string, error) { + rows, err := GetRowsForSql(queryist, sqlCtx, "select name from dolt_remotes") + if err != nil { + return "", err + } + if len(rows) == 0 { + return "", env.ErrNoRemote + } + if len(rows) == 1 { + return rows[0][0].(string), nil + } + for _, row := range rows { + if row[0].(string) == "origin" { + return "origin", nil + } + } + return rows[0][0].(string), nil +} diff --git a/go/cmd/dolt/commands/push.go b/go/cmd/dolt/commands/push.go index a739d1f9d7..e9f0fcc5f8 100644 --- a/go/cmd/dolt/commands/push.go +++ b/go/cmd/dolt/commands/push.go @@ -18,13 +18,11 @@ import ( "context" "errors" "fmt" - "strconv" "strings" "sync" "time" "github.com/dolthub/go-mysql-server/sql" - "github.com/dolthub/go-mysql-server/sql/types" "github.com/dustin/go-humanize" "github.com/gocraft/dbr/v2" "github.com/gocraft/dbr/v2/dialect" @@ -34,13 +32,10 @@ import ( "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/env/actions" "github.com/dolthub/dolt/go/libraries/doltcore/remotestorage" - "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dprocedures" "github.com/dolthub/dolt/go/libraries/utils/argparser" - "github.com/dolthub/dolt/go/store/datas" "github.com/dolthub/dolt/go/store/datas/pull" ) @@ -121,11 +116,7 @@ func (cmd PushCmd) Exec(ctx context.Context, commandStr string, args []string, d errChan <- err return } - err = printPushMessage(sqlRows) - if err != nil { - errChan <- err - return - } + printPushResult(sqlRows) }() spinner := TextSpinner{} @@ -136,8 +127,8 @@ func (cmd PushCmd) Exec(ctx context.Context, commandStr string, args []string, d for { select { - case err := <-errChan: - return handlePushError(err, usage, apr, queryist, sqlCtx) + case err = <-errChan: + return handlePushError(err, usage) case <-ctx.Done(): if ctx.Err() != nil { switch ctx.Err() { @@ -168,6 +159,9 @@ func constructInterpolatedDoltPushQuery(apr *argparser.ArgParseResults) (string, if force := apr.Contains(cli.ForceFlag); force { args = append(args, "'--force'") } + if all := apr.Contains(cli.AllFlag); all { + args = append(args, fmt.Sprintf("'--%s'", cli.AllFlag)) + } for _, arg := range apr.Args { args = append(args, "?") params = append(params, arg) @@ -182,118 +176,22 @@ func constructInterpolatedDoltPushQuery(apr *argparser.ArgParseResults) (string, return interpolatedQuery, nil } -// getDefaultRemote gets the name of the default remote. -func getDefaultRemote(sqlCtx *sql.Context, queryist cli.Queryist) (string, error) { - rows, err := GetRowsForSql(queryist, sqlCtx, "select name from dolt_remotes") - if err != nil { - return "", err +// printPushResult prints the appropriate message for the given push output. +// This function is called only when error is nil. +func printPushResult(rows []sql.Row) { + if len(rows[0]) > 1 { + cli.Println(rows[0][1].(string)) } - if len(rows) == 0 { - return "", env.ErrNoRemote - } - if len(rows) == 1 { - return rows[0][0].(string), nil - } - for _, row := range rows { - if row[0].(string) == "origin" { - return "origin", nil - } - } - return rows[0][0].(string), nil -} - -// processFetchSpecs takes a string of fetch specs and returns the destination ref and remote ref -// Assumes the fetch specs look something like: ["refs/heads/*:refs/remotes/origin/*"] -func processFetchSpecs(fetchSpecs string, branch string) (destRef, remoteRef string) { - destAndRemoteRefs := strings.Split(fetchSpecs, ":") - destRef = destAndRemoteRefs[0] - destRef = strings.TrimPrefix(destRef, "[\"") - destRef = strings.ReplaceAll(destRef, "*", branch) - - remoteRef = destAndRemoteRefs[1] - remoteRef = strings.TrimSuffix(remoteRef, "\"]") - remoteRef = strings.ReplaceAll(remoteRef, "*", branch) - - return -} - -// printPushMessage prints the appropriate message for the given push output -func printPushMessage(rows []sql.Row) error { - var statusCode int64 - if intCode, ok := rows[0][0].(int64); ok { - statusCode = intCode - } else if strCode, ok := rows[0][0].(string); ok { - // remote execution returns status code as a string - intCode, err := strconv.Atoi(strCode) - if err != nil { - return err - } - statusCode = int64(intCode) - } - if statusCode == 0 && len(rows[0]) > 1 && rows[0][1].(string) == dprocedures.UpToDateMessage { - cli.Println(dprocedures.UpToDateMessage) - } - return nil } // handlePushError prints the appropriate error message and returns the exit code -func handlePushError(err error, usage cli.UsagePrinter, apr *argparser.ArgParseResults, queryist cli.Queryist, sqlCtx *sql.Context) int { +func handlePushError(err error, usage cli.UsagePrinter) int { if err == nil { return 0 } var verr errhand.VerboseError switch err { - case env.ErrNoUpstreamForBranch: - rows, err := GetRowsForSql(queryist, sqlCtx, "select active_branch()") - if err != nil { - verr = errhand.BuildDError("fatal: The current branch could not be identified").AddCause(err).Build() - } else { - currentBranch := rows[0][0].(string) - remoteName := "" - if defRemote, verr := getDefaultRemote(sqlCtx, queryist); verr == nil { - remoteName = defRemote - } - verr = errhand.BuildDError("fatal: The current branch " + currentBranch + " has no upstream branch.\n" + - "To push the current branch and set the remote as upstream, use\n" + - "\tdolt push --set-upstream " + remoteName + " " + currentBranch + "\n" + - "To have this happen automatically for branches without a tracking\n" + - "upstream, see 'push.autoSetupRemote' in 'dolt config --help'.").Build() - } - case env.ErrInvalidSetUpstreamArgs: - verr = errhand.BuildDError("error: --set-upstream requires and params.").SetPrintUsage().Build() - case doltdb.ErrIsAhead, actions.ErrCantFF, datas.ErrMergeNeeded: - rows, err := GetRowsForSql(queryist, sqlCtx, fmt.Sprintf("select url, fetch_specs from dolt_remotes where name = '%s'", apr.Arg(0))) - if err != nil { - verr = errhand.BuildDError("could not identify remote").AddCause(err).Build() - } else { - remoteUrl := rows[0][0].(string) - fetchSpecs := rows[0][1].(types.JSONDocument) - fetchSpecsStr, err := fetchSpecs.ToString(sqlCtx) - if err != nil { - verr = errhand.BuildDError("could not identify destination remote").AddCause(err).Build() - } - var branch string - if apr.NArg() > 1 { - branch = apr.Arg(1) - } else { - rows, err := GetRowsForSql(queryist, sqlCtx, "select active_branch()") - if err != nil { - verr = errhand.BuildDError("could not identify current branch").AddCause(err).Build() - } else { - branch = rows[0][0].(string) - } - } - destRef, remoteRef := processFetchSpecs(fetchSpecsStr, branch) - - cli.Printf("To %s\n", remoteUrl) - cli.Printf("! [rejected] %s -> %s (non-fast-forward)\n", destRef, remoteRef) - cli.Printf("error: failed to push some refs to '%s'\n", remoteUrl) - cli.Println("hint: Updates were rejected because the tip of your current branch is behind") - cli.Println("hint: its remote counterpart. Integrate the remote changes (e.g.") - cli.Println("hint: 'dolt pull ...') before pushing again.") - verr = errhand.BuildDError("").Build() - } case actions.ErrUnknownPushErr: s, ok := status.FromError(err) if ok && s.Code() == codes.PermissionDenied { diff --git a/go/libraries/doltcore/dtestutils/testcommands/multienv.go b/go/libraries/doltcore/dtestutils/testcommands/multienv.go index 17eaf34262..ecb6ba4db1 100644 --- a/go/libraries/doltcore/dtestutils/testcommands/multienv.go +++ b/go/libraries/doltcore/dtestutils/testcommands/multienv.go @@ -353,21 +353,31 @@ func (mr *MultiRepoTestSetup) PushToRemote(dbName, remoteName, branchName string if err != nil { mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error())) } - opts, err := env.NewPushOpts(ctx, apr, dEnv.RepoStateReader(), dEnv.DoltDB, false, false, false) + targets, remote, err := env.NewPushOpts(ctx, apr, dEnv.RepoStateReader(), dEnv.DoltDB, false, false, false, false) if err != nil { mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error())) } - remoteDB, err := opts.Remote.GetRemoteDB(ctx, dEnv.DoltDB.ValueReadWriter().Format(), mr.envs[dbName]) + remoteDB, err := remote.GetRemoteDB(ctx, dEnv.DoltDB.ValueReadWriter().Format(), mr.envs[dbName]) if err != nil { - mr.Errhand(actions.HandleInitRemoteStorageClientErr(opts.Remote.Name, opts.Remote.Url, err)) + mr.Errhand(actions.HandleInitRemoteStorageClientErr(remote.Name, remote.Url, err)) } tmpDir, err := dEnv.TempTableFilesDir() if err != nil { mr.Errhand(fmt.Sprintf("Failed to access .dolt directory: %s", err.Error())) } - err = actions.DoPush(ctx, dEnv.RepoStateReader(), dEnv.RepoStateWriter(), dEnv.DoltDB, remoteDB, tmpDir, opts, actions.NoopRunProgFuncs, actions.NoopStopProgFuncs) + + pushOptions := &env.PushOptions{ + Targets: targets, + Remote: remote, + Rsr: dEnv.RepoStateReader(), + Rsw: dEnv.RepoStateWriter(), + SrcDb: dEnv.DoltDB, + DestDb: remoteDB, + TmpDir: tmpDir, + } + _, err = actions.DoPush(ctx, pushOptions, actions.NoopRunProgFuncs, actions.NoopStopProgFuncs) if err != nil { mr.Errhand(fmt.Sprintf("Failed to push remote: %s", err.Error())) } diff --git a/go/libraries/doltcore/env/actions/remotes.go b/go/libraries/doltcore/env/actions/remotes.go index c11b9a872d..803617deee 100644 --- a/go/libraries/doltcore/env/actions/remotes.go +++ b/go/libraries/doltcore/env/actions/remotes.go @@ -38,11 +38,8 @@ import ( var ErrCantFF = errors.New("can't fast forward merge") var ErrInvalidPullArgs = errors.New("dolt pull takes at most two args") var ErrCannotPushRef = errors.New("cannot push ref") -var ErrFailedToSaveRepoState = errors.New("failed to save repo state") var ErrFailedToDeleteRemote = errors.New("failed to delete remote") var ErrFailedToGetRemoteDb = errors.New("failed to get remote db") -var ErrFailedToDeleteBackup = errors.New("failed to delete backup") -var ErrFailedToGetBackupDb = errors.New("failed to get backup db") var ErrUnknownPushErr = errors.New("unknown push error") type ProgStarter func(ctx context.Context) (*sync.WaitGroup, chan pull.Stats) @@ -94,37 +91,84 @@ func Push(ctx context.Context, tempTableDir string, mode ref.UpdateMode, destRef return err } -func DoPush(ctx context.Context, rsr env.RepoStateReader, rsw env.RepoStateWriter, srcDB, destDB *doltdb.DoltDB, tempTableDir string, opts *env.PushOpts, progStarter ProgStarter, progStopper ProgStopper) error { - var err error +// DoPush returns a message about whether the push was successful for each branch or a tag. +// This includes if there is a new remote branch created, upstream is set or push was rejected for a branch. +func DoPush(ctx context.Context, pushMeta *env.PushOptions, progStarter ProgStarter, progStopper ProgStopper) (returnMsg string, err error) { + var successPush, setUpstreamPush, failedPush []string + for _, targets := range pushMeta.Targets { + err = push(ctx, pushMeta.Rsr, pushMeta.TmpDir, pushMeta.SrcDb, pushMeta.DestDb, pushMeta.Remote, targets, progStarter, progStopper) + if err == nil { + if targets.HasUpstream { + // TODO: should add commit hash info for branches with upstream set + // (e.g. 74476cf38..080b073e7 branch1 -> branch1) + } else { + successPush = append(successPush, fmt.Sprintf(" * [new branch] %s -> %s", targets.SrcRef.GetPath(), targets.DestRef.GetPath())) + } + } else if errors.Is(err, doltdb.ErrIsAhead) || errors.Is(err, ErrCantFF) || errors.Is(err, datas.ErrMergeNeeded) { + failedPush = append(failedPush, fmt.Sprintf(" ! [rejected] %s -> %s (non-fast-forward)", targets.SrcRef.GetPath(), targets.DestRef.GetPath())) + continue + } else if !errors.Is(err, doltdb.ErrUpToDate) { + // this will allow getting successful push messages along with the error of current push + break + } + if targets.SetUpstream { + err = pushMeta.Rsw.UpdateBranch(targets.SrcRef.GetPath(), env.BranchConfig{ + Merge: ref.MarshalableRef{ + Ref: targets.DestRef, + }, + Remote: pushMeta.Remote.Name, + }) + if err != nil { + return "", err + } + setUpstreamPush = append(setUpstreamPush, fmt.Sprintf("branch '%s' set up to track '%s'.", targets.SrcRef.GetPath(), targets.RemoteRef.GetPath())) + } + } + returnMsg, err = buildReturnMsg(successPush, setUpstreamPush, failedPush, pushMeta.Remote.Url, err) + return +} + +// push performs push on a branch or a tag. +func push(ctx context.Context, rsr env.RepoStateReader, tmpDir string, src, dest *doltdb.DoltDB, remote *env.Remote, opts *env.PushTarget, progStarter ProgStarter, progStopper ProgStopper) error { switch opts.SrcRef.GetType() { case ref.BranchRefType: if opts.SrcRef == ref.EmptyBranchRef { - err = deleteRemoteBranch(ctx, opts.DestRef, opts.RemoteRef, srcDB, destDB, opts.Remote) + return deleteRemoteBranch(ctx, opts.DestRef, opts.RemoteRef, src, dest, *remote) } else { - err = PushToRemoteBranch(ctx, rsr, tempTableDir, opts.Mode, opts.SrcRef, opts.DestRef, opts.RemoteRef, srcDB, destDB, opts.Remote, progStarter, progStopper) + return PushToRemoteBranch(ctx, rsr, tmpDir, opts.Mode, opts.SrcRef, opts.DestRef, opts.RemoteRef, src, dest, *remote, progStarter, progStopper) } case ref.TagRefType: - err = pushTagToRemote(ctx, tempTableDir, opts.SrcRef, opts.DestRef, srcDB, destDB, progStarter, progStopper) + return pushTagToRemote(ctx, tmpDir, opts.SrcRef, opts.DestRef, src, dest, progStarter, progStopper) default: - err = fmt.Errorf("%w: %s of type %s", ErrCannotPushRef, opts.SrcRef.String(), opts.SrcRef.GetType()) + return fmt.Errorf("%w: %s of type %s", ErrCannotPushRef, opts.SrcRef.String(), opts.SrcRef.GetType()) + } +} + +// buildReturnMsg combines the push progress information of created branches, remote tracking branches +// and rejected branches, in order. // TODO: updated branches info is missing +func buildReturnMsg(success, setUpstream, failed []string, remoteUrl string, err error) (string, error) { + var retMsg string + if len(success) == 0 && len(failed) == 0 { + return "", err + } else if len(failed) > 0 { + err = env.ErrFailedToPush.New(remoteUrl) + } else if errors.Is(err, doltdb.ErrUpToDate) { + // if there are some branches with successful push + err = nil } - if err == nil || errors.Is(err, doltdb.ErrUpToDate) || errors.Is(err, pull.ErrDBUpToDate) { - if opts.SetUpstream { - err := rsw.UpdateBranch(opts.SrcRef.GetPath(), env.BranchConfig{ - Merge: ref.MarshalableRef{ - Ref: opts.DestRef, - }, - Remote: opts.Remote.Name, - }) - if err != nil { - return err - } - } + retMsg = fmt.Sprintf("To %s", remoteUrl) + for _, sMsg := range success { + retMsg = fmt.Sprintf("%s\n%s", retMsg, sMsg) } - - return err + for _, fMsg := range failed { + retMsg = fmt.Sprintf("%s\n%s", retMsg, fMsg) + } + for _, uMsg := range setUpstream { + retMsg = fmt.Sprintf("%s\n%s", retMsg, uMsg) + } + return retMsg, err } // PushTag pushes a commit tag and all underlying data from a local source database to a remote destination database. @@ -173,7 +217,6 @@ func PushToRemoteBranch(ctx context.Context, rsr env.RepoStateReader, tempTableD return err } cm, err := localDB.Resolve(ctx, cs, headRef) - if err != nil { return fmt.Errorf("%w; refspec not found: '%s'; %s", ref.ErrInvalidRefSpec, srcRef.GetPath(), err.Error()) } diff --git a/go/libraries/doltcore/env/environment.go b/go/libraries/doltcore/env/environment.go index 187395cb6a..e4b438d77b 100644 --- a/go/libraries/doltcore/env/environment.go +++ b/go/libraries/doltcore/env/environment.go @@ -1083,7 +1083,7 @@ func GetRefSpecs(rsr RepoStateReader, remoteName string) ([]ref.RemoteRefSpec, e } else if r, ok := remotes[remoteName]; ok { remote = r } else { - err = ErrUnknownRemote + err = ErrInvalidRepository.New(remoteName) } if err != nil { diff --git a/go/libraries/doltcore/env/remotes.go b/go/libraries/doltcore/env/remotes.go index f5045addff..a0a6b4ca5b 100644 --- a/go/libraries/doltcore/env/remotes.go +++ b/go/libraries/doltcore/env/remotes.go @@ -24,6 +24,8 @@ import ( "sort" "strings" + goerrors "gopkg.in/src-d/go-errors.v1" + "github.com/dolthub/dolt/go/libraries/doltcore/dbfactory" "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/ref" @@ -37,18 +39,33 @@ import ( var NoRemote = Remote{} var ErrBranchDoesNotMatchUpstream = errors.New("the upstream branch of your current branch does not match the name of your current branch") -var ErrUpstreamBranchAlreadySet = errors.New("upstream branch already set") -var ErrNoUpstreamForBranch = errors.New("the current branch has no upstream branch") var ErrFailedToReadDb = errors.New("failed to read from the db") var ErrUnknownBranch = errors.New("unknown branch") var ErrCannotSetUpstreamForTag = errors.New("cannot set upstream for tag") var ErrCannotPushRef = errors.New("cannot push ref") var ErrNoRefSpecForRemote = errors.New("no refspec for remote") -var ErrInvalidSetUpstreamArgs = errors.New("invalid set-upstream arguments") var ErrInvalidFetchSpec = errors.New("invalid fetch spec") var ErrPullWithRemoteNoUpstream = errors.New("You asked to pull from the remote '%s', but did not specify a branch. Because this is not the default configured remote for your current branch, you must specify a branch.") var ErrPullWithNoRemoteAndNoUpstream = errors.New("There is no tracking information for the current branch.\nPlease specify which branch you want to merge with.\n\n\tdolt pull \n\nIf you wish to set tracking information for this branch you can do so with:\n\n\t dolt push --set-upstream \n") +var ErrCurrentBranchHasNoUpstream = goerrors.NewKind("fatal: The current branch %s has no upstream branch.\n" + + "To push the current branch and set the remote as upstream, use\n" + + "\tdolt push --set-upstream %s %s\n" + + "To have this happen automatically for branches without a tracking\n" + + "upstream, see 'push.autoSetupRemote' in 'dolt config --help'.") +var ErrInvalidRepository = goerrors.NewKind("fatal: remote '%s' not found.\n" + + "Please make sure the remote exists.") +var ErrAllFlagCannotBeUsedWithRefSpec = goerrors.NewKind("fatal: --all can't be combined with refspecs") +var ErrNoPushDestination = goerrors.NewKind("fatal: No configured push destination.\n" + + "Either specify the URL from the command-line or configure a remote repository using\n\n" + + "\tdolt remote add \n\n" + + "and then push using the remote name\n\n" + + "\tdolt push \n\n") +var ErrFailedToPush = goerrors.NewKind("error: failed to push some refs to '%s'\n" + + "hint: Updates were rejected because the tip of your current branch is behind\n" + + "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + + "hint: 'dolt pull ...') before pushing again.\n") + func IsEmptyRemote(r Remote) bool { return len(r.Name) == 0 && len(r.Url) == 0 && r.FetchSpecs == nil && r.Params == nil } @@ -114,108 +131,190 @@ func (r *Remote) GetRemoteDBWithoutCaching(ctx context.Context, nbf *types.NomsB return doltdb.LoadDoltDBWithParams(ctx, nbf, r.Url, filesys2.LocalFS, params) } -type PushOpts struct { +// PushOptions contains information needed for push for +// one or more branches or a tag for a specific remote database. +type PushOptions struct { + Targets []*PushTarget + Rsr RepoStateReader + Rsw RepoStateWriter + Remote *Remote + SrcDb *doltdb.DoltDB + DestDb *doltdb.DoltDB + TmpDir string +} + +// PushTarget contains information needed for push per branch or tag. +type PushTarget struct { SrcRef ref.DoltRef DestRef ref.DoltRef RemoteRef ref.DoltRef - Remote Remote Mode ref.UpdateMode SetUpstream bool + HasUpstream bool } -func NewPushOpts(ctx context.Context, apr *argparser.ArgParseResults, rsr RepoStateReader, ddb *doltdb.DoltDB, force bool, setUpstream bool, pushAutoSetupRemote bool) (*PushOpts, error) { - var err error - remotes, err := rsr.GetRemotes() +func NewPushOpts(ctx context.Context, apr *argparser.ArgParseResults, rsr RepoStateReader, ddb *doltdb.DoltDB, force, setUpstream, pushAutoSetupRemote, all bool) ([]*PushTarget, *Remote, error) { + if apr.NArg() == 0 { + return getPushTargetsAndRemoteFromNoArg(ctx, rsr, ddb, force, setUpstream, pushAutoSetupRemote, all) + } + + rsrBranches, err := rsr.GetBranches() if err != nil { - return nil, err + return nil, nil, err } - remoteName := "origin" - - args := apr.Args - if len(args) == 1 { - if _, ok := remotes[args[0]]; ok { - remoteName = args[0] - args = []string{} - } - } - - remote, remoteOK := remotes[remoteName] currentBranch, err := rsr.CWBHeadRef() if err != nil { - return nil, err + return nil, nil, err } - branches, err := rsr.GetBranches() - if err != nil { - return nil, err - } - upstream, hasUpstream := branches[currentBranch.GetPath()] - var refSpec ref.RefSpec - if remoteOK && len(args) == 1 { - refSpec, err = getRefSpecFromStr(ctx, ddb, args[0]) + // the first argument defines the remote name + remoteName := apr.Arg(0) + if apr.NArg() == 1 { + remote, err := getRemote(rsr, remoteName) if err != nil { - return nil, err + return nil, nil, err } - } else if len(args) == 2 { - remoteName = args[0] - refSpec, err = getRefSpecFromStr(ctx, ddb, args[1]) - if err != nil { - return nil, err - } - } else if pushAutoSetupRemote { - if hasUpstream { - remoteName = upstream.Remote - refSpec, err = getCurrentBranchRefSpecFromUpstream(currentBranch, upstream, len(args)) - if err != nil { - return nil, err - } + + if all { + return getPushTargetsAndRemoteForAllBranches(ctx, rsrBranches, currentBranch, &remote, ddb, force, setUpstream) } else { - // no args - set upstream for current branch of 'origin' remote. - setUpstream = true - refSpec, err = getRefSpecFromStr(ctx, ddb, currentBranch.GetPath()) + defaultRemote, err := GetDefaultRemote(rsr) if err != nil { - return nil, err + return nil, nil, err } - } - } else if setUpstream { - return nil, ErrInvalidSetUpstreamArgs - } else if hasUpstream { - remoteName = upstream.Remote - refSpec, err = getCurrentBranchRefSpecFromUpstream(currentBranch, upstream, len(args)) - if err != nil { - return nil, err + + refSpec, _, hasUpstream, err := getCurrentBranchRefSpec(ctx, rsrBranches, rsr, ddb, remoteName, defaultRemote.Name == remoteName, true, setUpstream, pushAutoSetupRemote) + if err != nil { + return nil, nil, err + } + + opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, &remote, force, setUpstream, hasUpstream) + if err != nil { + return nil, nil, err + } + return []*PushTarget{opts}, &remote, nil } } else { - if len(args) == 0 { - return nil, ErrNoUpstreamForBranch + if all { + return nil, nil, ErrAllFlagCannotBeUsedWithRefSpec.New() } - return nil, errors.New("unknown error for remote push args") + refSpecNames := apr.Args[1:] + // validate given refSpec names + for _, refSpecName := range refSpecNames { + if len(refSpecName) == 0 { + return nil, nil, fmt.Errorf("%w: '%s'", ref.ErrInvalidRefSpec, refSpecName) + } + } + + remote, err := getRemote(rsr, apr.Arg(0)) + if err != nil { + return nil, nil, err + } + return getPushTargetsAndRemoteForBranchRefs(ctx, rsrBranches, refSpecNames, currentBranch, &remote, ddb, force, setUpstream) } +} - remote, remoteOK = remotes[remoteName] - - if !remoteOK { - return nil, fmt.Errorf("%w: '%s'", ErrUnknownRemote, remoteName) - } - - hasRef, err := ddb.HasRef(ctx, currentBranch) - +func getRemote(rsr RepoStateReader, name string) (Remote, error) { + remotes, err := rsr.GetRemotes() if err != nil { - return nil, fmt.Errorf("%w: %s", ErrFailedToReadDb, err.Error()) - } else if !hasRef { - return nil, fmt.Errorf("%w: '%s'", ErrUnknownBranch, currentBranch.GetPath()) + return NoRemote, err } + remote, ok := remotes[name] + if !ok { + return NoRemote, ErrInvalidRepository.New(name) + } + return remote, nil +} + +// getPushTargetsAndRemoteFromNoArg pushes the current branch on default remote if upstream is set or `-u` is defined; +// otherwise, all branches of default remote if `--all` flag is used. +func getPushTargetsAndRemoteFromNoArg(ctx context.Context, rsr RepoStateReader, ddb *doltdb.DoltDB, force, setUpstream, pushAutoSetupRemote, all bool) ([]*PushTarget, *Remote, error) { + rsrBranches, err := rsr.GetBranches() + if err != nil { + return nil, nil, err + } + + currentBranch, err := rsr.CWBHeadRef() + if err != nil { + return nil, nil, err + } + + remote, err := GetDefaultRemote(rsr) + if err != nil { + if err == ErrNoRemote { + err = ErrNoPushDestination.New() + } + return nil, nil, err + } + if all { + return getPushTargetsAndRemoteForAllBranches(ctx, rsrBranches, currentBranch, &remote, ddb, force, setUpstream) + } else { + refSpec, remoteName, hasUpstream, err := getCurrentBranchRefSpec(ctx, rsrBranches, rsr, ddb, remote.Name, true, false, setUpstream, pushAutoSetupRemote) + if err != nil { + return nil, nil, err + } + if remoteName != remote.Name { + remote, err = getRemote(rsr, remoteName) + if err != nil { + return nil, nil, err + } + } + + opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, &remote, force, setUpstream, hasUpstream) + if err != nil { + return nil, nil, err + } + return []*PushTarget{opts}, &remote, nil + } +} + +func getPushTargetsAndRemoteForAllBranches(ctx context.Context, rsrBranches map[string]BranchConfig, currentBranch ref.DoltRef, remote *Remote, ddb *doltdb.DoltDB, force, setUpstream bool) ([]*PushTarget, *Remote, error) { + localBranches, err := ddb.GetBranches(ctx) + if err != nil { + return nil, nil, err + } + var lbNames = make([]string, len(localBranches)) + for i, branch := range localBranches { + lbNames[i] = branch.GetPath() + } + return getPushTargetsAndRemoteForBranchRefs(ctx, rsrBranches, lbNames, currentBranch, remote, ddb, force, setUpstream) +} + +func getPushTargetsAndRemoteForBranchRefs(ctx context.Context, rsrBranches map[string]BranchConfig, localBranches []string, currentBranch ref.DoltRef, remote *Remote, ddb *doltdb.DoltDB, force, setUpstream bool) ([]*PushTarget, *Remote, error) { + var pushOptsList []*PushTarget + for _, refSpecName := range localBranches { + refSpec, err := getRefSpecFromStr(ctx, ddb, refSpecName) + if err != nil { + return nil, nil, err + } + + // if the remote of upstream does not match the remote given, + // it should push to the given remote creating new remote branch + upstream, hasUpstream := rsrBranches[refSpecName] + hasUpstream = hasUpstream && upstream.Remote == remote.Name + + opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, remote, force, setUpstream, hasUpstream) + if err != nil { + return nil, nil, err + } + + pushOptsList = append(pushOptsList, opts) + } + return pushOptsList, remote, nil +} + +func getPushTargetFromRefSpec(refSpec ref.RefSpec, currentBranch ref.DoltRef, remote *Remote, force, setUpstream, hasUpstream bool) (*PushTarget, error) { src := refSpec.SrcRef(currentBranch) dest := refSpec.DestRef(src) var remoteRef ref.DoltRef - + var err error switch src.GetType() { case ref.BranchRefType: - remoteRef, err = GetTrackingRef(dest, remote) + remoteRef, err = GetTrackingRef(dest, *remote) case ref.TagRefType: if setUpstream { err = ErrCannotSetUpstreamForTag @@ -223,23 +322,57 @@ func NewPushOpts(ctx context.Context, apr *argparser.ArgParseResults, rsr RepoSt default: err = fmt.Errorf("%w: '%s' of type '%s'", ErrCannotPushRef, src.String(), src.GetType()) } - if err != nil { return nil, err } - opts := &PushOpts{ + return &PushTarget{ SrcRef: src, DestRef: dest, RemoteRef: remoteRef, - Remote: remote, Mode: ref.UpdateMode{ Force: force, }, SetUpstream: setUpstream, + HasUpstream: hasUpstream, + }, nil +} + +// getCurrentBranchRefSpec is called when refSpec is NOT specified. Whether to push depends on the specified remote. +// If the specified remote is the default or the only remote, then it cannot push without its upstream set. +// If the specified remote is one of many and non-default remote, then it pushes regardless of upstream is set. +// If there is no remote specified, the current branch needs to have upstream set to push; otherwise, returns error. +// This function returns |refSpec| for current branch, name of the remote the branch is associated with and +// whether the current branch has upstream set. +func getCurrentBranchRefSpec(ctx context.Context, branches map[string]BranchConfig, rsr RepoStateReader, ddb *doltdb.DoltDB, remoteName string, isDefaultRemote, remoteSpecified, setUpstream, pushAutoSetupRemote bool) (ref.RefSpec, string, bool, error) { + var refSpec ref.RefSpec + currentBranch, err := rsr.CWBHeadRef() + if err != nil { + return nil, "", false, err } - return opts, nil + currentBranchName := currentBranch.GetPath() + upstream, hasUpstream := branches[currentBranchName] + + if remoteSpecified || pushAutoSetupRemote { + if isDefaultRemote && !pushAutoSetupRemote { + return nil, "", false, ErrCurrentBranchHasNoUpstream.New(currentBranchName, remoteName, currentBranchName) + } + setUpstream = true + refSpec, err = getRefSpecFromStr(ctx, ddb, currentBranchName) + if err != nil { + return nil, "", false, err + } + } else if hasUpstream { + remoteName = upstream.Remote + refSpec, err = getCurrentBranchRefSpecFromUpstream(currentBranch, upstream) + if err != nil { + return nil, "", false, err + } + } else { + return nil, "", false, ErrCurrentBranchHasNoUpstream.New(currentBranchName, remoteName, currentBranchName) + } + return refSpec, remoteName, hasUpstream && upstream.Remote == remoteName, nil } // RemoteForFetchArgs returns the remote and remaining arg strings for a fetch command @@ -359,11 +492,7 @@ func getRefSpecFromStr(ctx context.Context, ddb *doltdb.DoltDB, refSpecStr strin // getCurrentBranchRefSpecFromUpstream validates the number of args defined and returns ref.RefSpec object of // current branch corresponding to the given upstream. -func getCurrentBranchRefSpecFromUpstream(currentBranch ref.DoltRef, upstream BranchConfig, argsLen int) (ref.RefSpec, error) { - if argsLen > 0 { - return nil, fmt.Errorf("%w for '%s'", ErrUpstreamBranchAlreadySet, currentBranch) - } - +func getCurrentBranchRefSpecFromUpstream(currentBranch ref.DoltRef, upstream BranchConfig) (ref.RefSpec, error) { if currentBranch.GetPath() != upstream.Merge.Ref.GetPath() { return nil, ErrBranchDoesNotMatchUpstream } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_push.go b/go/libraries/doltcore/sqle/dprocedures/dolt_push.go index 04d83f62d6..a332771e42 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_push.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_push.go @@ -30,8 +30,6 @@ import ( "github.com/dolthub/dolt/go/store/datas" ) -var UpToDateMessage = "Everything up-to-date" - var doltPushSchema = []*sql.Column{ { Name: "status", @@ -48,13 +46,7 @@ var doltPushSchema = []*sql.Column{ // doltPush is the stored procedure version for the CLI command `dolt push`. func doltPush(ctx *sql.Context, args ...string) (sql.RowIter, error) { res, message, err := doDoltPush(ctx, args) - if err != nil { - if err == doltdb.ErrUpToDate { - return rowToIter(int64(cmdSuccess)), doltdb.ErrUpToDate - } - return nil, err - } - return rowToIter(int64(res), message), nil + return rowToIter(int64(res), message), err } func doDoltPush(ctx *sql.Context, args []string) (int, string, error) { @@ -69,7 +61,6 @@ func doDoltPush(ctx *sql.Context, args []string) (int, string, error) { sess := dsess.DSessFromSess(ctx.Session) dbData, ok := sess.GetDbData(ctx, dbName) - if !ok { return cmdFailure, "", fmt.Errorf("could not load database %s", dbName) } @@ -85,30 +76,48 @@ func doDoltPush(ctx *sql.Context, args []string) (int, string, error) { return cmdFailure, "", err } - opts, err := env.NewPushOpts(ctx, apr, dbData.Rsr, dbData.Ddb, apr.Contains(cli.ForceFlag), apr.Contains(cli.SetUpstreamFlag), pushAutoSetUpRemote) + targets, remote, err := env.NewPushOpts(ctx, apr, dbData.Rsr, dbData.Ddb, apr.Contains(cli.ForceFlag), apr.Contains(cli.SetUpstreamFlag), pushAutoSetUpRemote, apr.Contains(cli.AllFlag)) if err != nil { return cmdFailure, "", err } - remoteDB, err := sess.Provider().GetRemoteDB(ctx, dbData.Ddb.ValueReadWriter().Format(), opts.Remote, true) + remoteDB, err := sess.Provider().GetRemoteDB(ctx, dbData.Ddb.ValueReadWriter().Format(), *remote, true) if err != nil { - return 1, "", actions.HandleInitRemoteStorageClientErr(opts.Remote.Name, opts.Remote.Url, err) + return cmdFailure, "", actions.HandleInitRemoteStorageClientErr(remote.Name, remote.Url, err) } tmpDir, err := dbData.Rsw.TempTableFilesDir() if err != nil { return cmdFailure, "", err } - err = actions.DoPush(ctx, dbData.Rsr, dbData.Rsw, dbData.Ddb, remoteDB, tmpDir, opts, runProgFuncs, stopProgFuncs) + + var returnMsg string + po := &env.PushOptions{ + Targets: targets, + Remote: remote, + Rsr: dbData.Rsr, + Rsw: dbData.Rsw, + SrcDb: dbData.Ddb, + DestDb: remoteDB, + TmpDir: tmpDir, + } + returnMsg, err = actions.DoPush(ctx, po, runProgFuncs, stopProgFuncs) if err != nil { switch err { case doltdb.ErrUpToDate: - return cmdSuccess, UpToDateMessage, nil + return cmdSuccess, "Everything up-to-date", nil case datas.ErrMergeNeeded: - return cmdFailure, "", fmt.Errorf("%w; the tip of your current branch is behind its remote counterpart", err) + return cmdFailure, returnMsg, fmt.Errorf("%w; the tip of your current branch is behind its remote counterpart", err) default: + if returnMsg != "" { + // For multiple branches push, we need to print successful push message + // before the error message. We currently cannot return success message + // if there was a failed push with error. So, we need to include the success + // message in the error message before returning. + err = fmt.Errorf("%s\n%s", returnMsg, err.Error()) + } return cmdFailure, "", err } } // TODO : set upstream should be persisted outside of session - return cmdSuccess, "", nil + return cmdSuccess, returnMsg, nil } diff --git a/integration-tests/bats/events.bats b/integration-tests/bats/events.bats index 57e6a130b9..7453b53084 100644 --- a/integration-tests/bats/events.bats +++ b/integration-tests/bats/events.bats @@ -37,6 +37,8 @@ teardown() { @test "events: disabling current_timestamp one time event after execution" { dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "CREATE EVENT insert9 ON SCHEDULE AT CURRENT_TIMESTAMP DO INSERT INTO totals (int_col) VALUES (9);" + # used for debugging + dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" [ $status -eq 0 ] [[ $output =~ "| 1 |" ]] || false @@ -46,6 +48,8 @@ teardown() { [[ $output =~ "| 0 |" ]] || false dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "CREATE EVENT insert8 ON SCHEDULE AT CURRENT_TIMESTAMP ON COMPLETION PRESERVE DO INSERT INTO totals (int_col) VALUES (8);" + # used for debugging + dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" [ $status -eq 0 ] [[ $output =~ "| 2 |" ]] || false @@ -69,9 +73,12 @@ teardown() { [[ $output =~ "ON COMPLETION PRESERVE ENABLE" ]] || false sleep 4 - run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" + # used for debugging + dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" + + run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) >= 1 FROM totals;" [ $status -eq 0 ] - [[ $output =~ "| 1 |" ]] || false + [[ $output =~ "| 1 |" ]] || false run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM information_schema.events;" [ $status -eq 0 ] @@ -85,9 +92,13 @@ teardown() { @test "events: recurring event with STARTS and ENDS defined" { dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "CREATE EVENT insert1 ON SCHEDULE EVERY 2 SECOND STARTS CURRENT_TIMESTAMP + INTERVAL 2 SECOND ENDS CURRENT_TIMESTAMP + INTERVAL 5 SECOND DO INSERT INTO totals (int_col) VALUES (1);" sleep 10 - run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" + + # used for debugging + dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" + + run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) >= 2 FROM totals;" [ $status -eq 0 ] - [[ $output =~ "| 2 |" ]] || false + [[ $output =~ "| 1 |" ]] || false # should be dropped run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM information_schema.events;" @@ -97,7 +108,10 @@ teardown() { @test "events: recurring event with ENDS defined" { dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "CREATE EVENT insert1 ON SCHEDULE EVERY 2 SECOND ENDS CURRENT_TIMESTAMP + INTERVAL 3 SECOND ON COMPLETION PRESERVE DO INSERT INTO totals (int_col) VALUES (1); SELECT SLEEP(5);" - sleep 2 + sleep 3 + + # used for debugging + dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" run dolt sql-client -P $PORT -u dolt --use-db 'repo1' -q "SELECT COUNT(*) FROM totals;" [ $status -eq 0 ] [[ $output =~ "| 2 |" ]] || false diff --git a/integration-tests/bats/helper/local-remote.bash b/integration-tests/bats/helper/local-remote.bash index 8c5eb936e0..e65ac2fb78 100644 --- a/integration-tests/bats/helper/local-remote.bash +++ b/integration-tests/bats/helper/local-remote.bash @@ -78,6 +78,7 @@ SKIP_SERVER_TESTS=$(cat <<-EOM ~sql-server-remotesrv.bats~ ~large-update.bats~ ~remotes.bats~ +~remotes-push-pull.bats~ ~create-views.bats~ ~blame.bats~ ~multiple-tables.bats~ diff --git a/integration-tests/bats/pull.bats b/integration-tests/bats/pull.bats index 501133c3e5..92448f6927 100755 --- a/integration-tests/bats/pull.bats +++ b/integration-tests/bats/pull.bats @@ -114,7 +114,7 @@ teardown() { cd repo2 run dolt pull unknown [ "$status" -eq 1 ] - [[ "$output" =~ "unknown remote" ]] || false + [[ "$output" =~ "fatal: remote 'unknown' not found" ]] || false [[ ! "$output" =~ "panic" ]] || false } diff --git a/integration-tests/bats/push.bats b/integration-tests/bats/push.bats index 55c795591c..c8912c219a 100755 --- a/integration-tests/bats/push.bats +++ b/integration-tests/bats/push.bats @@ -61,9 +61,17 @@ teardown() { [[ "$output" =~ "t1" ]] || false } -@test "push: push infers correct remote" { +@test "push: push without repository defined throws error" { cd repo1 - dolt push main # should push to origin + run dolt push main # should push to origin + [ "$status" -eq 1 ] + [[ "$output" =~ "fatal: remote 'main' not found." ]] || false + + run dolt push origin # should not push to current branch since its upstream is not set + [ "$status" -eq 1 ] + [[ "$output" =~ "fatal: The current branch main has no upstream branch." ]] || false + + dolt push origin main cd ../repo2 dolt pull origin @@ -180,16 +188,16 @@ teardown() { cd ../repo1 run dolt push origin main [ "$status" -eq 1 ] - [[ "$output" =~ "the tip of your current branch is behind its remote counterpart" ]] || false + [[ "$output" =~ "hint: Updates were rejected because the tip of your current branch is behind" ]] || false dolt push --force origin main } @test "push: push to unknown remote" { cd repo1 - run dolt push unknkown main + run dolt push unknown main [ "$status" -eq 1 ] - [[ "$output" =~ "unknown remote: 'unknkown'" ]] || false + [[ "$output" =~ "fatal: remote 'unknown' not found" ]] || false } @test "push: push unknown branch" { @@ -203,7 +211,7 @@ teardown() { cd repo1 run dolt push -u origin [ "$status" -eq 1 ] - [[ "$output" =~ "--set-upstream requires and params" ]] || false + [[ "$output" =~ "fatal: The current branch main has no upstream branch." ]] || false } @test "push: pushing empty branch does not panic" { diff --git a/integration-tests/bats/remotes-file-system.bats b/integration-tests/bats/remotes-file-system.bats index e3af12efdf..ba745fc1a2 100644 --- a/integration-tests/bats/remotes-file-system.bats +++ b/integration-tests/bats/remotes-file-system.bats @@ -105,7 +105,7 @@ SQL #add origin push and fetch dolt remote add origin file://remote1 - dolt push main:notmain + dolt push origin main:notmain #fetch should now work without a specified remote because origin exists dolt fetch diff --git a/integration-tests/bats/remotes-localbs.bats b/integration-tests/bats/remotes-localbs.bats index 8e429e7fa2..1d1e5ce349 100644 --- a/integration-tests/bats/remotes-localbs.bats +++ b/integration-tests/bats/remotes-localbs.bats @@ -92,7 +92,7 @@ SQL #add origin push and fetch dolt remote add origin localbs://remote1 - dolt push main:notmain + dolt push origin main:notmain #fetch should now work without a specified remote because origin exists dolt fetch diff --git a/integration-tests/bats/remotes-push-pull.bats b/integration-tests/bats/remotes-push-pull.bats new file mode 100644 index 0000000000..cfa4b4477a --- /dev/null +++ b/integration-tests/bats/remotes-push-pull.bats @@ -0,0 +1,853 @@ +#!/usr/bin/env bats +load $BATS_TEST_DIRNAME/helper/common.bash + +remotesrv_pid= +setup() { + skiponwindows "tests are flaky on Windows" + setup_common + cd $BATS_TMPDIR + mkdir remotes-$$ + mkdir remotes-$$/empty + echo remotesrv log available here $BATS_TMPDIR/remotes-$$/remotesrv.log + remotesrv --http-port 1234 --dir ./remotes-$$ &> ./remotes-$$/remotesrv.log 3>&- & + remotesrv_pid=$! + cd dolt-repo-$$ + mkdir "dolt-repo-clones" +} + +teardown() { + teardown_common + kill $remotesrv_pid + rm -rf $BATS_TMPDIR/remotes-$$ +} + +@test "remotes-push-pull: pull also fetches" { + mkdir remote + mkdir repo1 + + cd repo1 + dolt init + dolt remote add origin file://../remote + dolt push origin main + + cd .. + dolt clone file://./remote repo2 + + cd repo2 + run dolt branch -va + [[ "$output" =~ "main" ]] || false + [[ ! "$output" =~ "other" ]] || false + + cd ../repo1 + dolt checkout -b other + dolt push origin other + + cd ../repo2 + dolt pull + run dolt branch -va + [[ "$output" =~ "main" ]] || false + [[ "$output" =~ "other" ]] || false +} + +@test "remotes-push-pull: pull also fetches, but does not merge other branches" { + mkdir remote + mkdir repo1 + + cd repo1 + dolt init + dolt remote add origin file://../remote + dolt push --set-upstream origin main + dolt checkout -b other + dolt commit --allow-empty -m "first commit on other" + dolt push --set-upstream origin other + + cd .. + dolt clone file://./remote repo2 + + cd repo2 + dolt pull + + + dolt commit --allow-empty -m "a commit for main from repo2" + dolt push + + run dolt checkout other + [ "$status" -eq 0 ] + [[ "$output" =~ "branch 'other' set up to track 'origin/other'." ]] || false + + run dolt log --oneline -n 1 + [ "$status" -eq 0 ] + [[ "$output" =~ "first commit on other" ]] || false + + run dolt status + [[ "$output" =~ "Your branch is up to date with 'origin/other'." ]] || false + + dolt commit --allow-empty -m "second commit on other from repo2" + dolt push + + cd ../repo1 + dolt checkout other + run dolt pull + [ "$status" -eq 0 ] + [[ "$output" =~ "Updating" ]] || false + + run dolt log --oneline -n 1 + [ "$status" -eq 0 ] + [[ "$output" =~ "second commit on other from repo2" ]] || false + + dolt checkout main + run dolt status + [[ "$output" =~ "behind 'origin/main' by 1 commit" ]] || false + + run dolt log --oneline -n 1 + [ "$status" -eq 0 ] + [[ ! "$output" =~ "a commit for main from repo2" ]] || false + + run dolt pull + [ "$status" -eq 0 ] + [[ "$output" =~ "Updating" ]] || false + + run dolt log --oneline -n 1 + [ "$status" -eq 0 ] + [[ "$output" =~ "a commit for main from repo2" ]] || false +} + +@test "remotes-push-pull: push and pull an unknown remote" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + run dolt push poop main + [ "$status" -eq 1 ] + [[ "$output" =~ "remote 'poop' not found" ]] || false + run dolt pull poop + [ "$status" -eq 1 ] + [[ "$output" =~ "remote 'poop' not found" ]] || false +} + +@test "remotes-push-pull: push with only one argument" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + run dolt push test-remote + [ "$status" -eq 1 ] + [[ "$output" =~ "fatal: The current branch main has no upstream branch." ]] || false + [[ "$output" =~ "To push the current branch and set the remote as upstream, use" ]] || false + [[ "$output" =~ "dolt push --set-upstream test-remote main" ]] || false + [[ "$output" =~ "To have this happen automatically for branches without a tracking" ]] || false + [[ "$output" =~ "upstream, see 'push.autoSetupRemote' in 'dolt config --help'" ]] || false +} + +@test "remotes-push-pull: push without set-upstream works with autoSetUpRemote set to true" { + dolt config --local --add push.autoSetUpRemote true + dolt remote add test-remote http://localhost:50051/test-org/test-repo + run dolt push test-remote main + [ "$status" -eq 0 ] + [[ "$output" =~ "Uploading" ]] || false + + run dolt push test-remote main + [ "$status" -eq 0 ] + [[ "$output" =~ "Everything up-to-date" ]] || false +} + +@test "remotes-push-pull: push and pull main branch from a remote" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + run dolt push --set-upstream test-remote main + [ "$status" -eq 0 ] + [ -d "$BATS_TMPDIR/remotes-$$/test-org/test-repo" ] + run dolt pull test-remote + [ "$status" -eq 0 ] + [[ "$output" =~ "Everything up-to-date" ]] || false +} + +@test "remotes-push-pull: push and pull non-main branch from remote" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt checkout -b test-branch + run dolt push --set-upstream test-remote test-branch + [ "$status" -eq 0 ] + run dolt pull test-remote + [ "$status" -eq 0 ] + [[ "$output" =~ "Everything up-to-date" ]] || false +} + +@test "remotes-push-pull: pull with explicit remote and branch" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt checkout -b test-branch + dolt sql -q "create table t1(c0 varchar(100));" + dolt add . + dolt commit -am "adding table t1" + run dolt push test-remote test-branch + [ "$status" -eq 0 ] + dolt checkout main + run dolt sql -q "show tables" + [ "$status" -eq 0 ] + [[ ! "$output" =~ "t1" ]] || false + + # Specifying a non-existent remote branch returns an error + run dolt pull test-remote doesnotexist + [ "$status" -eq 1 ] + [[ "$output" =~ 'branch "doesnotexist" not found on remote' ]] || false + + # Explicitly specifying the remote and branch will merge in that branch + run dolt pull test-remote test-branch + [ "$status" -eq 0 ] + run dolt sql -q "show tables" + [ "$status" -eq 0 ] + [[ "$output" =~ "t1" ]] || false + + # Make a conflicting working set change and test that pull complains + dolt reset --hard HEAD^1 + dolt sql -q "create table t1 (pk int primary key);" + run dolt pull test-remote test-branch + [ "$status" -eq 1 ] + [[ "$output" =~ 'cannot merge with uncommitted changes' ]] || false + + # Commit changes and test that a merge conflict fails the pull + dolt add . + dolt commit -am "adding new t1 table" + run dolt pull test-remote test-branch + [ "$status" -eq 1 ] + [[ "$output" =~ "table with same name 't1' added in 2 commits can't be merged" ]] || false +} + +@test "remotes-push-pull: push and pull from non-main branch and use --set-upstream" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt checkout -b test-branch + run dolt push --set-upstream test-remote test-branch + [ "$status" -eq 0 ] + [[ ! "$output" =~ "panic:" ]] || false + dolt sql -q "create table test (pk int, c1 int, primary key(pk))" + dolt add . + dolt commit -m "Added test table" + run dolt push + [ "$status" -eq 0 ] +} + +@test "remotes-push-pull: push output" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt checkout -b test-branch + dolt sql -q "create table test (pk int, c1 int, primary key(pk))" + dolt add . + dolt commit -m "Added test table" + dolt push --set-upstream test-remote test-branch | tr "\n" "*" > output.txt + run tail -c 1 output.txt + [[ "$output" != "" ]] || false # should have a spinner +} + +@test "remotes-push-pull: push and pull with docs from remote" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + echo "license-text" > LICENSE.md + dolt docs upload LICENSE.md LICENSE.md + echo "readme-text" > README.md + dolt docs upload README.md README.md + dolt add . + dolt commit -m "test doc commit" + dolt push test-remote main + cd "dolt-repo-clones" + run dolt clone http://localhost:50051/test-org/test-repo + [ "$status" -eq 0 ] + + cd test-repo + run dolt log + [ "$status" -eq 0 ] + [[ "$output" =~ "test doc commit" ]] || false + + cd ../../ + echo "updated-license" > LICENSE.md + dolt docs upload LICENSE.md LICENSE.md + dolt add . + dolt commit -m "updated license" + dolt push test-remote main + + cd dolt-repo-clones/test-repo + run dolt pull + [[ "$output" =~ "Updating" ]] || false + run dolt log + [ "$status" -eq 0 ] + [[ "$output" =~ "updated license" ]] || false + dolt docs print LICENSE.md > LICENSE.md + run cat LICENSE.md + [ "$status" -eq 0 ] + [[ "$output" =~ "updated-license" ]] || false +} + +@test "remotes-push-pull: push and pull tags to/from remote" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt sql < main" ]] || false + [[ "$output" =~ "tip of your current branch is behind" ]] || false +} + +@test "remotes-push-pull: dolt_pull() with divergent head" { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt push test-remote main + dolt sql < test-branch" ]] || false +} + +@test "remotes-push-pull: push not specifying a branch creates new remote branch and sets upstream on non-default remote when -u is used" { + dolt remote add origin http://localhost:50051/test-org/test-repo + dolt remote add test-remote http://localhost:50051/test-org/test-repo + run dolt remote -v + [[ "$output" =~ origin ]] || false + [[ "$output" =~ test-remote ]] || false + + dolt checkout -b test-branch + run dolt push -u test-remote + [ "$status" -eq 0 ] + [[ "$output" =~ "* [new branch] test-branch -> test-branch" ]] || false + [[ "$output" =~ "branch 'test-branch' set up to track 'test-remote/test-branch'." ]] || false +} + +@test "remotes-push-pull: push origin throws an error when no remote is set" { + run dolt remote -v + [ "$output" = "" ] + run dolt push origin + [ "$status" -eq 1 ] + [[ "$output" =~ "fatal: remote 'origin' not found" ]] || false +} + +@test "remotes-push-pull: push --all with no remote or refspec specified" { + dolt remote add origin http://localhost:50051/test-org/test-repo + run dolt remote -v + [[ "$output" =~ origin ]] || false + + dolt checkout -b new-branch + run dolt push --all + [ "$status" -eq 0 ] + [[ "$output" =~ "* [new branch] main -> main" ]] || false + [[ "$output" =~ "* [new branch] new-branch -> new-branch" ]] || false +} + +@test "remotes-push-pull: push --all with remote specified" { + mkdir remote + mkdir repo1 + + cd repo1 + dolt init + dolt remote add origin file://../remote + dolt push origin main + + cd .. + dolt clone file://./remote repo2 + cd repo2 + + dolt sql -q "CREATE TABLE test (pk INT PRIMARY KEY, col1 VARCHAR(10))" + dolt add . + dolt commit -am "create table" + dolt checkout -b branch1 + dolt sql -q "INSERT INTO test VALUES (1, '1')" + dolt commit -am "add 1s" + run dolt push --all origin # should not set upstream for new branches + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch1 -> branch1" ]] || false + + # on branch1 + run dolt push + [ "$status" -eq 1 ] + [[ "$output" =~ "fatal: The current branch branch1 has no upstream branch." ]] || false + + dolt sql -q "INSERT INTO test VALUES (2, '2')" + dolt commit -am "add 2s" + dolt checkout -b branch2 + dolt sql -q "INSERT INTO test VALUES (3, '3')" + dolt commit -am "add 3s" + run dolt push --all -u origin # should set upstream for all branches + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch1 -> branch1" ]] || false + [[ "$output" =~ "branch 'branch1' set up to track 'origin/branch1'." ]] || false + [[ "$output" =~ "branch 'branch2' set up to track 'origin/branch2'." ]] || false +} + +@test "remotes-push-pull: push --all with multiple remotes will push all local branches to default remote, regardless of their upstream" { + mkdir remote1 + mkdir repo1 + cd repo1 + dolt init + dolt remote add origin file://../remote1 + dolt push origin main + + cd .. + dolt clone file://./remote1 remote2 + cd repo1 + dolt remote add test-remote file://../remote2 + + dolt sql -q "CREATE TABLE test (pk INT PRIMARY KEY, col1 VARCHAR(10))" + dolt add . + dolt commit -am "create table" + dolt checkout -b branch1 + dolt sql -q "INSERT INTO test VALUES (1, '1')" + dolt commit -am "add 1s" + run dolt push --all -u origin + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch1 -> branch1" ]] || false + [[ "$output" =~ "branch 'branch1' set up to track 'origin/branch1'." ]] || false + + dolt sql -q "INSERT INTO test VALUES (2, '2')" + dolt commit -am "add 2s" + dolt checkout -b branch2 + dolt sql -q "INSERT INTO test VALUES (3, '3')" + dolt commit -am "add 3s" + run dolt push -u test-remote branch2 + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch2 -> branch2" ]] || false + [[ "$output" =~ "branch 'branch2' set up to track 'test-remote/branch2'." ]] || false + + run dolt branch -a + [ "$status" -eq 0 ] + [[ "$output" =~ "remotes/origin/branch1" ]] || false + [[ "$output" =~ "remotes/origin/main" ]] || false + [[ "$output" =~ "remotes/test-remote/branch2" ]] || false + [[ ! "$output" =~ "remotes/origin/branch2" ]] || false + + run dolt push --all # should push all branches to origin including branch2 + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch2 -> branch2" ]] || false + + run dolt branch -a + [ "$status" -eq 0 ] + [[ "$output" =~ "remotes/origin/branch2" ]] || false +} + +@test "remotes-push-pull: push --all with local branch that has conflict" { + mkdir remote + mkdir repo1 + + cd repo1 + dolt init + dolt remote add origin file://../remote + dolt push origin main + + cd .. + dolt clone file://./remote repo2 + cd repo2 + + dolt sql -q "CREATE TABLE test (pk INT PRIMARY KEY, col1 VARCHAR(10))" + dolt add . + dolt commit -am "create table" + dolt checkout -b branch1 + dolt sql -q "INSERT INTO test VALUES (1, '1')" + dolt commit -am "add 1s" + run dolt push --all -u # should not set upstream for new branches + [ "$status" -eq 0 ] + [[ "$output" =~ " * [new branch] branch1 -> branch1" ]] || false + [[ "$output" =~ "branch 'branch1' set up to track 'origin/branch1'." ]] || false + + cd ../repo1 + dolt fetch + dolt pull origin main + dolt checkout branch1 + dolt sql -q "INSERT INTO test VALUES (2, '2')" + dolt commit -am "add 2s" + dolt push + + cd ../repo2 + dolt sql -q "INSERT INTO test VALUES (2, '2')" + dolt commit -am "add 2s" + + dolt checkout -b branch2 + dolt sql -q "INSERT INTO test VALUES (3, '3')" + dolt commit -am "add 3s" + run dolt push --all -u # should set upstream for all branches + [ "$status" -eq 1 ] + [[ "$output" =~ " * [new branch] branch2 -> branch2" ]] || false + [[ "$output" =~ " ! [rejected] branch1 -> branch1 (non-fast-forward)" ]] || false + [[ "$output" =~ "branch 'branch2' set up to track 'origin/branch2'." ]] || false + [[ "$output" =~ "Updates were rejected because the tip of your current branch is behind" ]] || false +} + +@test "remotes-push-pull: pushing empty branch does not panic" { + run dolt push origin '' + [ "$status" -eq 1 ] + [[ "$output" =~ "invalid ref spec: ''" ]] || false +} + +@test "remotes-push-pull: push set upstream succeeds even if up to date" { + dolt remote add origin http://localhost:50051/test-org/test-repo + dolt push origin main + dolt checkout -b feature + dolt push --set-upstream origin feature + + cd dolt-repo-clones + dolt clone http://localhost:50051/test-org/test-repo + cd test-repo + dolt checkout -b feature + run dolt push + [ "$status" -eq 1 ] + [[ "$output" =~ "The current branch feature has no upstream branch." ]] || false + dolt push --set-upstream origin feature + dolt push +} + +@test "remotes-push-pull: local clone pushes to other branch" { + mkdir repo1 + mkdir rem1 + cd repo1 + dolt init + dolt sql -q "create table t (i int)" + run dolt status + [[ "$output" =~ "new table:" ]] || false + dolt commit -Am "new table" + dolt remote add rem1 file://../rem1 + dolt push rem1 main + cd .. + + dolt clone file://./rem1/ repo2 + cd repo2 + dolt checkout -b other + dolt sql -q "create table t2 (i int)" + dolt add . + dolt commit -am "adding table from other" + dolt remote add rem1 file://../rem1 + dolt push rem1 other + + cd ../repo1 + dolt fetch rem1 + dolt checkout other + run dolt log + [[ "$output" =~ "adding table from other" ]] || false +} + +@test "remotes-push-pull: pull with DOLT_AUTHOR_DATE and DOLT_COMMITER_DATE doesn't overwrite commit timestamps" { + mkdir repo1 + + cd repo1 + dolt init + dolt sql -q "create table t1(a int)" + dolt commit -Am "new table" + dolt branch b1 + dolt remote add origin file://../remote1 + dolt push origin main + dolt push origin b1 + + cd .. + dolt clone file://./remote1 repo2 + + cd repo2 + TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt fetch + TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt pull + + run dolt_log_in_PST + [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false + + TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt checkout b1 + run dolt_log_in_PST + [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false + + cd ../repo1 + dolt checkout b1 + dolt commit --allow-empty -m 'empty commit' + dolt push origin b1 + + cd ../repo2 + TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt pull + + run dolt_log_in_PST + [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false +} + +@test "remotes-push-pull: validate that a config is needed for a pull." { + dolt remote add test-remote http://localhost:50051/test-org/test-repo + dolt push test-remote main + dolt fetch test-remote + cd "dolt-repo-clones" + dolt clone http://localhost:50051/test-org/test-repo + cd .. + dolt sql < output.txt - run tail -c 1 output.txt - [[ "$output" != "" ]] || false # should have a spinner -} - -@test "remotes: push and pull with docs from remote" { - dolt remote add test-remote http://localhost:50051/test-org/test-repo - echo "license-text" > LICENSE.md - dolt docs upload LICENSE.md LICENSE.md - echo "readme-text" > README.md - dolt docs upload README.md README.md - dolt add . - dolt commit -m "test doc commit" - dolt push test-remote main - cd "dolt-repo-clones" - run dolt clone http://localhost:50051/test-org/test-repo - [ "$status" -eq 0 ] - - cd test-repo - run dolt log - [ "$status" -eq 0 ] - [[ "$output" =~ "test doc commit" ]] || false - - cd ../../ - echo "updated-license" > LICENSE.md - dolt docs upload LICENSE.md LICENSE.md - dolt add . - dolt commit -m "updated license" - dolt push test-remote main - - cd dolt-repo-clones/test-repo - run dolt pull - [[ "$output" =~ "Updating" ]] || false - run dolt log - [ "$status" -eq 0 ] - [[ "$output" =~ "updated license" ]] || false - dolt docs print LICENSE.md > LICENSE.md - run cat LICENSE.md - [ "$status" -eq 0 ] - [[ "$output" =~ "updated-license" ]] || false -} - -@test "remotes: push and pull tags to/from remote" { - dolt remote add test-remote http://localhost:50051/test-org/test-repo - dolt sql < refs/remotes/origin/main" ]] || false - [[ "$output" =~ "tip of your current branch is behind" ]] || false -} - @test "remotes: generate a merge with no conflict with a remote branch" { dolt remote add test-remote http://localhost:50051/test-org/test-repo dolt push test-remote main @@ -1051,43 +718,6 @@ SQL [[ ! "$output" =~ "another test commit" ]] || false } -@test "remotes: dolt_pull() with divergent head" { - dolt remote add test-remote http://localhost:50051/test-org/test-repo - dolt push test-remote main - dolt sql < and params." ]] || false -} - -@test "remotes: pushing empty branch does not panic" { - run dolt push origin '' - [ "$status" -eq 1 ] - [[ "$output" =~ "invalid ref spec: ''" ]] || false + [[ "$output" =~ "fatal: No configured push destination." ]] || false } @test "remotes: existing parent directory is not wiped when clone fails" { @@ -1850,23 +1297,6 @@ setup_ref_test() { dolt push } -@test "remotes: set upstream succeeds even if up to date" { - dolt remote add origin http://localhost:50051/test-org/test-repo - dolt push origin main - dolt checkout -b feature - dolt push --set-upstream origin feature - - cd dolt-repo-clones - dolt clone http://localhost:50051/test-org/test-repo - cd test-repo - dolt checkout -b feature - run dolt push - [ "$status" -eq 1 ] - [[ "$output" =~ "The current branch feature has no upstream branch." ]] || false - dolt push --set-upstream origin feature - dolt push -} - @test "remotes: local clone does not contain working set changes" { mkdir repo1 mkdir rem1 @@ -1888,35 +1318,6 @@ setup_ref_test() { [[ "$output" =~ "nothing to commit, working tree clean" ]] || false } -@test "remotes: local clone pushes to other branch" { - mkdir repo1 - mkdir rem1 - cd repo1 - dolt init - dolt sql -q "create table t (i int)" - run dolt status - [[ "$output" =~ "new table:" ]] || false - dolt commit -Am "new table" - dolt remote add rem1 file://../rem1 - dolt push rem1 main - cd .. - - dolt clone file://./rem1/ repo2 - cd repo2 - dolt checkout -b other - dolt sql -q "create table t2 (i int)" - dolt add . - dolt commit -am "adding table from other" - dolt remote add rem1 file://../rem1 - dolt push rem1 other - - cd ../repo1 - dolt fetch rem1 - dolt checkout other - run dolt log - [[ "$output" =~ "adding table from other" ]] || false -} - @test "remotes: dolt_remote uses the right db directory in a multidb env" { tempDir=$(mktemp -d) @@ -2496,41 +1897,3 @@ SQL [ "$status" -ne 0 ] [[ "$output" =~ "--prune option cannot be provided with a ref spec" ]] || false } - -@test "remotes: pull with DOLT_AUTHOR_DATE and DOLT_COMMITER_DATE doesn't overwrite commit timestamps" { - mkdir repo1 - - cd repo1 - dolt init - dolt sql -q "create table t1(a int)" - dolt commit -Am "new table" - dolt branch b1 - dolt remote add origin file://../remote1 - dolt push origin main - dolt push origin b1 - - cd .. - dolt clone file://./remote1 repo2 - - cd repo2 - TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt fetch - TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt pull - - run dolt_log_in_PST - [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false - - TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt checkout b1 - run dolt_log_in_PST - [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false - - cd ../repo1 - dolt checkout b1 - dolt commit --allow-empty -m 'empty commit' - dolt push origin b1 - - cd ../repo2 - TZ=PST+8 DOLT_COMMITTER_DATE='2023-09-26T12:34:56' DOLT_AUTHOR_DATE='2023-09-26T01:23:45' dolt pull - - run dolt_log_in_PST - [[ ! "$output" =~ 'Tue Sep 26 01:23:45' ]] || false -} diff --git a/integration-tests/bats/sql-pull.bats b/integration-tests/bats/sql-pull.bats index b1f18faad9..c0a4588e4c 100644 --- a/integration-tests/bats/sql-pull.bats +++ b/integration-tests/bats/sql-pull.bats @@ -105,7 +105,7 @@ teardown() { cd repo2 run dolt sql -q "call dolt_pull('unknown')" [ "$status" -eq 1 ] - [[ "$output" =~ "unknown remote" ]] || false + [[ "$output" =~ "fatal: remote 'unknown' not found" ]] || false [[ ! "$output" =~ "panic" ]] || false } diff --git a/integration-tests/bats/sql-push.bats b/integration-tests/bats/sql-push.bats index 693ece72ba..506d3fa034 100644 --- a/integration-tests/bats/sql-push.bats +++ b/integration-tests/bats/sql-push.bats @@ -48,19 +48,6 @@ teardown() { [[ "$output" =~ "t1" ]] || false } -@test "sql-push: CALL dolt_push origin" { - cd repo1 - dolt sql -q "CALL dolt_push('origin', 'main')" - - cd ../repo2 - dolt pull origin - run dolt sql -q "show tables" -r csv - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 2 ] - [[ "$output" =~ "Table" ]] || false - [[ "$output" =~ "t1" ]] || false -} - @test "sql-push: CALL dolt_push origin in stored procedure" { cd repo1 dolt sql <