Skip to content

Commit

Permalink
Merge pull request #6635 from dolthub/zachmu/replication-force
Browse files Browse the repository at this point in the history
Change default of replication pull to force
  • Loading branch information
zachmu authored Sep 12, 2023
2 parents e7ea090 + 004d5b9 commit 93aeb0c
Show file tree
Hide file tree
Showing 15 changed files with 520 additions and 269 deletions.
2 changes: 1 addition & 1 deletion go/cmd/dolt/cli/arg_parser_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -217,7 +217,7 @@ func CreatePullArgParser() *argparser.ArgParser {
ap.ArgListHelp = append(ap.ArgListHelp, [2]string{"remoteBranch", "The name of a branch on the specified remote to be merged into the current working set."})
ap.SupportsFlag(SquashParam, "", "Merge changes to the working set without updating the commit history")
ap.SupportsFlag(NoFFParam, "", "Create a merge commit even when the merge resolves as a fast-forward.")
ap.SupportsFlag(ForceFlag, "f", "Ignore any foreign key warnings and proceed with the commit.")
ap.SupportsFlag(ForceFlag, "f", "Update from the remote HEAD even if there are errors.")
ap.SupportsFlag(CommitFlag, "", "Perform the merge and commit the result. This is the default option, but can be overridden with the --no-commit flag. Note that this option does not affect fast-forward merges, which don't create a new merge commit, and if any merge conflicts or constraint violations are detected, no commit will be attempted.")
ap.SupportsFlag(NoCommitFlag, "", "Perform the merge and stop just before creating a merge commit. Note this will not prevent a fast-forward merge; use the --no-ff arg together with the --no-commit arg to prevent both fast-forwards and merge commits.")
ap.SupportsFlag(NoEditFlag, "", "Use an auto-generated commit message when creating a merge commit. The default for interactive CLI sessions is to open an editor.")
Expand Down
31 changes: 16 additions & 15 deletions go/cmd/dolt/commands/merge.go
Original file line number Diff line number Diff line change
Expand Up @@ -497,7 +497,7 @@ func validateMergeSpec(ctx context.Context, spec *merge.MergeSpec) errhand.Verbo
if _, err := merge.MayHaveConstraintViolations(ctx, ancRoot, mergedRoot); err != nil {
return errhand.VerboseErrorFromError(err)
}
if !spec.Noff {
if !spec.NoFF {
cli.Println("Fast-forward")
}
} else if err == doltdb.ErrUpToDate || err == doltdb.ErrIsAhead {
Expand Down Expand Up @@ -728,7 +728,7 @@ func performMerge(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryis
if ok, err := spec.HeadC.CanFastForwardTo(ctx, spec.MergeC); err != nil && !errors.Is(err, doltdb.ErrUpToDate) {
return nil, err
} else if ok {
if spec.Noff {
if spec.NoFF {
return executeNoFFMergeAndCommit(ctx, sqlCtx, queryist, dEnv, spec, suggestedMsg, cliCtx)
}
return nil, merge.ExecuteFFMerge(ctx, dEnv, spec)
Expand Down Expand Up @@ -763,18 +763,17 @@ func executeNoFFMergeAndCommit(ctx context.Context, sqlCtx *sql.Context, queryis
mergeParentCommits = []*doltdb.Commit{ws.MergeState().Commit()}
}

msg, err := getCommitMsgForMerge(ctx, sqlCtx, queryist, spec.Msg, suggestedMsg, spec.NoEdit, cliCtx)
msg, err := getCommitMsgForMerge(sqlCtx, queryist, suggestedMsg, spec.NoEdit, cliCtx)
if err != nil {
return tblToStats, err
}

pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws, mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
Message: msg,
Date: spec.Date,
AllowEmpty: spec.AllowEmpty,
Force: spec.Force,
Name: spec.Name,
Email: spec.Email,
Message: msg,
Date: spec.Date,
Force: spec.Force,
Name: spec.Name,
Email: spec.Email,
})

headRef, err := dEnv.RepoStateReader().CWBHeadRef()
Expand Down Expand Up @@ -816,7 +815,7 @@ func executeMergeAndCommit(ctx context.Context, sqlCtx *sql.Context, queryist cl
return tblToStats, nil
}

msg, err := getCommitMsgForMerge(ctx, sqlCtx, queryist, spec.Msg, suggestedMsg, spec.NoEdit, cliCtx)
msg, err := getCommitMsgForMerge(sqlCtx, queryist, suggestedMsg, spec.NoEdit, cliCtx)
if err != nil {
return tblToStats, err
}
Expand All @@ -832,11 +831,13 @@ func executeMergeAndCommit(ctx context.Context, sqlCtx *sql.Context, queryist cl
}

// getCommitMsgForMerge returns user defined message if exists; otherwise, get the commit message from editor.
func getCommitMsgForMerge(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryist, userDefinedMsg, suggestedMsg string, noEdit bool, cliCtx cli.CliContext) (string, error) {
if userDefinedMsg != "" {
return userDefinedMsg, nil
}

func getCommitMsgForMerge(
sqlCtx *sql.Context,
queryist cli.Queryist,
suggestedMsg string,
noEdit bool,
cliCtx cli.CliContext,
) (string, error) {
msg, err := getCommitMessageFromEditor(sqlCtx, queryist, suggestedMsg, "", noEdit, cliCtx)
if err != nil {
return msg, err
Expand Down
54 changes: 45 additions & 9 deletions go/cmd/dolt/commands/pull.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ package commands

import (
"context"
"errors"
"fmt"

"github.com/dolthub/go-mysql-server/sql"
Expand Down Expand Up @@ -107,7 +108,20 @@ func (cmd PullCmd) Exec(ctx context.Context, commandStr string, args []string, d
return HandleVErrAndExitCode(verr, usage)
}

pullSpec, err := env.NewPullSpec(ctx, dEnv.RepoStateReader(), remoteName, remoteRefName, apr.Contains(cli.SquashParam), apr.Contains(cli.NoFFParam), apr.Contains(cli.NoCommitFlag), apr.Contains(cli.NoEditFlag), apr.Contains(cli.ForceFlag), apr.NArg() == 1)
remoteOnly := apr.NArg() == 1
pullSpec, err := env.NewPullSpec(
ctx,
dEnv.RepoStateReader(),
remoteName,
remoteRefName,
remoteOnly,
env.WithSquash(apr.Contains(cli.SquashParam)),
env.WithNoFF(apr.Contains(cli.NoFFParam)),
env.WithNoCommit(apr.Contains(cli.NoCommitFlag)),
env.WithNoEdit(apr.Contains(cli.NoEditFlag)),
env.WithForce(apr.Contains(cli.ForceFlag)),
)

if err != nil {
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage)
}
Expand All @@ -120,7 +134,14 @@ func (cmd PullCmd) Exec(ctx context.Context, commandStr string, args []string, d
}

// pullHelper splits pull into fetch, prepare merge, and merge to interleave printing
func pullHelper(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryist, dEnv *env.DoltEnv, pullSpec *env.PullSpec, cliCtx cli.CliContext) error {
func pullHelper(
ctx context.Context,
sqlCtx *sql.Context,
queryist cli.Queryist,
dEnv *env.DoltEnv,
pullSpec *env.PullSpec,
cliCtx cli.CliContext,
) error {
srcDB, err := pullSpec.Remote.GetRemoteDBWithoutCaching(ctx, dEnv.DoltDB.ValueReadWriter().Format(), dEnv)
if err != nil {
return fmt.Errorf("failed to get remote db; %w", err)
Expand Down Expand Up @@ -160,8 +181,18 @@ func pullHelper(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryist,
}

err = dEnv.DoltDB.FastForward(ctx, remoteTrackRef, srcDBCommit)
if err != nil {
return fmt.Errorf("fetch failed; %w", err)
if errors.Is(err, datas.ErrMergeNeeded) {
// If the remote tracking branch has diverged from the local copy, we just overwrite it
h, err := srcDBCommit.HashOf()
if err != nil {
return err
}
err = dEnv.DoltDB.SetHead(ctx, remoteTrackRef, h)
if err != nil {
return err
}
} else if err != nil {
return fmt.Errorf("fetch failed: %w", err)
}

// Merge iff branch is current branch and there is an upstream set (pullSpec.Branch is set to nil if there is no upstream)
Expand All @@ -178,16 +209,16 @@ func pullHelper(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryist,

name, email, configErr := env.GetNameAndEmail(dEnv.Config)
// If the name and email aren't set we can set them to empty values for now. This is only valid for ff
// merges which detect for later.
// merges which we detect later.
if configErr != nil {
if pullSpec.Noff {
if pullSpec.NoFF {
return configErr
}
name, email = "", ""
}

// Begin merge
mergeSpec, err := merge.NewMergeSpec(ctx, dEnv.RepoStateReader(), dEnv.DoltDB, roots, name, email, pullSpec.Msg, remoteTrackRef.String(), pullSpec.Squash, pullSpec.Noff, pullSpec.Force, pullSpec.NoCommit, pullSpec.NoEdit, t)
// Begin merge of working and head with the remote head
mergeSpec, err := merge.NewMergeSpec(ctx, dEnv.RepoStateReader(), dEnv.DoltDB, roots, name, email, remoteTrackRef.String(), t, merge.WithPullSpecOpts(pullSpec))
if err != nil {
return err
}
Expand Down Expand Up @@ -217,7 +248,12 @@ func pullHelper(ctx context.Context, sqlCtx *sql.Context, queryist cli.Queryist,
return err
}

suggestedMsg := fmt.Sprintf("Merge branch '%s' of %s into %s", pullSpec.Branch.GetPath(), pullSpec.Remote.Url, headRef.GetPath())
suggestedMsg := fmt.Sprintf(
"Merge branch '%s' of %s into %s",
pullSpec.Branch.GetPath(),
pullSpec.Remote.Url,
headRef.GetPath(),
)
tblStats, err := performMerge(ctx, sqlCtx, queryist, dEnv, mergeSpec, suggestedMsg, cliCtx)
printSuccessStats(tblStats)
if err != nil {
Expand Down
4 changes: 0 additions & 4 deletions go/libraries/doltcore/doltdb/doltdb.go
Original file line number Diff line number Diff line change
Expand Up @@ -579,10 +579,6 @@ func (ddb *DoltDB) ReadCommit(ctx context.Context, h hash.Hash) (*Commit, error)

// Commit will update a branch's head value to be that of a previously committed root value hash
func (ddb *DoltDB) Commit(ctx context.Context, valHash hash.Hash, dref ref.DoltRef, cm *datas.CommitMeta) (*Commit, error) {
if dref.GetType() != ref.BranchRefType {
panic("can't commit to ref that isn't branch atm. will probably remove this.")
}

return ddb.CommitWithParentSpecs(ctx, valHash, dref, nil, cm)
}

Expand Down
61 changes: 49 additions & 12 deletions go/libraries/doltcore/env/remotes.go
Original file line number Diff line number Diff line change
Expand Up @@ -391,9 +391,8 @@ func GetTrackingRef(branchRef ref.DoltRef, remote Remote) (ref.DoltRef, error) {
}

type PullSpec struct {
Msg string
Squash bool
Noff bool
NoFF bool
NoCommit bool
NoEdit bool
Force bool
Expand All @@ -403,10 +402,47 @@ type PullSpec struct {
Branch ref.DoltRef
}

// NewPullSpec returns PullSpec object using arguments passed into this function, which are remoteName, remoteRefName,
// squash, noff, noCommit, noEdit, refSpecs, force and remoteOnly. This function validates remote and gets remoteRef
type PullSpecOpt func(*PullSpec)

func WithSquash(squash bool) PullSpecOpt {
return func(ps *PullSpec) {
ps.Squash = squash
}
}

func WithNoFF(noff bool) PullSpecOpt {
return func(ps *PullSpec) {
ps.NoFF = noff
}
}

func WithNoCommit(nocommit bool) PullSpecOpt {
return func(ps *PullSpec) {
ps.NoCommit = nocommit
}
}

func WithNoEdit(noedit bool) PullSpecOpt {
return func(ps *PullSpec) {
ps.NoEdit = noedit
}
}

func WithForce(force bool) PullSpecOpt {
return func(ps *PullSpec) {
ps.Force = force
}
}

// NewPullSpec returns a PullSpec for the arguments given. This function validates remote and gets remoteRef
// for given remoteRefName; if it's not defined, it uses current branch to get its upstream branch if it exists.
func NewPullSpec(_ context.Context, rsr RepoStateReader, remoteName, remoteRefName string, squash, noff, noCommit, noEdit, force, remoteOnly bool) (*PullSpec, error) {
func NewPullSpec(
_ context.Context,
rsr RepoStateReader,
remoteName, remoteRefName string,
remoteOnly bool,
opts ...PullSpecOpt,
) (*PullSpec, error) {
refSpecs, err := GetRefSpecs(rsr, remoteName)
if err != nil {
return nil, err
Expand Down Expand Up @@ -446,17 +482,18 @@ func NewPullSpec(_ context.Context, rsr RepoStateReader, remoteName, remoteRefNa
remoteRef = ref.NewBranchRef(remoteRefName)
}

return &PullSpec{
Squash: squash,
Noff: noff,
NoCommit: noCommit,
NoEdit: noEdit,
spec := &PullSpec{
RemoteName: remoteName,
Remote: remote,
RefSpecs: refSpecs,
Branch: remoteRef,
Force: force,
}, nil
}

for _, opt := range opts {
opt(spec)
}

return spec, nil
}

func GetAbsRemoteUrl(fs filesys2.Filesys, cfg config.ReadableConfig, urlArg string) (string, string, error) {
Expand Down
79 changes: 61 additions & 18 deletions go/libraries/doltcore/merge/action.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,7 @@ import (
"github.com/dolthub/dolt/go/store/hash"
)

var ErrFailedToDetermineUnstagedDocs = errors.New("failed to determine unstaged docs")
var ErrFailedToReadDatabase = errors.New("failed to read database")
var ErrMergeFailedToUpdateDocs = errors.New("failed to update docs to the new working root")
var ErrMergeFailedToUpdateRepoState = errors.New("unable to execute repo state update")
var ErrFailedToDetermineMergeability = errors.New("failed to determine mergeability")

type MergeSpec struct {
Expand All @@ -43,21 +40,67 @@ type MergeSpec struct {
StompedTblNames []string
WorkingDiffs map[string]hash.Hash
Squash bool
Msg string
Noff bool
NoFF bool
NoCommit bool
NoEdit bool
Force bool
AllowEmpty bool
Email string
Name string
Date time.Time
}

// NewMergeSpec returns MergeSpec object using arguments passed into this function, which are doltdb.Roots, username,
// user email, commit msg, commitSpecStr, to squash, to noff, to force, noCommit, noEdit and date. This function
// resolves head and merge commit, and it gets current diffs between current head and working set if it exists.
func NewMergeSpec(ctx context.Context, rsr env.RepoStateReader, ddb *doltdb.DoltDB, roots doltdb.Roots, name, email, msg, commitSpecStr string, squash, noff, force, noCommit, noEdit bool, date time.Time) (*MergeSpec, error) {
type MergeSpecOpt func(*MergeSpec)

func WithNoFF(noFF bool) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.NoFF = noFF
}
}

func WithNoCommit(noCommit bool) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.NoCommit = noCommit
}
}

func WithNoEdit(noEdit bool) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.NoEdit = noEdit
}
}

func WithForce(force bool) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.Force = force
}
}

func WithSquash(squash bool) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.Squash = squash
}
}

func WithPullSpecOpts(pullSpec *env.PullSpec) MergeSpecOpt {
return func(ms *MergeSpec) {
ms.NoEdit = pullSpec.NoEdit
ms.NoCommit = pullSpec.NoCommit
ms.Force = pullSpec.Force
ms.NoFF = pullSpec.NoFF
ms.Squash = pullSpec.Squash
}
}

// NewMergeSpec returns a MergeSpec with the arguments provided.
func NewMergeSpec(
ctx context.Context,
rsr env.RepoStateReader,
ddb *doltdb.DoltDB,
roots doltdb.Roots,
name, email, commitSpecStr string,
date time.Time,
opts ...MergeSpecOpt,
) (*MergeSpec, error) {
headCS, err := doltdb.NewCommitSpec("HEAD")
if err != nil {
return nil, err
Expand Down Expand Up @@ -99,24 +142,24 @@ func NewMergeSpec(ctx context.Context, rsr env.RepoStateReader, ddb *doltdb.Dolt
return nil, fmt.Errorf("%w; %s", ErrFailedToDetermineMergeability, err.Error())
}

return &MergeSpec{
spec := &MergeSpec{
HeadH: headH,
MergeH: mergeH,
HeadC: headCM,
MergeCSpecStr: commitSpecStr,
MergeC: mergeCM,
StompedTblNames: stompedTblNames,
WorkingDiffs: workingDiffs,
Squash: squash,
Msg: msg,
Noff: noff,
NoCommit: noCommit,
NoEdit: noEdit,
Force: force,
Email: email,
Name: name,
Date: date,
}, nil
}

for _, opt := range opts {
opt(spec)
}

return spec, nil
}

func ExecNoFFMerge(ctx context.Context, dEnv *env.DoltEnv, spec *MergeSpec) (map[string]*MergeStats, error) {
Expand Down
Loading

0 comments on commit 93aeb0c

Please sign in to comment.