diff --git a/go/cmd/dolt/commands/branch.go b/go/cmd/dolt/commands/branch.go index a63f6d99a6e..d7eb909445b 100644 --- a/go/cmd/dolt/commands/branch.go +++ b/go/cmd/dolt/commands/branch.go @@ -138,7 +138,10 @@ func printBranches(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPar return HandleVErrAndExitCode(errhand.BuildDError("error: failed to read refs from db").AddCause(err).Build(), nil) } - currentBranch := dEnv.RepoStateReader().CWBHeadRef() + currentBranch, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return HandleVErrAndExitCode(errhand.BuildDError("error: failed to read refs from db").AddCause(err).Build(), nil) + } sort.Slice(branches, func(i, j int) bool { return branches[i].String() < branches[j].String() }) @@ -172,7 +175,7 @@ func printBranches(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPar } if verbose { - cm, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + cm, err := dEnv.DoltDB.Resolve(ctx, cs, currentBranch) if err == nil { h, err := cm.HashOf() @@ -195,7 +198,11 @@ func printBranches(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPar } func printCurrentBranch(dEnv *env.DoltEnv) int { - cli.Println(dEnv.RepoStateReader().CWBHeadRef().GetPath()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return HandleVErrAndExitCode(errhand.BuildDError(err.Error()).Build(), nil) + } + cli.Println(headRef.GetPath()) return 0 } diff --git a/go/cmd/dolt/commands/checkout.go b/go/cmd/dolt/commands/checkout.go index 5badb3d5e7f..d52e95e2d2e 100644 --- a/go/cmd/dolt/commands/checkout.go +++ b/go/cmd/dolt/commands/checkout.go @@ -119,7 +119,10 @@ func (cmd CheckoutCmd) Exec(ctx context.Context, commandStr string, args []strin if err != nil { return HandleVErrAndExitCode(errhand.BuildDError(err.Error()).Build(), usagePrt) } - headRef := dEnv.RepoStateReader().CWBHeadRef() + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return HandleVErrAndExitCode(errhand.BuildDError(err.Error()).Build(), nil) + } ws, err := dEnv.WorkingSet(ctx) if err != nil { HandleVErrAndExitCode(errhand.BuildDError(err.Error()).Build(), usagePrt) @@ -177,9 +180,14 @@ func checkoutNewBranch(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.Ar return verr } + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return errhand.BuildDError(err.Error()).Build() + } + // the new branch is checked out at this point if setTrackUpstream { - verr = SetRemoteUpstreamForBranchRef(dEnv, remoteName, remoteBranchName, dEnv.RepoStateReader().CWBHeadRef()) + verr = SetRemoteUpstreamForBranchRef(dEnv, remoteName, remoteBranchName, headRef) if verr != nil { return verr } @@ -195,7 +203,7 @@ func checkoutNewBranch(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.Ar if !remoteOk { return nil } - verr = SetRemoteUpstreamForBranchRef(dEnv, remoteName, remoteBranchName, dEnv.RepoStateReader().CWBHeadRef()) + verr = SetRemoteUpstreamForBranchRef(dEnv, remoteName, remoteBranchName, headRef) if verr != nil { return verr } @@ -230,7 +238,11 @@ func checkoutRemoteBranchOrSuggestNew(ctx context.Context, dEnv *env.DoltEnv, na if verr != nil { return verr } - return SetRemoteUpstreamForBranchRef(dEnv, remoteRefs[0].GetRemote(), remoteRefs[0].GetBranch(), dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return errhand.BuildDError(err.Error()).Build() + } + return SetRemoteUpstreamForBranchRef(dEnv, remoteRefs[0].GetRemote(), remoteRefs[0].GetBranch(), headRef) } else { // TODO : add hint of using `dolt checkout --track /` when --track flag is supported return errhand.BuildDError("'%s' matched multiple (%v) remote tracking branches", name, len(remoteRefs)).Build() diff --git a/go/cmd/dolt/commands/cherry-pick.go b/go/cmd/dolt/commands/cherry-pick.go index 82ba75dcd5e..75ae000b7f1 100644 --- a/go/cmd/dolt/commands/cherry-pick.go +++ b/go/cmd/dolt/commands/cherry-pick.go @@ -183,7 +183,11 @@ func getCherryPickedRootValue(ctx context.Context, dEnv *env.DoltEnv, workingRoo if err != nil { return nil, "", err } - cherryCm, err := dEnv.DoltDB.Resolve(ctx, cherrySpec, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil, "", err + } + cherryCm, err := dEnv.DoltDB.Resolve(ctx, cherrySpec, headRef) if err != nil { return nil, "", err } diff --git a/go/cmd/dolt/commands/commit.go b/go/cmd/dolt/commands/commit.go index 40c197c1206..7cf9cf1ae91 100644 --- a/go/cmd/dolt/commands/commit.go +++ b/go/cmd/dolt/commands/commit.go @@ -221,9 +221,13 @@ func performCommit(ctx context.Context, commandStr string, args []string, dEnv * return handleCommitErr(ctx, dEnv, err, usage) } + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return handleCommitErr(ctx, dEnv, err, usage) + } _, err = dEnv.DoltDB.CommitWithWorkingSet( ctx, - dEnv.RepoStateReader().CWBHeadRef(), + headRef, ws.Ref(), pendingCommit, ws.WithStagedRoot(pendingCommit.Roots.Staged).WithWorkingRoot(pendingCommit.Roots.Working).ClearMerge(), @@ -381,7 +385,10 @@ func buildInitalCommitMsg(ctx context.Context, dEnv *env.DoltEnv, suggestedMsg s return "", err } - currBranch := dEnv.RepoStateReader().CWBHeadRef() + currBranch, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return "", err + } initialCommitMessage := fmt.Sprintf("%s\n# Please enter the commit message for your changes. Lines starting"+ "\n# with '#' will be ignored, and an empty message aborts the commit."+ "\n# On branch %s\n#\n", suggestedMsg, currBranch) diff --git a/go/cmd/dolt/commands/filter-branch.go b/go/cmd/dolt/commands/filter-branch.go index 039c3b3f070..931918f6171 100644 --- a/go/cmd/dolt/commands/filter-branch.go +++ b/go/cmd/dolt/commands/filter-branch.go @@ -186,7 +186,12 @@ func getNerf(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgParseResu return nil, err } - cm, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil, err + } + + cm, err := dEnv.DoltDB.Resolve(ctx, cs, headRef) if err != nil { return nil, err } diff --git a/go/cmd/dolt/commands/log.go b/go/cmd/dolt/commands/log.go index 0c547913049..e590e976993 100644 --- a/go/cmd/dolt/commands/log.go +++ b/go/cmd/dolt/commands/log.go @@ -123,7 +123,11 @@ func (cmd LogCmd) logWithLoggerFunc(ctx context.Context, commandStr string, args return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) } if len(opts.commitSpecs) == 0 { - opts.commitSpecs = append(opts.commitSpecs, dEnv.RepoStateReader().CWBHeadSpec()) + headRef, err := dEnv.RepoStateReader().CWBHeadSpec() + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + opts.commitSpecs = append(opts.commitSpecs, headRef) } if len(opts.tableName) > 0 { return handleErrAndExit(logTableCommits(ctx, dEnv, opts)) @@ -245,7 +249,10 @@ func (opts *logOpts) parseRefsAndTable(ctx context.Context, apr *argparser.ArgPa opts.excludingCommitSpecs = append(opts.excludingCommitSpecs, notCs) } else { - argIsRef := actions.IsValidRef(ctx, arg, dEnv.DoltDB, dEnv.RepoStateReader()) + argIsRef, err := actions.IsValidRef(ctx, arg, dEnv.DoltDB, dEnv.RepoStateReader()) + if err != nil { + return nil + } // if argIsRef && !seenRefs[arg] { cs, err := getCommitSpec(arg) @@ -327,8 +334,12 @@ func getHashToRefs(ctx context.Context, dEnv *env.DoltEnv, decorationLevel strin func logCommits(ctx context.Context, dEnv *env.DoltEnv, opts *logOpts) int { hashes := make([]hash.Hash, len(opts.commitSpecs)) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return handleErrAndExit(err) + } for i, cs := range opts.commitSpecs { - commit, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + commit, err := dEnv.DoltDB.Resolve(ctx, cs, headRef) if err != nil { cli.PrintErrln(color.HiRedString("Fatal error: cannot get HEAD commit for current branch.")) return 1 @@ -360,7 +371,7 @@ func logCommits(ctx context.Context, dEnv *env.DoltEnv, opts *logOpts) int { excludingHashes := make([]hash.Hash, len(opts.excludingCommitSpecs)) for i, excludingSpec := range opts.excludingCommitSpecs { - excludingCommit, err := dEnv.DoltDB.Resolve(ctx, excludingSpec, dEnv.RepoStateReader().CWBHeadRef()) + excludingCommit, err := dEnv.DoltDB.Resolve(ctx, excludingSpec, headRef) if err != nil { cli.PrintErrln(color.HiRedString("Fatal error: cannot get excluding commit for current branch.")) return 1 @@ -383,7 +394,6 @@ func logCommits(ctx context.Context, dEnv *env.DoltEnv, opts *logOpts) int { return 1 } - headRef := dEnv.RepoStateReader().CWBHeadRef() cwbHash, err := dEnv.DoltDB.GetHashForRefStr(ctx, headRef.String()) if err != nil { @@ -441,8 +451,13 @@ func tableExists(ctx context.Context, commit *doltdb.Commit, tableName string) ( func logTableCommits(ctx context.Context, dEnv *env.DoltEnv, opts *logOpts) error { hashes := make([]hash.Hash, len(opts.commitSpecs)) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + for i, cs := range opts.commitSpecs { - commit, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + commit, err := dEnv.DoltDB.Resolve(ctx, cs, headRef) if err != nil { return err } diff --git a/go/cmd/dolt/commands/ls.go b/go/cmd/dolt/commands/ls.go index 243a671acac..8ee63aaccb2 100644 --- a/go/cmd/dolt/commands/ls.go +++ b/go/cmd/dolt/commands/ls.go @@ -121,7 +121,12 @@ func getRootForCommitSpecStr(ctx context.Context, csStr string, dEnv *env.DoltEn return "", nil, bdr.AddCause(err).Build() } - cm, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return "", nil, errhand.VerboseErrorFromError(err) + } + + cm, err := dEnv.DoltDB.Resolve(ctx, cs, headRef) if err != nil { return "", nil, errhand.BuildDError(`Unable to resolve "%s"`, csStr).AddCause(err).Build() diff --git a/go/cmd/dolt/commands/merge.go b/go/cmd/dolt/commands/merge.go index b9751f2fd51..93651de708a 100644 --- a/go/cmd/dolt/commands/merge.go +++ b/go/cmd/dolt/commands/merge.go @@ -159,7 +159,12 @@ func (cmd MergeCmd) Exec(ctx context.Context, commandStr string, args []string, return handleCommitErr(ctx, dEnv, err, usage) } - suggestedMsg := fmt.Sprintf("Merge branch '%s' into %s", commitSpecStr, dEnv.RepoStateReader().CWBHeadRef().GetPath()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return handleCommitErr(ctx, dEnv, err, usage) + } + + suggestedMsg := fmt.Sprintf("Merge branch '%s' into %s", commitSpecStr, headRef.GetPath()) msg := "" if m, ok := apr.GetValue(cli.MessageArg); ok { msg = m @@ -529,10 +534,15 @@ func executeNoFFMergeAndCommit(ctx context.Context, dEnv *env.DoltEnv, spec *mer Email: spec.Email, }) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return tblToStats, err + } + wsHash, err := ws.HashOf() _, err = dEnv.DoltDB.CommitWithWorkingSet( ctx, - dEnv.RepoStateReader().CWBHeadRef(), + headRef, ws.Ref(), pendingCommit, ws.WithStagedRoot(pendingCommit.Roots.Staged).WithWorkingRoot(pendingCommit.Roots.Working).ClearMerge(), diff --git a/go/cmd/dolt/commands/merge_base.go b/go/cmd/dolt/commands/merge_base.go index d2612d61d29..c8608449720 100644 --- a/go/cmd/dolt/commands/merge_base.go +++ b/go/cmd/dolt/commands/merge_base.go @@ -119,7 +119,12 @@ func ResolveCommitWithVErr(dEnv *env.DoltEnv, cSpecStr string) (*doltdb.Commit, return nil, errhand.BuildDError("'%s' is not a valid commit", cSpecStr).Build() } - cm, err := dEnv.DoltDB.Resolve(context.TODO(), cs, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil, errhand.VerboseErrorFromError(err) + } + + cm, err := dEnv.DoltDB.Resolve(context.TODO(), cs, headRef) if err != nil { if errors.Is(err, doltdb.ErrInvalidAncestorSpec) { return nil, errhand.BuildDError("'%s' could not resolve ancestor spec", cSpecStr).Build() diff --git a/go/cmd/dolt/commands/pull.go b/go/cmd/dolt/commands/pull.go index d4b4ff90898..0841b246a85 100644 --- a/go/cmd/dolt/commands/pull.go +++ b/go/cmd/dolt/commands/pull.go @@ -202,7 +202,12 @@ func pullHelper(ctx context.Context, dEnv *env.DoltEnv, pullSpec *env.PullSpec) return err } - suggestedMsg := fmt.Sprintf("Merge branch '%s' of %s into %s", pullSpec.Branch.GetPath(), pullSpec.Remote.Url, dEnv.RepoStateReader().CWBHeadRef().GetPath()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + + suggestedMsg := fmt.Sprintf("Merge branch '%s' of %s into %s", pullSpec.Branch.GetPath(), pullSpec.Remote.Url, headRef.GetPath()) tblStats, err := performMerge(ctx, dEnv, mergeSpec, suggestedMsg) printSuccessStats(tblStats) if err != nil { diff --git a/go/cmd/dolt/commands/push.go b/go/cmd/dolt/commands/push.go index c39650a4517..7fa06517fb9 100644 --- a/go/cmd/dolt/commands/push.go +++ b/go/cmd/dolt/commands/push.go @@ -96,16 +96,20 @@ func (cmd PushCmd) Exec(ctx context.Context, commandStr string, args []string, d var verr errhand.VerboseError switch err { case env.ErrNoUpstreamForBranch: - currentBranch := dEnv.RepoStateReader().CWBHeadRef() - remoteName := "" - if defRemote, verr := env.GetDefaultRemote(dEnv.RepoStateReader()); verr == nil { - remoteName = defRemote.Name + currentBranch, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + verr = errhand.BuildDError("fatal: The current branch could not be identified").AddCause(err).Build() + } else { + remoteName := "" + if defRemote, verr := env.GetDefaultRemote(dEnv.RepoStateReader()); verr == nil { + remoteName = defRemote.Name + } + verr = errhand.BuildDError("fatal: The current branch " + currentBranch.GetPath() + " has no upstream branch.\n" + + "To push the current branch and set the remote as upstream, use\n" + + "\tdolt push --set-upstream " + remoteName + " " + currentBranch.GetPath() + "\n" + + "To have this happen automatically for branches without a tracking\n" + + "upstream, see 'push.autoSetupRemote' in 'dolt config --help'.").Build() } - verr = errhand.BuildDError("fatal: The current branch " + currentBranch.GetPath() + " has no upstream branch.\n" + - "To push the current branch and set the remote as upstream, use\n" + - "\tdolt push --set-upstream " + remoteName + " " + currentBranch.GetPath() + "\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() diff --git a/go/cmd/dolt/commands/reset.go b/go/cmd/dolt/commands/reset.go index f074870ac4b..eae57e8acb4 100644 --- a/go/cmd/dolt/commands/reset.go +++ b/go/cmd/dolt/commands/reset.go @@ -103,7 +103,11 @@ func (cmd ResetCmd) Exec(ctx context.Context, commandStr string, args []string, } else { if apr.NArg() == 1 { ref := apr.Arg(0) - if actions.IsValidRef(ctx, ref, dEnv.DoltDB, dEnv.RepoStateReader()) { + isValidRef, err := actions.IsValidRef(ctx, ref, dEnv.DoltDB, dEnv.RepoStateReader()) + if err != nil { + return handleErrAndExit(err) + } + if isValidRef { return handleResetSoftToRef(ctx, dEnv, ref, usage) } } @@ -145,7 +149,10 @@ func handleResetHard(ctx context.Context, apr *argparser.ArgParseResults, usage arg = apr.Arg(0) } - headRef := dEnv.RepoStateReader().CWBHeadRef() + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } ws, err := dEnv.WorkingSet(ctx) if err != nil { return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) diff --git a/go/cmd/dolt/commands/show.go b/go/cmd/dolt/commands/show.go index c926593933b..026889a0995 100644 --- a/go/cmd/dolt/commands/show.go +++ b/go/cmd/dolt/commands/show.go @@ -159,7 +159,11 @@ func parseShowArgs(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPar func showObjects(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts) error { if len(opts.specRefs) == 0 { - return showCommitSpec(ctx, dEnv, opts, dEnv.RepoStateReader().CWBHeadSpec()) + headRef, err := dEnv.RepoStateReader().CWBHeadSpec() + if err != nil { + return err + } + return showCommitSpec(ctx, dEnv, opts, headRef) } for _, specRef := range opts.specRefs { @@ -243,7 +247,12 @@ func showSpecRef(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, specRef func showCommitSpec(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, commitSpec *doltdb.CommitSpec) error { - commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + + commit, err := dEnv.DoltDB.Resolve(ctx, commitSpec, headRef) if err != nil { return err } @@ -283,7 +292,10 @@ func showCommit(ctx context.Context, dEnv *env.DoltEnv, opts *showOpts, comm *do return err } - headRef := dEnv.RepoStateReader().CWBHeadRef() + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } cwbHash, err := dEnv.DoltDB.GetHashForRefStr(ctx, headRef.String()) if err != nil { return err diff --git a/go/cmd/dolt/commands/stashcmds/pop.go b/go/cmd/dolt/commands/stashcmds/pop.go index 6d273bf690d..a75c5d6b766 100644 --- a/go/cmd/dolt/commands/stashcmds/pop.go +++ b/go/cmd/dolt/commands/stashcmds/pop.go @@ -133,7 +133,11 @@ func applyStashAtIdx(ctx context.Context, dEnv *env.DoltEnv, curWorkingRoot *dol if err != nil { return false, err } - parentCommit, err := dEnv.DoltDB.Resolve(ctx, headCommitSpec, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return false, err + } + parentCommit, err := dEnv.DoltDB.Resolve(ctx, headCommitSpec, headRef) if err != nil { return false, err } diff --git a/go/cmd/dolt/commands/stashcmds/stash.go b/go/cmd/dolt/commands/stashcmds/stash.go index fff56f3c25d..39499b4fc06 100644 --- a/go/cmd/dolt/commands/stashcmds/stash.go +++ b/go/cmd/dolt/commands/stashcmds/stash.go @@ -224,7 +224,10 @@ func stashChanges(ctx context.Context, dEnv *env.DoltEnv, apr *argparser.ArgPars } } - curHeadRef := dEnv.RepoStateReader().CWBHeadRef() + curHeadRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } curBranchName := curHeadRef.String() commitSpec, err := doltdb.NewCommitSpec(curBranchName) if err != nil { diff --git a/go/cmd/dolt/commands/status.go b/go/cmd/dolt/commands/status.go index 02cf6488777..14040bb4dc2 100644 --- a/go/cmd/dolt/commands/status.go +++ b/go/cmd/dolt/commands/status.go @@ -95,9 +95,14 @@ func (cmd StatusCmd) Exec(ctx context.Context, commandStr string, args []string, } func PrintStatus(ctx context.Context, dEnv *env.DoltEnv, stagedTbls, notStagedTbls []diff.TableDelta, showIgnoredTables bool, as merge.ArtifactStatus) error { - cli.Printf(branchHeader, dEnv.RepoStateReader().CWBHeadRef().GetPath()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + + cli.Printf(branchHeader, headRef.GetPath()) - err := printRemoteRefTrackingInfo(ctx, dEnv) + err = printRemoteRefTrackingInfo(ctx, dEnv) if err != nil { return err } @@ -141,7 +146,10 @@ func handleStatusVErr(err error) int { func printRemoteRefTrackingInfo(ctx context.Context, dEnv *env.DoltEnv) error { ddb := dEnv.DoltDB rsr := dEnv.RepoStateReader() - headRef := rsr.CWBHeadRef() + headRef, err := rsr.CWBHeadRef() + if err != nil { + return err + } branches, err := rsr.GetBranches() if err != nil { return err diff --git a/go/libraries/doltcore/diff/diffsplitter.go b/go/libraries/doltcore/diff/diffsplitter.go index 65b162aab07..2bcecdb80fe 100644 --- a/go/libraries/doltcore/diff/diffsplitter.go +++ b/go/libraries/doltcore/diff/diffsplitter.go @@ -219,7 +219,11 @@ func MaybeResolveRoot(ctx context.Context, rsr env.RepoStateReader, doltDB *dolt return nil, false } - cm, err := doltDB.Resolve(ctx, cs, rsr.CWBHeadRef()) + headRef, err := rsr.CWBHeadRef() + if err != nil { + return nil, false + } + cm, err := doltDB.Resolve(ctx, cs, headRef) if err != nil { return nil, false } diff --git a/go/libraries/doltcore/doltdb/errors.go b/go/libraries/doltcore/doltdb/errors.go index 418af7789ea..c09f42b118c 100644 --- a/go/libraries/doltcore/doltdb/errors.go +++ b/go/libraries/doltcore/doltdb/errors.go @@ -29,7 +29,6 @@ var ErrInvalidBranchOrHash = errors.New("string is not a valid branch or hash") var ErrInvalidHash = errors.New("string is not a valid hash") var ErrFoundHashNotACommit = errors.New("the value retrieved for this hash is not a commit") - var ErrHashNotFound = errors.New("could not find a value for this hash") var ErrBranchNotFound = errors.New("branch not found") var ErrTagNotFound = errors.New("tag not found") @@ -49,6 +48,8 @@ var ErrIsBehind = errors.New("cannot reverse from b to a. b is a is behind a alr var ErrUnresolvedConflictsOrViolations = errors.New("merge has unresolved conflicts or constraint violations") var ErrMergeActive = errors.New("merging is not possible because you have not committed an active merge") +var ErrOperationNotSupportedInDetachedHead = errors.New("this operation is not supported while in a detached head state") + type ErrClientOutOfDate struct { RepoVer FeatureVersion ClientVer FeatureVersion diff --git a/go/libraries/doltcore/dtestutils/testcommands/multienv.go b/go/libraries/doltcore/dtestutils/testcommands/multienv.go index f669255be3b..9cca2ecaa82 100644 --- a/go/libraries/doltcore/dtestutils/testcommands/multienv.go +++ b/go/libraries/doltcore/dtestutils/testcommands/multienv.go @@ -260,9 +260,14 @@ func (mr *MultiRepoTestSetup) CommitWithWorkingSet(dbName string) *doltdb.Commit panic("pending commit error: " + err.Error()) } + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + panic("couldn't get working set: " + err.Error()) + } + commit, err := dEnv.DoltDB.CommitWithWorkingSet( ctx, - dEnv.RepoStateReader().CWBHeadRef(), + headRef, ws.Ref(), pendingCommit, ws.WithStagedRoot(pendingCommit.Roots.Staged).WithWorkingRoot(pendingCommit.Roots.Working).ClearMerge(), diff --git a/go/libraries/doltcore/env/actions/branch.go b/go/libraries/doltcore/env/actions/branch.go index 0c182cce86b..4a2cb8afa93 100644 --- a/go/libraries/doltcore/env/actions/branch.go +++ b/go/libraries/doltcore/env/actions/branch.go @@ -42,7 +42,11 @@ func RenameBranch(ctx context.Context, dbData env.DbData, oldBranch, newBranch s return err } - if ref.Equals(dbData.Rsr.CWBHeadRef(), oldRef) { + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return err + } + if ref.Equals(headRef, oldRef) { err = dbData.Rsw.SetCWBHeadRef(ctx, ref.MarshalableRef{Ref: newRef}) if err != nil { return err @@ -124,7 +128,11 @@ func DeleteBranch(ctx context.Context, dbData env.DbData, brName string, opts De } } else { branchRef = ref.NewBranchRef(brName) - if ref.Equals(dbData.Rsr.CWBHeadRef(), branchRef) { + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return err + } + if ref.Equals(headRef, branchRef) { return ErrCOBranchDelete } } @@ -195,7 +203,11 @@ func validateBranchMergedIntoCurrentWorkingBranch(ctx context.Context, dbdata en return err } - cwbHead, err := dbdata.Ddb.Resolve(ctx, cwbCs, dbdata.Rsr.CWBHeadRef()) + headRef, err := dbdata.Rsr.CWBHeadRef() + if err != nil { + return err + } + cwbHead, err := dbdata.Ddb.Resolve(ctx, cwbCs, headRef) if err != nil { return err } @@ -325,7 +337,11 @@ func CreateBranchOnDB(ctx context.Context, ddb *doltdb.DoltDB, newBranch, starti } func createBranch(ctx context.Context, dbData env.DbData, newBranch, startingPoint string, force bool, rsc *doltdb.ReplicationStatusController) error { - return CreateBranchOnDB(ctx, dbData.Ddb, newBranch, startingPoint, force, dbData.Rsr.CWBHeadRef(), rsc) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return err + } + return CreateBranchOnDB(ctx, dbData.Ddb, newBranch, startingPoint, force, headRef, rsc) } var emptyHash = hash.Hash{} @@ -343,7 +359,11 @@ func MaybeGetCommit(ctx context.Context, dEnv *env.DoltEnv, str string) (*doltdb cs, err := doltdb.NewCommitSpec(str) if err == nil { - cm, err := dEnv.DoltDB.Resolve(ctx, cs, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil, err + } + cm, err := dEnv.DoltDB.Resolve(ctx, cs, headRef) if errors.Is(err, doltdb.ErrBranchNotFound) { return nil, nil diff --git a/go/libraries/doltcore/env/actions/checkout.go b/go/libraries/doltcore/env/actions/checkout.go index 9f8a8bc26eb..4234bf92579 100644 --- a/go/libraries/doltcore/env/actions/checkout.go +++ b/go/libraries/doltcore/env/actions/checkout.go @@ -159,7 +159,10 @@ func rootsForBranch(ctx context.Context, roots doltdb.Roots, branchRoot *doltdb. func CheckoutBranch(ctx context.Context, dEnv *env.DoltEnv, brName string, force bool) error { branchRef := ref.NewBranchRef(brName) - initialHeadRef := dEnv.RepoStateReader().CWBHeadRef() + initialHeadRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } db := dEnv.DoltDB hasRef, err := db.HasRef(ctx, branchRef) @@ -170,7 +173,11 @@ func CheckoutBranch(ctx context.Context, dEnv *env.DoltEnv, brName string, force return doltdb.ErrBranchNotFound } - if ref.Equals(dEnv.RepoStateReader().CWBHeadRef(), branchRef) { + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + if ref.Equals(headRef, branchRef) { return doltdb.ErrAlreadyOnBranch } diff --git a/go/libraries/doltcore/env/actions/remotes.go b/go/libraries/doltcore/env/actions/remotes.go index 179e8efeef6..6200cba599f 100644 --- a/go/libraries/doltcore/env/actions/remotes.go +++ b/go/libraries/doltcore/env/actions/remotes.go @@ -168,7 +168,11 @@ func PushToRemoteBranch(ctx context.Context, rsr env.RepoStateReader, tempTableD } cs, _ := doltdb.NewCommitSpec(srcRef.GetPath()) - cm, err := localDB.Resolve(ctx, cs, rsr.CWBHeadRef()) + headRef, err := rsr.CWBHeadRef() + if err != nil { + 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/actions/reset.go b/go/libraries/doltcore/env/actions/reset.go index 62b8903b0e6..34604dad185 100644 --- a/go/libraries/doltcore/env/actions/reset.go +++ b/go/libraries/doltcore/env/actions/reset.go @@ -39,7 +39,11 @@ func resetHardTables(ctx context.Context, dbData env.DbData, cSpecStr string, ro return nil, doltdb.Roots{}, err } - newHead, err = ddb.Resolve(ctx, cs, rsr.CWBHeadRef()) + headRef, err := rsr.CWBHeadRef() + if err != nil { + return nil, doltdb.Roots{}, err + } + newHead, err = ddb.Resolve(ctx, cs, headRef) if err != nil { return nil, doltdb.Roots{}, err } @@ -220,7 +224,11 @@ func ResetSoftToRef(ctx context.Context, dbData env.DbData, cSpecStr string) (do return doltdb.Roots{}, err } - newHead, err := dbData.Ddb.Resolve(ctx, cs, dbData.Rsr.CWBHeadRef()) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return doltdb.Roots{}, err + } + newHead, err := dbData.Ddb.Resolve(ctx, cs, headRef) if err != nil { return doltdb.Roots{}, err } @@ -231,7 +239,7 @@ func ResetSoftToRef(ctx context.Context, dbData env.DbData, cSpecStr string) (do } // Update the head to this commit - if err = dbData.Ddb.SetHeadToCommit(ctx, dbData.Rsr.CWBHeadRef(), newHead); err != nil { + if err = dbData.Ddb.SetHeadToCommit(ctx, headRef, newHead); err != nil { return doltdb.Roots{}, err } @@ -265,19 +273,31 @@ func resetStaged(ctx context.Context, roots doltdb.Roots, tbls []string) (doltdb } // IsValidRef validates whether the input parameter is a valid cString -// TODO: this doesn't belong int his package -func IsValidRef(ctx context.Context, cSpecStr string, ddb *doltdb.DoltDB, rsr env.RepoStateReader) bool { +// TODO: this doesn't belong in this package +func IsValidRef(ctx context.Context, cSpecStr string, ddb *doltdb.DoltDB, rsr env.RepoStateReader) (bool, error) { + // The error return value is only for propagating unhandled errors from rsr.CWBHeadRef() + // All other errors merely indicate an invalid ref spec. + // TODO: It's much better to enumerate the expected errors, to make sure we don't suppress any unexpected ones. cs, err := doltdb.NewCommitSpec(cSpecStr) if err != nil { - return false + return false, nil + } + + headRef, err := rsr.CWBHeadRef() + if err == doltdb.ErrOperationNotSupportedInDetachedHead { + // This is safe because ddb.Resolve checks if headRef is nil, but only when the value is actually needed. + // Basically, this guarentees that resolving "HEAD" or similar will return an error but other resolves will work. + headRef = nil + } else if err != nil { + return false, err } - _, err = ddb.Resolve(ctx, cs, rsr.CWBHeadRef()) + _, err = ddb.Resolve(ctx, cs, headRef) if err != nil { - return false + return false, nil } - return true + return true, nil } // CleanUntracked deletes untracked tables from the working root. diff --git a/go/libraries/doltcore/env/actions/tag.go b/go/libraries/doltcore/env/actions/tag.go index d34859f02b5..52a8eb95d89 100644 --- a/go/libraries/doltcore/env/actions/tag.go +++ b/go/libraries/doltcore/env/actions/tag.go @@ -32,7 +32,11 @@ type TagProps struct { } func CreateTag(ctx context.Context, dEnv *env.DoltEnv, tagName, startPoint string, props TagProps) error { - return CreateTagOnDB(ctx, dEnv.DoltDB, tagName, startPoint, props, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + return CreateTagOnDB(ctx, dEnv.DoltDB, tagName, startPoint, props, headRef) } func CreateTagOnDB(ctx context.Context, ddb *doltdb.DoltDB, tagName, startPoint string, props TagProps, headRef ref.DoltRef) error { diff --git a/go/libraries/doltcore/env/actions/workspace.go b/go/libraries/doltcore/env/actions/workspace.go index 7f40da3a1e2..7f525532af4 100644 --- a/go/libraries/doltcore/env/actions/workspace.go +++ b/go/libraries/doltcore/env/actions/workspace.go @@ -28,7 +28,11 @@ var ErrCOWorkspaceDelete = errors.New("attempted to delete checked out workspace var ErrBranchNameExists = errors.New("workspace name must not be existing branch name") func CreateWorkspace(ctx context.Context, dEnv *env.DoltEnv, name, startPoint string) error { - return CreateWorkspaceOnDB(ctx, dEnv.DoltDB, name, startPoint, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil + } + return CreateWorkspaceOnDB(ctx, dEnv.DoltDB, name, startPoint, headRef) } func CreateWorkspaceOnDB(ctx context.Context, ddb *doltdb.DoltDB, name, startPoint string, headRef ref.DoltRef) error { @@ -86,7 +90,11 @@ func DeleteWorkspace(ctx context.Context, dEnv *env.DoltEnv, workspaceName strin } } else { dref = ref.NewWorkspaceRef(workspaceName) - if ref.Equals(dEnv.RepoStateReader().CWBHeadRef(), dref) { + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + if ref.Equals(headRef, dref) { return ErrCOWorkspaceDelete } } diff --git a/go/libraries/doltcore/env/environment.go b/go/libraries/doltcore/env/environment.go index 3fb3a4e562f..5a420e749e0 100644 --- a/go/libraries/doltcore/env/environment.go +++ b/go/libraries/doltcore/env/environment.go @@ -206,7 +206,10 @@ func (dEnv *DoltEnv) Valid() bool { // initWorkingSetFromRepoState sets the working set for the env's head to mirror the contents of the repo state file. // This is only necessary to migrate repos written before this method was introduced, and can be removed after 1.0 func (dEnv *DoltEnv) initWorkingSetFromRepoState(ctx context.Context) error { - headRef := dEnv.RepoStateReader().CWBHeadRef() + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } wsRef, err := ref.WorkingSetRefForHead(headRef) if err != nil { return err @@ -591,7 +594,11 @@ func (dEnv *DoltEnv) WorkingSet(ctx context.Context) (*doltdb.WorkingSet, error) } func WorkingSet(ctx context.Context, ddb *doltdb.DoltDB, rsr RepoStateReader) (*doltdb.WorkingSet, error) { - workingSetRef, err := ref.WorkingSetRefForHead(rsr.CWBHeadRef()) + headRef, err := rsr.CWBHeadRef() + if err != nil { + return nil, err + } + workingSetRef, err := ref.WorkingSetRefForHead(headRef) if err != nil { return nil, err } @@ -655,12 +662,12 @@ type repoStateReader struct { *DoltEnv } -func (r *repoStateReader) CWBHeadRef() ref.DoltRef { - return r.RepoState.CWBHeadRef() +func (r *repoStateReader) CWBHeadRef() (ref.DoltRef, error) { + return r.RepoState.CWBHeadRef(), nil } -func (r *repoStateReader) CWBHeadSpec() *doltdb.CommitSpec { - return r.RepoState.CWBHeadSpec() +func (r *repoStateReader) CWBHeadSpec() (*doltdb.CommitSpec, error) { + return r.RepoState.CWBHeadSpec(), nil } func (dEnv *DoltEnv) RepoStateReader() RepoStateReader { diff --git a/go/libraries/doltcore/env/memory.go b/go/libraries/doltcore/env/memory.go index c973fc428fb..14e09954cea 100644 --- a/go/libraries/doltcore/env/memory.go +++ b/go/libraries/doltcore/env/memory.go @@ -101,16 +101,20 @@ type MemoryRepoState struct { var _ RepoStateReader = MemoryRepoState{} var _ RepoStateWriter = MemoryRepoState{} -func (m MemoryRepoState) CWBHeadRef() ref.DoltRef { - return m.Head +func (m MemoryRepoState) CWBHeadRef() (ref.DoltRef, error) { + return m.Head, nil } -func (m MemoryRepoState) CWBHeadSpec() *doltdb.CommitSpec { - spec, err := doltdb.NewCommitSpec(m.CWBHeadRef().GetPath()) +func (m MemoryRepoState) CWBHeadSpec() (*doltdb.CommitSpec, error) { + headRef, err := m.CWBHeadRef() if err != nil { - panic(err) + return nil, err + } + spec, err := doltdb.NewCommitSpec(headRef.GetPath()) + if err != nil { + return nil, err } - return spec + return spec, nil } func (m MemoryRepoState) UpdateStagedRoot(ctx context.Context, newRoot *doltdb.RootValue) error { @@ -120,7 +124,11 @@ func (m MemoryRepoState) UpdateStagedRoot(ctx context.Context, newRoot *doltdb.R ws, err := m.WorkingSet(ctx) if err == doltdb.ErrWorkingSetNotFound { // first time updating root - wsRef, err = ref.WorkingSetRefForHead(m.CWBHeadRef()) + headRef, err := m.CWBHeadRef() + if err != nil { + return err + } + wsRef, err = ref.WorkingSetRefForHead(headRef) if err != nil { return err } @@ -146,7 +154,11 @@ func (m MemoryRepoState) UpdateWorkingRoot(ctx context.Context, newRoot *doltdb. ws, err := m.WorkingSet(ctx) if err == doltdb.ErrWorkingSetNotFound { // first time updating root - wsRef, err = ref.WorkingSetRefForHead(m.CWBHeadRef()) + headRef, err := m.CWBHeadRef() + if err != nil { + return err + } + wsRef, err = ref.WorkingSetRefForHead(headRef) if err != nil { return err } @@ -166,7 +178,11 @@ func (m MemoryRepoState) UpdateWorkingRoot(ctx context.Context, newRoot *doltdb. } func (m MemoryRepoState) WorkingSet(ctx context.Context) (*doltdb.WorkingSet, error) { - workingSetRef, err := ref.WorkingSetRefForHead(m.CWBHeadRef()) + headRef, err := m.CWBHeadRef() + if err != nil { + return nil, err + } + workingSetRef, err := ref.WorkingSetRefForHead(headRef) if err != nil { return nil, err } diff --git a/go/libraries/doltcore/env/remotes.go b/go/libraries/doltcore/env/remotes.go index a5ee0e434a3..96c4b4cccf6 100644 --- a/go/libraries/doltcore/env/remotes.go +++ b/go/libraries/doltcore/env/remotes.go @@ -141,7 +141,10 @@ func NewPushOpts(ctx context.Context, apr *argparser.ArgParseResults, rsr RepoSt } remote, remoteOK := remotes[remoteName] - currentBranch := rsr.CWBHeadRef() + currentBranch, err := rsr.CWBHeadRef() + if err != nil { + return nil, err + } branches, err := rsr.GetBranches() if err != nil { return nil, err @@ -422,7 +425,10 @@ func NewPullSpec(_ context.Context, rsr RepoStateReader, remoteName, remoteRefNa var remoteRef ref.DoltRef if remoteRefName == "" { - branch := rsr.CWBHeadRef() + branch, err := rsr.CWBHeadRef() + if err != nil { + return nil, err + } trackedBranches, err := rsr.GetBranches() if err != nil { return nil, err diff --git a/go/libraries/doltcore/env/repo_state.go b/go/libraries/doltcore/env/repo_state.go index 80ce0ded1fc..f3a265ecf1a 100644 --- a/go/libraries/doltcore/env/repo_state.go +++ b/go/libraries/doltcore/env/repo_state.go @@ -27,8 +27,8 @@ import ( // TODO: change name to ClientStateReader, move out of env package type RepoStateReader interface { - CWBHeadRef() ref.DoltRef - CWBHeadSpec() *doltdb.CommitSpec + CWBHeadRef() (ref.DoltRef, error) + CWBHeadSpec() (*doltdb.CommitSpec, error) GetRemotes() (map[string]Remote, error) GetBackups() (map[string]Remote, error) GetBranches() (map[string]BranchConfig, error) diff --git a/go/libraries/doltcore/merge/action.go b/go/libraries/doltcore/merge/action.go index 8d1ff9b6a89..e4f0303b745 100644 --- a/go/libraries/doltcore/merge/action.go +++ b/go/libraries/doltcore/merge/action.go @@ -62,7 +62,12 @@ func NewMergeSpec(ctx context.Context, rsr env.RepoStateReader, ddb *doltdb.Dolt return nil, err } - headCM, err := ddb.Resolve(context.TODO(), headCS, rsr.CWBHeadRef()) + headRef, err := rsr.CWBHeadRef() + if err != nil { + return nil, err + } + + headCM, err := ddb.Resolve(context.TODO(), headCS, headRef) if err != nil { return nil, err } @@ -72,7 +77,7 @@ func NewMergeSpec(ctx context.Context, rsr env.RepoStateReader, ddb *doltdb.Dolt return nil, err } - mergeCM, err := ddb.Resolve(context.TODO(), mergeCS, rsr.CWBHeadRef()) + mergeCM, err := ddb.Resolve(context.TODO(), mergeCS, headRef) if err != nil { return nil, err } @@ -159,7 +164,11 @@ func ExecuteFFMerge( } if !spec.Squash { - err = dEnv.DoltDB.FastForward(ctx, dEnv.RepoStateReader().CWBHeadRef(), spec.MergeC) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return err + } + err = dEnv.DoltDB.FastForward(ctx, headRef, spec.MergeC) if err != nil { return err diff --git a/go/libraries/doltcore/rebase/rebase.go b/go/libraries/doltcore/rebase/rebase.go index 3cad66c4043..5898f0a6143 100644 --- a/go/libraries/doltcore/rebase/rebase.go +++ b/go/libraries/doltcore/rebase/rebase.go @@ -113,7 +113,11 @@ func AllBranches(ctx context.Context, dEnv *env.DoltEnv, replay ReplayCommitFn, // CurrentBranch rewrites the history of the current branch using the |replay| function. func CurrentBranch(ctx context.Context, dEnv *env.DoltEnv, replay ReplayCommitFn, nerf NeedsRebaseFn) error { - return rebaseRefs(ctx, dEnv.DbData(), replay, nerf, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil + } + return rebaseRefs(ctx, dEnv.DbData(), replay, nerf, headRef) } // AllBranchesByRoots rewrites the history of all branches in the repo using the |replay| function. @@ -130,7 +134,11 @@ func AllBranchesByRoots(ctx context.Context, dEnv *env.DoltEnv, replay ReplayRoo // CurrentBranchByRoot rewrites the history of the current branch using the |replay| function. func CurrentBranchByRoot(ctx context.Context, dEnv *env.DoltEnv, replay ReplayRootFn, nerf NeedsRebaseFn) error { replayCommit := wrapReplayRootFn(replay) - return rebaseRefs(ctx, dEnv.DbData(), replayCommit, nerf, dEnv.RepoStateReader().CWBHeadRef()) + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return nil + } + return rebaseRefs(ctx, dEnv.DbData(), replayCommit, nerf, headRef) } func rebaseRefs(ctx context.Context, dbData env.DbData, replay ReplayCommitFn, nerf NeedsRebaseFn, refs ...ref.DoltRef) error { diff --git a/go/libraries/doltcore/sqle/database.go b/go/libraries/doltcore/sqle/database.go index 034364d142f..53cfccfc68a 100644 --- a/go/libraries/doltcore/sqle/database.go +++ b/go/libraries/doltcore/sqle/database.go @@ -417,14 +417,17 @@ func (db Database) getTableInsensitive(ctx *sql.Context, head *doltdb.Commit, ds // resolveAsOf resolves given expression to a commit, if one exists. func resolveAsOf(ctx *sql.Context, db Database, asOf interface{}) (*doltdb.Commit, *doltdb.RootValue, error) { - head := db.rsr.CWBHeadRef() + head, err := db.rsr.CWBHeadRef() + if err != nil { + return nil, nil, err + } switch x := asOf.(type) { case time.Time: return resolveAsOfTime(ctx, db.ddb, head, x) case string: return resolveAsOfCommitRef(ctx, db, head, x) default: - panic(fmt.Sprintf("unsupported AS OF type %T", asOf)) + return nil, nil, fmt.Errorf("unsupported AS OF type %T", asOf) } } @@ -645,6 +648,12 @@ func (db Database) GetRoot(ctx *sql.Context) (*doltdb.RootValue, error) { return dbState.GetRoots().Working, nil } +// GetWorkingSet gets the current working set for the database. +// If there is no working set (most likely because the DB is in Detached Head mode, return an error. +// If a command needs to work while in Detached Head, that command should call sess.LookupDbState directly. +// TODO: This is a temporary measure to make sure that new commands that call GetWorkingSet don't unexpectedly receive +// a null pointer. In the future, we should replace all uses of dbState.WorkingSet, including this, with a new interface +// where users avoid handling the WorkingSet directly. func (db Database) GetWorkingSet(ctx *sql.Context) (*doltdb.WorkingSet, error) { sess := dsess.DSessFromSess(ctx.Session) dbState, ok, err := sess.LookupDbState(ctx, db.Name()) @@ -654,6 +663,9 @@ func (db Database) GetWorkingSet(ctx *sql.Context) (*doltdb.WorkingSet, error) { if !ok { return nil, fmt.Errorf("no root value found in session") } + if dbState.WorkingSet == nil { + return nil, doltdb.ErrOperationNotSupportedInDetachedHead + } return dbState.WorkingSet, nil } diff --git a/go/libraries/doltcore/sqle/database_provider.go b/go/libraries/doltcore/sqle/database_provider.go index 811d61dd767..2b3a50d16e3 100644 --- a/go/libraries/doltcore/sqle/database_provider.go +++ b/go/libraries/doltcore/sqle/database_provider.go @@ -864,7 +864,11 @@ func initialDbState(ctx context.Context, db dsess.SqlDatabase, branch string) (d if len(branch) > 0 { r = ref.NewBranchRef(branch) } else { - r = rsr.CWBHeadRef() + var err error + r, err = rsr.CWBHeadRef() + if err != nil { + return dsess.InitialDbState{}, err + } } var retainedErr error @@ -1418,7 +1422,11 @@ func initialStateForCommit(ctx context.Context, srcDb ReadOnlyDatabase) (dsess.I return dsess.InitialDbState{}, err } - cm, err := srcDb.DbData().Ddb.Resolve(ctx, spec, srcDb.DbData().Rsr.CWBHeadRef()) + headRef, err := srcDb.DbData().Rsr.CWBHeadRef() + if err != nil { + return dsess.InitialDbState{}, err + } + cm, err := srcDb.DbData().Ddb.Resolve(ctx, spec, headRef) if err != nil { return dsess.InitialDbState{}, err } @@ -1448,8 +1456,8 @@ type staticRepoState struct { env.RepoStateReader } -func (s staticRepoState) CWBHeadRef() ref.DoltRef { - return s.branch +func (s staticRepoState) CWBHeadRef() (ref.DoltRef, error) { + return s.branch, nil } // formatDbMapKeyName returns formatted string of database name and/or branch name. Database name is case-insensitive, diff --git a/go/libraries/doltcore/sqle/dfunctions/active_branch.go b/go/libraries/doltcore/sqle/dfunctions/active_branch.go index ddb987482d8..771c9bd84cb 100644 --- a/go/libraries/doltcore/sqle/dfunctions/active_branch.go +++ b/go/libraries/doltcore/sqle/dfunctions/active_branch.go @@ -20,6 +20,7 @@ import ( "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/go-mysql-server/sql/types" + "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" ) @@ -46,6 +47,10 @@ func (ab *ActiveBranchFunc) Eval(ctx *sql.Context, row sql.Row) (interface{}, er } currentBranchRef, err := dSess.CWBHeadRef(ctx, dbName) + if err == doltdb.ErrOperationNotSupportedInDetachedHead { + // active_branch should return NULL if we're in detached head state + return nil, nil + } if err != nil { return nil, err } diff --git a/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go b/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go index 38cac2809aa..cdca5af2e41 100644 --- a/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go +++ b/go/libraries/doltcore/sqle/dfunctions/dolt_merge_base.go @@ -94,11 +94,16 @@ func resolveRefSpecs(ctx *sql.Context, leftSpec, rightSpec string) (left, right return nil, nil, sql.ErrDatabaseNotFound.New(dbName) } - left, err = doltDB.Resolve(ctx, lcs, dbData.Rsr.CWBHeadRef()) + headRef, err := dbData.Rsr.CWBHeadRef() if err != nil { return nil, nil, err } - right, err = doltDB.Resolve(ctx, rcs, dbData.Rsr.CWBHeadRef()) + + left, err = doltDB.Resolve(ctx, lcs, headRef) + if err != nil { + return nil, nil, err + } + right, err = doltDB.Resolve(ctx, rcs, headRef) if err != nil { return nil, nil, err } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_checkout.go b/go/libraries/doltcore/sqle/dprocedures/dolt_checkout.go index 4e851e18fc2..cfbc7eb81a7 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_checkout.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_checkout.go @@ -224,7 +224,12 @@ func checkoutRemoteBranch(ctx *sql.Context, dbName string, dbData env.DbData, br return errhand.BuildDError(fmt.Errorf("%w: '%s'", err, remoteRef.GetRemote()).Error()).Build() } - return env.SetRemoteUpstreamForRefSpec(dbData.Rsw, refSpec, remoteRef.GetRemote(), dbData.Rsr.CWBHeadRef()) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return err + } + + return env.SetRemoteUpstreamForRefSpec(dbData.Rsw, refSpec, remoteRef.GetRemote(), headRef) } else { return fmt.Errorf("'%s' matched multiple (%v) remote tracking branches", branchName, len(remoteRefs)) } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_cherry_pick.go b/go/libraries/doltcore/sqle/dprocedures/dolt_cherry_pick.go index 132a9804ecb..c73d138f4cb 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_cherry_pick.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_cherry_pick.go @@ -136,7 +136,11 @@ func cherryPick(ctx *sql.Context, dSess *dsess.DoltSession, roots doltdb.Roots, if err != nil { return nil, "", err } - cherryCommit, err := doltDB.Resolve(ctx, cherryCommitSpec, dbData.Rsr.CWBHeadRef()) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return nil, "", err + } + cherryCommit, err := doltDB.Resolve(ctx, cherryCommitSpec, headRef) if err != nil { return nil, "", err } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_merge.go b/go/libraries/doltcore/sqle/dprocedures/dolt_merge.go index a098e288f4c..a587de23283 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_merge.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_merge.go @@ -120,7 +120,12 @@ func doDoltMerge(ctx *sql.Context, args []string) (int, int, error) { if !ok { return noConflictsOrViolations, threeWayMerge, fmt.Errorf("Could not load database %s", dbName) } - msg := fmt.Sprintf("Merge branch '%s' into %s", branchName, dbData.Rsr.CWBHeadRef().GetPath()) + + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return noConflictsOrViolations, threeWayMerge, err + } + msg := fmt.Sprintf("Merge branch '%s' into %s", branchName, headRef.GetPath()) if userMsg, mOk := apr.GetValue(cli.MessageArg); mOk { msg = userMsg } @@ -266,7 +271,11 @@ func executeFFMerge(ctx *sql.Context, dbName string, squash bool, ws *doltdb.Wor // TODO: This is all incredibly suspect, needs to be replaced with library code that is functional instead of // altering global state if !squash { - err = dbData.Ddb.FastForward(ctx, dbData.Rsr.CWBHeadRef(), cm2) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return nil, err + } + err = dbData.Ddb.FastForward(ctx, headRef, cm2) if err != nil { return ws, err } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_pull.go b/go/libraries/doltcore/sqle/dprocedures/dolt_pull.go index 3f600c3a026..98cf54fbdb9 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_pull.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_pull.go @@ -148,7 +148,11 @@ func doDoltPull(ctx *sql.Context, args []string) (int, int, error) { return noConflictsOrViolations, threeWayMerge, err } - msg := fmt.Sprintf("Merge branch '%s' of %s into %s", pullSpec.Branch.GetPath(), pullSpec.Remote.Url, dbData.Rsr.CWBHeadRef().GetPath()) + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return noConflictsOrViolations, threeWayMerge, err + } + msg := fmt.Sprintf("Merge branch '%s' of %s into %s", pullSpec.Branch.GetPath(), pullSpec.Remote.Url, headRef.GetPath()) ws, conflicts, fastForward, err = performMerge(ctx, sess, roots, ws, dbName, mergeSpec, apr.Contains(cli.NoCommitFlag), msg) if err != nil && !errors.Is(doltdb.ErrUpToDate, err) { return conflicts, fastForward, err diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_reset.go b/go/libraries/doltcore/sqle/dprocedures/dolt_reset.go index b23ddeaab6e..d6d4269d38e 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_reset.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_reset.go @@ -100,7 +100,11 @@ func doDoltReset(ctx *sql.Context, args []string) (int, error) { // TODO: this overrides the transaction setting, needs to happen at commit, not here if newHead != nil { - if err := dbData.Ddb.SetHeadToCommit(ctx, dbData.Rsr.CWBHeadRef(), newHead); err != nil { + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return 1, err + } + if err := dbData.Ddb.SetHeadToCommit(ctx, headRef, newHead); err != nil { return 1, err } } diff --git a/go/libraries/doltcore/sqle/dprocedures/dolt_tag.go b/go/libraries/doltcore/sqle/dprocedures/dolt_tag.go index 4967481f825..95e54a3f9e0 100644 --- a/go/libraries/doltcore/sqle/dprocedures/dolt_tag.go +++ b/go/libraries/doltcore/sqle/dprocedures/dolt_tag.go @@ -97,7 +97,10 @@ func doDoltTag(ctx *sql.Context, args []string) (int, error) { if len(apr.Args) > 1 { startPoint = apr.Arg(1) } - headRef := dbData.Rsr.CWBHeadRef() + headRef, err := dbData.Rsr.CWBHeadRef() + if err != nil { + return 0, err + } err = actions.CreateTagOnDB(ctx, dbData.Ddb, tagName, startPoint, props, headRef) if err != nil { return 1, err diff --git a/go/libraries/doltcore/sqle/dsess/session.go b/go/libraries/doltcore/sqle/dsess/session.go index 3d9cde65705..aec019701bf 100644 --- a/go/libraries/doltcore/sqle/dsess/session.go +++ b/go/libraries/doltcore/sqle/dsess/session.go @@ -573,7 +573,7 @@ func (d *DoltSession) NewPendingCommit(ctx *sql.Context, dbName string, roots do headHash, _ := headCommit.HashOf() if sessionState.WorkingSet == nil { - return nil, fmt.Errorf("Cannot commit while not attached to a branch. ") + return nil, doltdb.ErrOperationNotSupportedInDetachedHead } var mergeParentCommits []*doltdb.Commit @@ -841,6 +841,10 @@ func (d *DoltSession) SetRoot(ctx *sql.Context, dbName string, newRoot *doltdb.R return err } + if sessionState.WorkingSet == nil { + return doltdb.ErrOperationNotSupportedInDetachedHead + } + if rootsEqual(sessionState.GetRoots().Working, newRoot) { return nil } @@ -864,6 +868,10 @@ func (d *DoltSession) SetRoots(ctx *sql.Context, dbName string, roots doltdb.Roo return err } + if sessionState.WorkingSet == nil { + return doltdb.ErrOperationNotSupportedInDetachedHead + } + workingSet := sessionState.WorkingSet.WithWorkingRoot(roots.Working).WithStagedRoot(roots.Staged) return d.SetWorkingSet(ctx, dbName, workingSet) } @@ -1039,6 +1047,9 @@ func (d *DoltSession) WorkingSet(ctx *sql.Context, dbName string) (*doltdb.Worki if err != nil { return nil, err } + if sessionState.WorkingSet == nil { + return nil, doltdb.ErrOperationNotSupportedInDetachedHead + } return sessionState.WorkingSet, nil } @@ -1266,7 +1277,7 @@ func (d *DoltSession) CWBHeadRef(ctx *sql.Context, dbName string) (ref.DoltRef, } if dbState.WorkingSet == nil { - return nil, nil + return nil, doltdb.ErrOperationNotSupportedInDetachedHead } return dbState.WorkingSet.Ref().ToHeadRef() diff --git a/go/libraries/doltcore/sqle/dsess/session_state_adapter.go b/go/libraries/doltcore/sqle/dsess/session_state_adapter.go index 5bb89670435..700480d008f 100644 --- a/go/libraries/doltcore/sqle/dsess/session_state_adapter.go +++ b/go/libraries/doltcore/sqle/dsess/session_state_adapter.go @@ -61,29 +61,30 @@ func (s SessionStateAdapter) GetRoots(ctx context.Context) (doltdb.Roots, error) return state.GetRoots(), nil } -func (s SessionStateAdapter) CWBHeadRef() ref.DoltRef { +func (s SessionStateAdapter) CWBHeadRef() (ref.DoltRef, error) { workingSet, err := s.session.WorkingSet(sql.NewContext(context.Background()), s.dbName) if err != nil { - // TODO: fix this interface - panic(err) + return nil, err } headRef, err := workingSet.Ref().ToHeadRef() - // TODO: fix this interface if err != nil { - panic(err) + return nil, err } - return headRef + return headRef, nil } -func (s SessionStateAdapter) CWBHeadSpec() *doltdb.CommitSpec { +func (s SessionStateAdapter) CWBHeadSpec() (*doltdb.CommitSpec, error) { // TODO: get rid of this - ref := s.CWBHeadRef() + ref, err := s.CWBHeadRef() + if err != nil { + return nil, err + } spec, err := doltdb.NewCommitSpec(ref.GetPath()) if err != nil { panic(err) } - return spec + return spec, nil } func (s SessionStateAdapter) GetRemotes() (map[string]env.Remote, error) { diff --git a/go/libraries/doltcore/sqle/dtables/ignore_table.go b/go/libraries/doltcore/sqle/dtables/ignore_table.go index b0b3c716911..d9ff8009c22 100644 --- a/go/libraries/doltcore/sqle/dtables/ignore_table.go +++ b/go/libraries/doltcore/sqle/dtables/ignore_table.go @@ -117,13 +117,12 @@ var _ sql.RowDeleter = (*ignoreWriter)(nil) type ignoreWriter struct { it *IgnoreTable errDuringStatementBegin error - workingSet *doltdb.WorkingSet prevHash *hash.Hash tableWriter writer.TableWriter } func newIgnoreWriter(it *IgnoreTable) *ignoreWriter { - return &ignoreWriter{it, nil, nil, nil, nil} + return &ignoreWriter{it, nil, nil, nil} } // Insert inserts the row given, returning an error if it cannot. Insert will be called once for each row to process @@ -179,7 +178,6 @@ func (iw *ignoreWriter) StatementBegin(ctx *sql.Context) { iw.prevHash = &prevHash - iw.workingSet = dbState.WorkingSet found, err := roots.Working.HasTable(ctx, doltdb.IgnoreTableName) if err != nil { @@ -229,6 +227,11 @@ func (iw *ignoreWriter) StatementBegin(ctx *sql.Context) { return } + if dbState.WorkingSet == nil { + iw.errDuringStatementBegin = doltdb.ErrOperationNotSupportedInDetachedHead + return + } + // We use WriteSession.SetWorkingSet instead of DoltSession.SetRoot because we want to avoid modifying the root // until the end of the transaction, but we still want the WriteSession to be able to find the newly // created table. diff --git a/go/libraries/doltcore/sqle/index/mergeable_indexes_setup_test.go b/go/libraries/doltcore/sqle/index/mergeable_indexes_setup_test.go index 18d72fe3129..cee830b1079 100644 --- a/go/libraries/doltcore/sqle/index/mergeable_indexes_setup_test.go +++ b/go/libraries/doltcore/sqle/index/mergeable_indexes_setup_test.go @@ -169,11 +169,18 @@ func drainIter(ctx *sql.Context, iter sql.RowIter) error { return iter.Close(ctx) } -func getDbState(t *testing.T, db sql.Database, dEnv *env.DoltEnv) dsess.InitialDbState { +func getDbState(t *testing.T, db sql.Database, dEnv *env.DoltEnv) (dsess.InitialDbState, error) { ctx := context.Background() - head := dEnv.RepoStateReader().CWBHeadSpec() - headCommit, err := dEnv.DoltDB.Resolve(ctx, head, dEnv.RepoStateReader().CWBHeadRef()) + headSpec, err := dEnv.RepoStateReader().CWBHeadSpec() + if err != nil { + return dsess.InitialDbState{}, err + } + headRef, err := dEnv.RepoStateReader().CWBHeadRef() + if err != nil { + return dsess.InitialDbState{}, err + } + headCommit, err := dEnv.DoltDB.Resolve(ctx, headSpec, headRef) require.NoError(t, err) ws, err := dEnv.WorkingSet(ctx) @@ -185,5 +192,5 @@ func getDbState(t *testing.T, db sql.Database, dEnv *env.DoltEnv) dsess.InitialD WorkingSet: ws, DbData: dEnv.DbData(), Remotes: dEnv.RepoState.Remotes, - } + }, nil } diff --git a/go/libraries/doltcore/sqle/tables.go b/go/libraries/doltcore/sqle/tables.go index 5b71a957a82..b53e381d0c7 100644 --- a/go/libraries/doltcore/sqle/tables.go +++ b/go/libraries/doltcore/sqle/tables.go @@ -1258,6 +1258,9 @@ func (t *AlterableDoltTable) RewriteInserter( } ws := dbState.WorkingSet + if ws == nil { + return nil, doltdb.ErrOperationNotSupportedInDetachedHead + } head, err := sess.GetHeadCommit(ctx, t.db.Name()) if err != nil { diff --git a/go/libraries/doltcore/sqle/temp_table.go b/go/libraries/doltcore/sqle/temp_table.go index 7af13838400..2795e309e3d 100644 --- a/go/libraries/doltcore/sqle/temp_table.go +++ b/go/libraries/doltcore/sqle/temp_table.go @@ -79,6 +79,9 @@ func NewTempTable( } ws := dbState.WorkingSet + if ws == nil { + return nil, doltdb.ErrOperationNotSupportedInDetachedHead + } sch, err := temporaryDoltSchema(ctx, pkSch, collation) if err != nil { @@ -153,6 +156,9 @@ func setTempTableRoot(t *TempTable) func(ctx *sql.Context, dbName string, newRoo } ws := dbState.WorkingSet + if ws == nil { + return doltdb.ErrOperationNotSupportedInDetachedHead + } newWs := ws.WithWorkingRoot(newRoot) ait, err := globalstate.NewAutoIncrementTracker(ctx, newWs)