mirror of
https://github.com/dolthub/dolt.git
synced 2026-04-21 02:57:46 -05:00
688 lines
22 KiB
Go
688 lines
22 KiB
Go
// Copyright 2019 Dolthub, Inc.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package commands
|
|
|
|
import (
|
|
"bytes"
|
|
"context"
|
|
"fmt"
|
|
"io"
|
|
"os"
|
|
"strings"
|
|
|
|
"github.com/fatih/color"
|
|
goisatty "github.com/mattn/go-isatty"
|
|
|
|
"github.com/dolthub/dolt/go/cmd/dolt/cli"
|
|
"github.com/dolthub/dolt/go/cmd/dolt/errhand"
|
|
"github.com/dolthub/dolt/go/libraries/doltcore/diff"
|
|
"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/merge"
|
|
"github.com/dolthub/dolt/go/libraries/utils/argparser"
|
|
"github.com/dolthub/dolt/go/libraries/utils/editor"
|
|
"github.com/dolthub/dolt/go/libraries/utils/iohelp"
|
|
"github.com/dolthub/dolt/go/libraries/utils/set"
|
|
"github.com/dolthub/dolt/go/store/datas"
|
|
)
|
|
|
|
var commitDocs = cli.CommandDocumentationContent{
|
|
ShortDesc: "Record changes to the database",
|
|
LongDesc: `
|
|
Stores the current contents of the staged tables in a new commit along with a log message from the user describing the changes.
|
|
|
|
The content to be added can be specified by using dolt add to incrementally \"add\" changes to the staged tables before using the commit command (Note: even modified tables must be \"added\").
|
|
|
|
The log message can be added with the parameter {{.EmphasisLeft}}-m <msg>{{.EmphasisRight}}. If the {{.LessThan}}-m{{.GreaterThan}} parameter is not provided an editor will be opened where you can review the commit and provide a log message.
|
|
|
|
The commit timestamp can be modified using the --date parameter. Dates can be specified in the formats {{.LessThan}}YYYY-MM-DD{{.GreaterThan}}, {{.LessThan}}YYYY-MM-DDTHH:MM:SS{{.GreaterThan}}, or {{.LessThan}}YYYY-MM-DDTHH:MM:SSZ07:00{{.GreaterThan}} (where {{.LessThan}}07:00{{.GreaterThan}} is the time zone offset)."`,
|
|
Synopsis: []string{
|
|
"[options]",
|
|
},
|
|
}
|
|
|
|
type CommitCmd struct{}
|
|
|
|
// Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
|
|
func (cmd CommitCmd) Name() string {
|
|
return "commit"
|
|
}
|
|
|
|
// Description returns a description of the command
|
|
func (cmd CommitCmd) Description() string {
|
|
return "Record changes to the repository."
|
|
}
|
|
|
|
func (cmd CommitCmd) Docs() *cli.CommandDocumentation {
|
|
ap := cli.CreateCommitArgParser()
|
|
return cli.NewCommandDocumentation(commitDocs, ap)
|
|
}
|
|
|
|
func (cmd CommitCmd) ArgParser() *argparser.ArgParser {
|
|
return cli.CreateCommitArgParser()
|
|
}
|
|
|
|
// Exec executes the command
|
|
func (cmd CommitCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int {
|
|
res, skipped := performCommit(ctx, commandStr, args, dEnv)
|
|
if res == 1 {
|
|
return res
|
|
}
|
|
|
|
if skipped {
|
|
iohelp.WriteLine(cli.CliOut, "Skipping empty commit")
|
|
return res
|
|
}
|
|
|
|
// if the commit was successful, print it out using the log command
|
|
return LogCmd{}.Exec(ctx, "log", []string{"-n=1"}, dEnv, nil)
|
|
}
|
|
|
|
// performCommit creates a new Dolt commit using the specified |commandStr| and |args| for the specified Dolt environment
|
|
// |dEnv|. The response is an integer status code indicating success or failure, as well as a boolean that indicates
|
|
// if the commit was skipped (e.g. because --skip-empty was specified as an argument).
|
|
func performCommit(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv) (int, bool) {
|
|
ap := cli.CreateCommitArgParser()
|
|
help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, commitDocs, ap))
|
|
apr := cli.ParseArgsOrDie(ap, args, help)
|
|
|
|
if err := cli.VerifyCommitArgs(apr); err != nil {
|
|
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), help), false
|
|
}
|
|
|
|
allFlag := apr.Contains(cli.AllFlag)
|
|
upperCaseAllFlag := apr.Contains(cli.UpperCaseAllFlag)
|
|
|
|
if dEnv.IsLocked() {
|
|
return HandleVErrAndExitCode(errhand.VerboseErrorFromError(env.ErrActiveServerLock.New(dEnv.LockFile())), help), false
|
|
}
|
|
|
|
roots, err := dEnv.Roots(ctx)
|
|
if err != nil {
|
|
return HandleVErrAndExitCode(errhand.BuildDError("Couldn't get working root").AddCause(err).Build(), usage), false
|
|
}
|
|
|
|
if upperCaseAllFlag {
|
|
roots, err = actions.StageAllTables(ctx, roots, true)
|
|
if err != nil {
|
|
return handleCommitErr(ctx, dEnv, err, help), false
|
|
}
|
|
} else if allFlag {
|
|
roots, err = actions.StageModifiedAndDeletedTables(ctx, roots)
|
|
if err != nil {
|
|
return handleCommitErr(ctx, dEnv, err, help), false
|
|
}
|
|
}
|
|
|
|
headCommit, _ := dEnv.HeadCommit(ctx)
|
|
headHash, _ := headCommit.HashOf()
|
|
|
|
var name, email string
|
|
// Check if the author flag is provided otherwise get the name and email stored in configs
|
|
if authorStr, ok := apr.GetValue(cli.AuthorParam); ok {
|
|
name, email, err = cli.ParseAuthor(authorStr)
|
|
} else {
|
|
// This command creates a commit, so we need user identity
|
|
if !cli.CheckUserNameAndEmail(dEnv) {
|
|
return 1, false
|
|
}
|
|
name, email, err = env.GetNameAndEmail(dEnv.Config)
|
|
}
|
|
|
|
if err != nil {
|
|
return handleCommitErr(ctx, dEnv, err, usage), false
|
|
}
|
|
|
|
msg, msgOk := apr.GetValue(cli.MessageArg)
|
|
if !msgOk {
|
|
amendStr := ""
|
|
if apr.Contains(cli.AmendFlag) {
|
|
commitMeta, cmErr := headCommit.GetCommitMeta(ctx)
|
|
if cmErr != nil {
|
|
return handleCommitErr(ctx, dEnv, cmErr, usage), false
|
|
}
|
|
amendStr = commitMeta.Description
|
|
}
|
|
msg, err = getCommitMessageFromEditor(ctx, dEnv, "", amendStr, false)
|
|
if err != nil {
|
|
return handleCommitErr(ctx, dEnv, err, usage), false
|
|
}
|
|
}
|
|
|
|
t := datas.CommitNowFunc()
|
|
if commitTimeStr, ok := apr.GetValue(cli.DateParam); ok {
|
|
var err error
|
|
t, err = cli.ParseDate(commitTimeStr)
|
|
|
|
if err != nil {
|
|
return HandleVErrAndExitCode(errhand.BuildDError("error: invalid date").AddCause(err).Build(), usage), false
|
|
}
|
|
}
|
|
|
|
var parentsHeadForAmend []*doltdb.Commit
|
|
if apr.Contains(cli.AmendFlag) {
|
|
numParentsHeadForAmend := headCommit.NumParents()
|
|
for i := 0; i < numParentsHeadForAmend; i++ {
|
|
parentCommit, err := headCommit.GetParent(ctx, i)
|
|
if err == nil {
|
|
parentsHeadForAmend = append(parentsHeadForAmend, parentCommit)
|
|
}
|
|
}
|
|
|
|
newRoots, err := actions.ResetSoftToRef(ctx, dEnv.DbData(), "HEAD~1")
|
|
if err != nil {
|
|
return handleResetError(err, usage), false
|
|
}
|
|
|
|
err = dEnv.UpdateStagedRoot(ctx, newRoots.Staged)
|
|
if err != nil {
|
|
return handleResetError(err, usage), false
|
|
}
|
|
}
|
|
|
|
ws, err := dEnv.WorkingSet(ctx)
|
|
if err != nil {
|
|
return HandleVErrAndExitCode(errhand.BuildDError("Couldn't get working set").AddCause(err).Build(), usage), false
|
|
}
|
|
|
|
prevHash, err := ws.HashOf()
|
|
if err != nil {
|
|
return HandleVErrAndExitCode(errhand.BuildDError("Couldn't get working set").AddCause(err).Build(), usage), false
|
|
}
|
|
|
|
var mergeParentCommits []*doltdb.Commit
|
|
if ws.MergeActive() {
|
|
mergeParentCommits = []*doltdb.Commit{ws.MergeState().Commit()}
|
|
} else if apr.Contains(cli.AmendFlag) && len(parentsHeadForAmend) > 1 {
|
|
mergeParentCommits = parentsHeadForAmend
|
|
}
|
|
|
|
pendingCommit, err := actions.GetCommitStaged(ctx, roots, ws, mergeParentCommits, dEnv.DbData().Ddb, actions.CommitStagedProps{
|
|
Message: msg,
|
|
Date: t,
|
|
AllowEmpty: apr.Contains(cli.AllowEmptyFlag) || apr.Contains(cli.AmendFlag),
|
|
SkipEmpty: apr.Contains(cli.SkipEmptyFlag),
|
|
Force: apr.Contains(cli.ForceFlag),
|
|
Name: name,
|
|
Email: email,
|
|
})
|
|
if err != nil {
|
|
if apr.Contains(cli.AmendFlag) {
|
|
newRoots, errRes := actions.ResetSoftToRef(ctx, dEnv.DbData(), headHash.String())
|
|
if errRes != nil {
|
|
return handleResetError(errRes, usage), false
|
|
}
|
|
|
|
err = dEnv.UpdateStagedRoot(ctx, newRoots.Staged)
|
|
if err != nil {
|
|
return handleResetError(err, usage), false
|
|
}
|
|
}
|
|
return handleCommitErr(ctx, dEnv, err, usage), false
|
|
}
|
|
|
|
// If no error was reported and there is no pending commit, then no commit was created, likely
|
|
// because of --skip-empty, so return a success code, but indicate that no commit was created.
|
|
if pendingCommit == nil {
|
|
return 0, true
|
|
}
|
|
|
|
headRef, err := dEnv.RepoStateReader().CWBHeadRef()
|
|
if err != nil {
|
|
return handleCommitErr(ctx, dEnv, err, usage), false
|
|
}
|
|
_, err = dEnv.DoltDB.CommitWithWorkingSet(
|
|
ctx,
|
|
headRef,
|
|
ws.Ref(),
|
|
pendingCommit,
|
|
ws.WithStagedRoot(pendingCommit.Roots.Staged).WithWorkingRoot(pendingCommit.Roots.Working).ClearMerge(),
|
|
prevHash,
|
|
dEnv.NewWorkingSetMeta(fmt.Sprintf("Updated by %s %s", commandStr, strings.Join(args, " "))),
|
|
nil,
|
|
)
|
|
if err != nil {
|
|
if apr.Contains(cli.AmendFlag) {
|
|
newRoots, errRes := actions.ResetSoftToRef(ctx, dEnv.DbData(), headHash.String())
|
|
if errRes != nil {
|
|
return handleResetError(errRes, usage), false
|
|
}
|
|
|
|
err = dEnv.UpdateStagedRoot(ctx, newRoots.Staged)
|
|
if err != nil {
|
|
return handleResetError(err, usage), false
|
|
}
|
|
}
|
|
return HandleVErrAndExitCode(errhand.BuildDError("Couldn't commit").AddCause(err).Build(), usage), false
|
|
}
|
|
|
|
return 0, false
|
|
}
|
|
|
|
func handleCommitErr(ctx context.Context, dEnv *env.DoltEnv, err error, usage cli.UsagePrinter) int {
|
|
if err == nil {
|
|
return 0
|
|
}
|
|
|
|
if err == datas.ErrNameNotConfigured {
|
|
bdr := errhand.BuildDError("Could not determine %s.", env.UserNameKey)
|
|
bdr.AddDetails("Log into DoltHub: dolt login")
|
|
bdr.AddDetails("OR add name to config: dolt config [--global|--local] --add %[1]s \"FIRST LAST\"", env.UserNameKey)
|
|
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
|
|
if err == datas.ErrEmailNotConfigured {
|
|
bdr := errhand.BuildDError("Could not determine %s.", env.UserEmailKey)
|
|
bdr.AddDetails("Log into DoltHub: dolt login")
|
|
bdr.AddDetails("OR add email to config: dolt config [--global|--local] --add %[1]s \"EMAIL_ADDRESS\"", env.UserEmailKey)
|
|
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
|
|
if err == datas.ErrEmptyCommitMessage {
|
|
bdr := errhand.BuildDError("Aborting commit due to empty commit message.")
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
|
|
if actions.IsNothingStaged(err) {
|
|
notStagedTbls := actions.NothingStagedTblDiffs(err)
|
|
n, newErr := PrintDiffsNotStaged(ctx, dEnv, cli.CliOut, notStagedTbls, false, false, 0, merge.ArtifactStatus{})
|
|
if newErr != nil {
|
|
bdr := errhand.BuildDError(`No changes added to commit (use "dolt add")\nCould not print diff because of additional error`)
|
|
bdr.AddCause(newErr)
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
|
|
if n == 0 {
|
|
bdr := errhand.BuildDError(`no changes added to commit (use "dolt add")`)
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
}
|
|
|
|
if actions.IsTblInConflict(err) {
|
|
inConflict := actions.GetTablesForError(err)
|
|
bdr := errhand.BuildDError(`tables %v have unresolved conflicts from the merge. resolve the conflicts before commiting`, inConflict)
|
|
return HandleVErrAndExitCode(bdr.Build(), usage)
|
|
}
|
|
|
|
verr := errhand.BuildDError("error: Failed to commit changes.").AddCause(err).Build()
|
|
return HandleVErrAndExitCode(verr, usage)
|
|
}
|
|
|
|
// getCommitMessageFromEditor opens editor to ask user for commit message if none defined from command line.
|
|
// suggestedMsg will be returned if no-edit flag is defined or if this function was called from sql dolt_merge command.
|
|
func getCommitMessageFromEditor(ctx context.Context, dEnv *env.DoltEnv, suggestedMsg, amendString string, noEdit bool) (string, error) {
|
|
if cli.ExecuteWithStdioRestored == nil || noEdit {
|
|
return suggestedMsg, nil
|
|
}
|
|
|
|
if !checkIsTerminal() {
|
|
return suggestedMsg, nil
|
|
}
|
|
|
|
var finalMsg string
|
|
initialMsg, err := buildInitalCommitMsg(ctx, dEnv, suggestedMsg)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
if amendString != "" {
|
|
initialMsg = fmt.Sprintf("%s\n%s", amendString, initialMsg)
|
|
}
|
|
|
|
backupEd := "vim"
|
|
// try getting default editor on the user system
|
|
if ed, edSet := os.LookupEnv("EDITOR"); edSet {
|
|
backupEd = ed
|
|
}
|
|
// try getting Dolt config core.editor
|
|
editorStr := dEnv.Config.GetStringOrDefault(env.DoltEditor, backupEd)
|
|
|
|
cli.ExecuteWithStdioRestored(func() {
|
|
commitMsg, cErr := editor.OpenCommitEditor(editorStr, initialMsg)
|
|
if cErr != nil {
|
|
err = cErr
|
|
}
|
|
finalMsg = parseCommitMessage(commitMsg)
|
|
})
|
|
|
|
if err != nil {
|
|
return "", fmt.Errorf("Failed to open commit editor: %v \n Check your `EDITOR` environment variable with `echo $EDITOR` or your dolt config with `dolt config --list` to ensure that your editor is valid", err)
|
|
}
|
|
|
|
return finalMsg, nil
|
|
}
|
|
|
|
func checkIsTerminal() bool {
|
|
isTerminal := false
|
|
cli.ExecuteWithStdioRestored(func() {
|
|
if goisatty.IsTerminal(os.Stdout.Fd()) || os.Getenv("DOLT_TEST_FORCE_OPEN_EDITOR") == "1" {
|
|
isTerminal = true
|
|
}
|
|
})
|
|
return isTerminal
|
|
}
|
|
|
|
func buildInitalCommitMsg(ctx context.Context, dEnv *env.DoltEnv, suggestedMsg string) (string, error) {
|
|
initialNoColor := color.NoColor
|
|
color.NoColor = true
|
|
|
|
roots, err := dEnv.Roots(ctx)
|
|
if err != nil {
|
|
panic(err)
|
|
}
|
|
|
|
stagedTblDiffs, notStagedTblDiffs, _ := diff.GetStagedUnstagedTableDeltas(ctx, roots)
|
|
|
|
ws, err := dEnv.WorkingSet(ctx)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
as, err := merge.GetMergeArtifactStatus(ctx, ws)
|
|
if err != nil {
|
|
return "", nil
|
|
}
|
|
|
|
buf := bytes.NewBuffer([]byte{})
|
|
n := printStagedDiffs(buf, stagedTblDiffs, true)
|
|
n, err = PrintDiffsNotStaged(ctx, dEnv, buf, notStagedTblDiffs, true, false, n, as)
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
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)
|
|
|
|
msgLines := strings.Split(buf.String(), "\n")
|
|
for i, msg := range msgLines {
|
|
msgLines[i] = "# " + msg
|
|
}
|
|
statusMsg := strings.Join(msgLines, "\n")
|
|
|
|
color.NoColor = initialNoColor
|
|
return initialCommitMessage + statusMsg, nil
|
|
}
|
|
|
|
func parseCommitMessage(cm string) string {
|
|
lines := strings.Split(cm, "\n")
|
|
filtered := make([]string, 0, len(lines))
|
|
for _, line := range lines {
|
|
if len(line) >= 1 && line[0] == '#' {
|
|
continue
|
|
}
|
|
filtered = append(filtered, line)
|
|
}
|
|
return strings.Join(filtered, "\n")
|
|
}
|
|
|
|
func PrintDiffsNotStaged(
|
|
ctx context.Context,
|
|
dEnv *env.DoltEnv,
|
|
wr io.Writer,
|
|
notStagedTbls []diff.TableDelta,
|
|
printHelp bool,
|
|
printIgnored bool,
|
|
linesPrinted int,
|
|
as merge.ArtifactStatus,
|
|
) (int, error) {
|
|
roots, err := dEnv.Roots(ctx)
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
|
|
inCnfSet := set.NewStrSet(as.DataConflictTables)
|
|
inCnfSet.Add(as.SchemaConflictsTables...)
|
|
violationSet := set.NewStrSet(as.ConstraintViolationsTables)
|
|
|
|
if as.HasConflicts() || as.HasConstraintViolations() {
|
|
if linesPrinted > 0 {
|
|
cli.Println()
|
|
}
|
|
iohelp.WriteLine(wr, unmergedPathsHeader)
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, mergedTableHelp)
|
|
}
|
|
|
|
if as.HasConflicts() {
|
|
lines := make([]string, 0, len(notStagedTbls))
|
|
for _, tblName := range as.SchemaConflictsTables {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, schemaConflictLabel, tblName))
|
|
}
|
|
for _, tblName := range as.DataConflictTables {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, bothModifiedLabel, tblName))
|
|
}
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
}
|
|
|
|
if as.HasConstraintViolations() {
|
|
violationOnly, _, _ := violationSet.LeftIntersectionRight(inCnfSet)
|
|
lines := make([]string, 0, len(notStagedTbls))
|
|
for _, tblName := range violationOnly.AsSortedSlice() {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, "modified", tblName))
|
|
}
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
}
|
|
}
|
|
|
|
added := 0
|
|
removeModified := 0
|
|
for _, td := range notStagedTbls {
|
|
if td.IsAdd() {
|
|
added++
|
|
} else if td.IsRename() {
|
|
added++
|
|
removeModified++
|
|
} else {
|
|
removeModified++
|
|
}
|
|
}
|
|
|
|
numRemovedOrModified := removeModified
|
|
|
|
if numRemovedOrModified-inCnfSet.Size()-violationSet.Size() > 0 {
|
|
if linesPrinted > 0 {
|
|
cli.Println()
|
|
}
|
|
|
|
iohelp.WriteLine(wr, workingHeader)
|
|
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, workingHeaderHelp)
|
|
}
|
|
|
|
lines := getModifiedAndRemovedNotStaged(notStagedTbls, inCnfSet, violationSet)
|
|
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
}
|
|
|
|
if added > 0 {
|
|
if linesPrinted > 0 {
|
|
cli.Println()
|
|
}
|
|
|
|
iohelp.WriteLine(wr, untrackedHeader)
|
|
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, untrackedHeaderHelp)
|
|
}
|
|
|
|
addedNotStagedTables := getAddedNotStagedTables(notStagedTbls)
|
|
filteredTables, err := doltdb.FilterIgnoredTables(ctx, addedNotStagedTables, roots)
|
|
if err != nil && doltdb.AsDoltIgnoreInConflict(err) == nil {
|
|
return 0, err
|
|
}
|
|
|
|
lines := make([]string, len(filteredTables.DontIgnore))
|
|
for i, tableName := range filteredTables.DontIgnore {
|
|
lines[i] = fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.AddedTable], tableName)
|
|
}
|
|
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
|
|
if printIgnored && len(filteredTables.Ignore) > 0 {
|
|
if linesPrinted > 0 {
|
|
cli.Println()
|
|
}
|
|
|
|
iohelp.WriteLine(wr, ignoredHeader)
|
|
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, ignoredHeaderHelp)
|
|
}
|
|
|
|
lines := make([]string, len(filteredTables.Ignore))
|
|
for i, tableName := range filteredTables.Ignore {
|
|
lines[i] = fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.AddedTable], tableName)
|
|
}
|
|
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
}
|
|
|
|
if len(filteredTables.Conflicts) > 0 {
|
|
if linesPrinted > 0 {
|
|
cli.Println()
|
|
}
|
|
|
|
iohelp.WriteLine(wr, conflictedIgnoredHeader)
|
|
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, conflictedIgnoredHeaderHelp)
|
|
}
|
|
|
|
lines := make([]string, len(filteredTables.Conflicts))
|
|
for i, conflict := range filteredTables.Conflicts {
|
|
lines[i] = fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.AddedTable], conflict.Table)
|
|
}
|
|
|
|
iohelp.WriteLine(wr, color.RedString(strings.Join(lines, "\n")))
|
|
linesPrinted += len(lines)
|
|
}
|
|
}
|
|
|
|
return linesPrinted, nil
|
|
}
|
|
|
|
func getModifiedAndRemovedNotStaged(notStagedTbls []diff.TableDelta, inCnfSet, violationSet *set.StrSet) (lines []string) {
|
|
lines = make([]string, 0, len(notStagedTbls))
|
|
for _, td := range notStagedTbls {
|
|
if td.IsAdd() || inCnfSet.Contains(td.CurName()) || violationSet.Contains(td.CurName()) {
|
|
continue
|
|
}
|
|
if td.IsDrop() {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.RemovedTable], td.CurName()))
|
|
} else if td.IsRename() {
|
|
// per Git, unstaged renames are shown as drop + add
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.RemovedTable], td.FromName))
|
|
} else {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.ModifiedTable], td.CurName()))
|
|
}
|
|
}
|
|
return lines
|
|
}
|
|
|
|
func getAddedNotStagedTables(notStagedTbls []diff.TableDelta) (tables []string) {
|
|
tables = make([]string, 0, len(notStagedTbls))
|
|
for _, td := range notStagedTbls {
|
|
if td.IsAdd() || td.IsRename() {
|
|
tables = append(tables, td.CurName())
|
|
}
|
|
}
|
|
return tables
|
|
}
|
|
|
|
const (
|
|
branchHeader = "On branch %s\n"
|
|
stagedHeader = `Changes to be committed:`
|
|
stagedHeaderHelp = ` (use "dolt reset <table>..." to unstage)`
|
|
|
|
unmergedTablesHeader = `You have unmerged tables.
|
|
(fix %s and run "dolt commit")
|
|
(use "dolt merge --abort" to abort the merge)
|
|
`
|
|
|
|
allMergedHeader = `All conflicts and constraint violations fixed but you are still merging.
|
|
(use "dolt commit" to conclude merge)`
|
|
|
|
unmergedPathsHeader = `Unmerged paths:`
|
|
mergedTableHelp = ` (use "dolt add <table>..." to mark resolution)`
|
|
|
|
workingHeader = `Changes not staged for commit:`
|
|
workingHeaderHelp = ` (use "dolt add <table>" to update what will be committed)
|
|
(use "dolt checkout <table>" to discard changes in working directory)`
|
|
|
|
untrackedHeader = `Untracked tables:`
|
|
untrackedHeaderHelp = ` (use "dolt add <table>" to include in what will be committed)`
|
|
|
|
ignoredHeader = `Ignored tables:`
|
|
ignoredHeaderHelp = ` (use "dolt add -f <table>" to include in what will be committed)`
|
|
|
|
conflictedIgnoredHeader = `Tables with conflicting dolt_ignore patterns:`
|
|
conflictedIgnoredHeaderHelp = ` (use "dolt add -f <table>" to include in what will be committed)`
|
|
|
|
statusFmt = "\t%-18s%s"
|
|
statusRenameFmt = "\t%-18s%s -> %s"
|
|
schemaConflictLabel = "schema conflict:"
|
|
bothModifiedLabel = "both modified:"
|
|
)
|
|
|
|
var tblDiffTypeToLabel = map[diff.TableDiffType]string{
|
|
diff.ModifiedTable: "modified:",
|
|
diff.RenamedTable: "renamed:",
|
|
diff.RemovedTable: "deleted:",
|
|
diff.AddedTable: "new table:",
|
|
}
|
|
|
|
func printStagedDiffs(wr io.Writer, stagedTbls []diff.TableDelta, printHelp bool) int {
|
|
if len(stagedTbls) > 0 {
|
|
iohelp.WriteLine(wr, stagedHeader)
|
|
|
|
if printHelp {
|
|
iohelp.WriteLine(wr, stagedHeaderHelp)
|
|
}
|
|
|
|
lines := make([]string, 0, len(stagedTbls))
|
|
for _, td := range stagedTbls {
|
|
if !doltdb.IsReadOnlySystemTable(td.CurName()) {
|
|
if td.IsAdd() {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.AddedTable], td.CurName()))
|
|
} else if td.IsDrop() {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.RemovedTable], td.CurName()))
|
|
} else if td.IsRename() {
|
|
lines = append(lines, fmt.Sprintf(statusRenameFmt, tblDiffTypeToLabel[diff.RenamedTable], td.FromName, td.ToName))
|
|
} else {
|
|
lines = append(lines, fmt.Sprintf(statusFmt, tblDiffTypeToLabel[diff.ModifiedTable], td.CurName()))
|
|
}
|
|
|
|
}
|
|
}
|
|
iohelp.WriteLine(wr, color.GreenString(strings.Join(lines, "\n")))
|
|
return len(stagedTbls)
|
|
}
|
|
|
|
return 0
|
|
}
|