From adcf3c10f3a409a99b6453bc879d6f7cb327f1b7 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Fri, 17 Nov 2023 14:24:02 -0800 Subject: [PATCH 01/11] tests for dolt reflog --- integration-tests/bats/reflog.bats | 215 ++++++++++++++++++++++--- integration-tests/bats/sql-reflog.bats | 57 +++++++ 2 files changed, 252 insertions(+), 20 deletions(-) mode change 100644 => 100755 integration-tests/bats/reflog.bats create mode 100644 integration-tests/bats/sql-reflog.bats diff --git a/integration-tests/bats/reflog.bats b/integration-tests/bats/reflog.bats old mode 100644 new mode 100755 index 38d5a7e34e..d8906f4794 --- a/integration-tests/bats/reflog.bats +++ b/integration-tests/bats/reflog.bats @@ -1,45 +1,32 @@ #!/usr/bin/env bats load $BATS_TEST_DIRNAME/helper/common.bash +setup() { + setup_common +} + teardown() { assert_feature_version teardown_common } -# Asserts that when DOLT_DISABLE_REFLOG is set, the dolt_reflog() table -# function returns an empty result set with no error. +# Asserts that when DOLT_DISABLE_REFLOG is set, dolt reflog returns nothing with no error. @test "reflog: disabled with DOLT_DISABLE_REFLOG" { export DOLT_DISABLE_REFLOG=true - setup_common dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" dolt commit --allow-empty -m "test commit 1" - run dolt sql -q "select * from dolt_reflog();" + run dolt reflog [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } -# Sanity check for the most basic case of querying the Dolt reflog -@test "reflog: enabled by default" { - setup_common - dolt sql -q "create table t (i int primary key, j int);" - dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; - dolt commit -Am "initial commit" - - run dolt sql -q "select * from dolt_reflog();" - [ "$status" -eq 0 ] - [ "${#lines[@]}" -eq 6 ] - [[ "$output" =~ "initial commit" ]] || false - [[ "$output" =~ "Initialize data repository" ]] || false -} - # Asserts that when DOLT_REFLOG_RECORD_LIMIT has been set, the reflog only contains the # most recent entries and is limited by the env var's value. @test "reflog: set DOLT_REFLOG_RECORD_LIMIT" { export DOLT_REFLOG_RECORD_LIMIT=2 - setup_common dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -47,10 +34,198 @@ teardown() { dolt commit --allow-empty -m "test commit 2" # Only the most recent two ref changes should appear in the log - run dolt sql -q "select * from dolt_reflog();" + run dolt reflog [ "$status" -eq 0 ] [[ "$output" =~ "test commit 1" ]] || false [[ "$output" =~ "test commit 2" ]] || false [[ ! "$output" =~ "initial commit" ]] || false [[ ! "$output" =~ "Initialize data repository" ]] || false } + +@test "reflog: simple reflog" { + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) HEAD@{0}: initial commit" ]] || false + [[ "$out" =~ "HEAD@{1}: Initialize data repository" ]] || false +} + +@test "reflog: reflog with ref given" { + dolt sql < main) HEAD@{0}: inserting row 1" ]] || false + [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false + [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + + # ref is case-insensitive + run dolt reflog rEFs/heAdS/MAIN + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false + [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false + [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + + run dolt reflog main + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false + [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false + [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + + # ref is case-insensitive + run dolt reflog MaIn + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false + [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false + [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + + run dolt reflog refs/heads/branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false + [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + + run dolt reflog branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false + [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + + run dolt reflog refs/tags/tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + + # ref is case-insensitive + run dolt reflog Refs/tAGs/TaG1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + + run dolt reflog tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + + # ref is case-insensitive + run dolt reflog TAg1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + + dolt branch -D branch1 + + run dolt reflog branch1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 3 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false + [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + + dolt tag -d tag1 + run dolt reflog tag1 + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false +} + +@test "reflog: garbage collection with no newgen data" { + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 1 ] + out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$out" =~ "(HEAD -> main) HEAD@{0}: Initialize data repository" ]] || false + + dolt gc + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "reflog: garbage collection with newgen data" { + dolt sql < main) HEAD@{0}: inserting row 2" ]] || false + [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "HEAD@{2}: creating table t1" ]] || false + [[ "$out" =~ "HEAD@{3}: Initialize data repository" ]] || false + + dolt gc + + run dolt reflog main + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +@test "reflog: too many arguments given" { + run dolt reflog foo bar + [ "$status" -eq 1 ] + [[ "$output" =~ "error: reflog has too many positional arguments" ]] +} + +@test "reflog: unknown ref returns nothing" { + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog foo + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} diff --git a/integration-tests/bats/sql-reflog.bats b/integration-tests/bats/sql-reflog.bats new file mode 100644 index 0000000000..bac628f016 --- /dev/null +++ b/integration-tests/bats/sql-reflog.bats @@ -0,0 +1,57 @@ +#!/usr/bin/env bats +load $BATS_TEST_DIRNAME/helper/common.bash + +setup() { + setup_common +} + +teardown() { + assert_feature_version + teardown_common +} + +# Asserts that when DOLT_DISABLE_REFLOG is set, the dolt_reflog() table +# function returns an empty result set with no error. +@test "sql-reflog: disabled with DOLT_DISABLE_REFLOG" { + export DOLT_DISABLE_REFLOG=true + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + dolt commit --allow-empty -m "test commit 1" + + run dolt sql -q "select * from dolt_reflog();" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 0 ] +} + +# Sanity check for the most basic case of querying the Dolt reflog +@test "sql-reflog: enabled by default" { + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt sql -q "select * from dolt_reflog();" + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 6 ] + [[ "$output" =~ "initial commit" ]] || false + [[ "$output" =~ "Initialize data repository" ]] || false +} + +# Asserts that when DOLT_REFLOG_RECORD_LIMIT has been set, the reflog only contains the +# most recent entries and is limited by the env var's value. +@test "sql-reflog: set DOLT_REFLOG_RECORD_LIMIT" { + export DOLT_REFLOG_RECORD_LIMIT=2 + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + dolt commit --allow-empty -m "test commit 1" + dolt commit --allow-empty -m "test commit 2" + + # Only the most recent two ref changes should appear in the log + run dolt sql -q "select * from dolt_reflog();" + [ "$status" -eq 0 ] + [[ "$output" =~ "test commit 1" ]] || false + [[ "$output" =~ "test commit 2" ]] || false + [[ ! "$output" =~ "initial commit" ]] || false + [[ ! "$output" =~ "Initialize data repository" ]] || false +} From fccec02c72f4135f39abe541bfb7feb17900fe3d Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Mon, 20 Nov 2023 16:06:35 -0800 Subject: [PATCH 02/11] basic dolt reflog implementation --- go/cmd/dolt/cli/arg_parser_helpers.go | 5 + go/cmd/dolt/commands/reflog.go | 189 ++++++++++++++++++ go/cmd/dolt/dolt.go | 1 + .../eventsapi/v1alpha1/event_constants.pb.go | 1 + .../eventsapi/v1alpha1/event_constants.proto | 1 + 5 files changed, 197 insertions(+) create mode 100644 go/cmd/dolt/commands/reflog.go diff --git a/go/cmd/dolt/cli/arg_parser_helpers.go b/go/cmd/dolt/cli/arg_parser_helpers.go index 9848beb391..2d15db07ee 100644 --- a/go/cmd/dolt/cli/arg_parser_helpers.go +++ b/go/cmd/dolt/cli/arg_parser_helpers.go @@ -280,6 +280,11 @@ func CreateCountCommitsArgParser() *argparser.ArgParser { return ap } +func CreateReflogArgParser() *argparser.ArgParser { + ap := argparser.NewArgParserWithMaxArgs("reflog", 1) + return ap +} + func CreateGlobalArgParser(name string) *argparser.ArgParser { ap := argparser.NewArgParserWithVariableArgs(name) if name == "dolt" { diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go new file mode 100644 index 0000000000..9c633394e7 --- /dev/null +++ b/go/cmd/dolt/commands/reflog.go @@ -0,0 +1,189 @@ +// Copyright 2023 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 ( + "context" + "fmt" + "strings" + + "github.com/dolthub/dolt/go/cmd/dolt/cli" + "github.com/dolthub/dolt/go/cmd/dolt/errhand" + eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" + "github.com/dolthub/dolt/go/libraries/doltcore/env" + "github.com/dolthub/dolt/go/libraries/utils/argparser" + "github.com/dolthub/dolt/go/store/util/outputpager" + "github.com/dolthub/go-mysql-server/sql" + "github.com/gocraft/dbr/v2" + "github.com/gocraft/dbr/v2/dialect" +) + +var reflogDocs = cli.CommandDocumentationContent{ + ShortDesc: "Shows a history of named refs", + LongDesc: `Shows the history of named refs (e.g. branches and tags), which is useful for understanding how a branch +or tag changed over time to reference different commits, particularly for information not surfaced through {{.EmphasisLeft}}dolt log{{.EmphasisRight}}. +The data from Dolt's reflog comes from [Dolt's journaling chunk store](https://www.dolthub.com/blog/2023-03-08-dolt-chunk-journal/). +This data is local to a Dolt database and never included when pushing, pulling, or cloning a Dolt database. This means when you clone a Dolt database, it will not have any reflog data until you perform operations that change what commit branches or tags reference. + +Dolt's reflog is similar to [Git's reflog](https://git-scm.com/docs/git-reflog), but there are a few differences: +- The Dolt reflog currently only supports named references, such as branches and tags, and not any of Git's special refs (e.g. {{.EmphasisLeft}}HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}FETCH-HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}MERGE-HEAD{{.EmphasisRight}}). +- The Dolt reflog can be queried for the log of references, even after a reference has been deleted. In Git, once a branch or tag is deleted, the reflog for that ref is also deleted and to find the last commit a branch or tag pointed to you have to use Git's special {{.EmphasisLeft}}HEAD{{.EmphasisRight}} reflog to find the commit, which can sometimes be challenging. Dolt makes this much easier by allowing you to see the history for a deleted ref so you can easily see the last commit a branch or tag pointed to before it was deleted.`, + Synopsis: []string{ + `{{.LessThan}}ref{{.GreaterThan}}`, + }, +} + +type ReflogCmd 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 ReflogCmd) Name() string { + return "reflog" +} + +// Description returns a description of the command +func (cmd ReflogCmd) Description() string { + return "Show history of named refs." +} + +// EventType returns the type of the event to log +func (cmd ReflogCmd) EventType() eventsapi.ClientEventType { + return eventsapi.ClientEventType_REFLOG +} + +func (cmd ReflogCmd) Docs() *cli.CommandDocumentation { + ap := cmd.ArgParser() + return cli.NewCommandDocumentation(reflogDocs, ap) +} + +func (cmd ReflogCmd) ArgParser() *argparser.ArgParser { + return cli.CreateReflogArgParser() +} + +func (cmd ReflogCmd) RequiresRepo() bool { + return false +} + +// Exec executes the command +func (cmd ReflogCmd) Exec(ctx context.Context, commandStr string, args []string, dEnv *env.DoltEnv, cliCtx cli.CliContext) int { + ap := cmd.ArgParser() + help, usage := cli.HelpAndUsagePrinters(cli.CommandDocsForCommandString(commandStr, reflogDocs, ap)) + apr := cli.ParseArgsOrDie(ap, args, help) + + queryist, sqlCtx, closeFunc, err := cliCtx.QueryEngine(ctx) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + if closeFunc != nil { + defer closeFunc() + } + + query, err := constructInterpolatedDoltReflogQuery(apr) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + rows, err := GetRowsForSql(queryist, sqlCtx, query) + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), usage) + } + + return printReflog(rows, queryist, sqlCtx) +} + +// constructInterpolatedDoltReflogQuery generates the sql query necessary to call the DOLT_REFLOG() function. +// Also interpolates this query to prevent sql injection +func constructInterpolatedDoltReflogQuery(apr *argparser.ArgParseResults) (string, error) { + var params []interface{} + refPlaceholder := "" + if apr.NArg() == 1 { + params = append(params, apr.Arg(0)) + refPlaceholder = "?" + } + + query := strings.Join([]string{"SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(", refPlaceholder, ")"}, "") + interpolatedQuery, err := dbr.InterpolateForDialect(query, params, dialect.MySQL) + if err != nil { + return "", err + } + + return interpolatedQuery, nil +} + +type ReflogInfo struct { + ref string + commitHash string + commitMessage string +} + +// printReflog takes a list of sql rows with columns ref, commit hash, commit message. Prints the reflog to stdout +func printReflog(rows []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) int { + var reflogInfo []ReflogInfo + + // Get the current branch + res, err := GetRowsForSql(queryist, sqlCtx, "SELECT active_branch()") + if err != nil { + return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), nil) + } + curBranch := res[0][0].(string) + + for _, row := range rows { + ref := row[0].(string) + commitHash := row[1].(string) + commitMessage := row[2].(string) + reflogInfo = append(reflogInfo, ReflogInfo{ref, commitHash, commitMessage}) + } + + reflogToStdOut(reflogInfo, curBranch) + + return 0 +} + +// reflogToStdOut takes a list of ReflogInfo and prints the reflog to stdout +func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { + if cli.ExecuteWithStdioRestored == nil { + return + } + cli.ExecuteWithStdioRestored(func() { + pager := outputpager.Start() + defer pager.Stop() + + for pos, info := range reflogInfo { + // TODO: use short hash instead + line := []string{fmt.Sprintf("\033[33m%s\033[0m", info.commitHash)} // commit hash in yellow (33m) + + processedRef := processRefForReflog(info.ref, curBranch) + line = append(line, fmt.Sprintf("\033[33m(%s\033[33m)\033[0m", processedRef)) // () in yellow (33m) + line = append(line, fmt.Sprintf("HEAD@{%d}: %s\n", pos, info.commitMessage)) + pager.Writer.Write([]byte(strings.Join(line, " "))) + } + }) +} + +// processRefForReflog takes a full ref (e.g. refs/heads/master) or tag name and returns the ref name (e.g. master) with relevant decoration. +func processRefForReflog(fullRef string, curBranch string) string { + if strings.HasPrefix(fullRef, "refs/heads/") { + branch := strings.TrimPrefix(fullRef, "refs/heads/") + if branch == curBranch { + return fmt.Sprintf("\033[36;1mHEAD -> \033[32;1m%s\033[0m", branch) // HEAD in cyan (36;1), branch in green (32;1m) + } + return fmt.Sprintf("\033[32;1m%s\033[0m", branch) // branch in green (32;1m) + } else if strings.HasPrefix(fullRef, "refs/tags/") { + return fmt.Sprintf("\033[33mtag: %s\033[0m", strings.TrimPrefix(fullRef, "refs/tags/")) // tag in yellow (33m) + } else if strings.HasPrefix(fullRef, "refs/remotes/") { + return fmt.Sprintf("\033[31;1m%s\033[0m", strings.TrimPrefix(fullRef, "refs/remotes/")) // remote in red (31;1m) + } else { + return fullRef + } +} diff --git a/go/cmd/dolt/dolt.go b/go/cmd/dolt/dolt.go index 41b3ef5b28..b4db7c462b 100644 --- a/go/cmd/dolt/dolt.go +++ b/go/cmd/dolt/dolt.go @@ -123,6 +123,7 @@ var doltSubCommands = []cli.Command{ &commands.Assist{}, commands.ProfileCmd{}, commands.QueryDiff{}, + commands.ReflogCmd{}, } var commandsWithoutCliCtx = []cli.Command{ diff --git a/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go b/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go index 188068cfc1..c561933fea 100644 --- a/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go +++ b/go/gen/proto/dolt/services/eventsapi/v1alpha1/event_constants.pb.go @@ -155,6 +155,7 @@ const ( ClientEventType_STASH_POP ClientEventType = 60 ClientEventType_SHOW ClientEventType = 61 ClientEventType_PROFILE ClientEventType = 62 + ClientEventType_REFLOG ClientEventType = 63 ) // Enum value maps for ClientEventType. diff --git a/proto/dolt/services/eventsapi/v1alpha1/event_constants.proto b/proto/dolt/services/eventsapi/v1alpha1/event_constants.proto index 51e6db7703..d8d2088b29 100644 --- a/proto/dolt/services/eventsapi/v1alpha1/event_constants.proto +++ b/proto/dolt/services/eventsapi/v1alpha1/event_constants.proto @@ -92,6 +92,7 @@ enum ClientEventType { STASH_POP = 60; SHOW = 61; PROFILE = 62; + REFLOG = 63; } enum MetricID { From d3e7c7dfb310d6aaefbf9d8140c203db5b4efb8c Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Tue, 21 Nov 2023 10:44:03 -0800 Subject: [PATCH 03/11] add --all for reflog --- go/cmd/dolt/cli/arg_parser_helpers.go | 1 + go/cmd/dolt/commands/reflog.go | 18 +++++- .../doltcore/sqle/reflog_table_function.go | 56 +++++++++---------- 3 files changed, 44 insertions(+), 31 deletions(-) diff --git a/go/cmd/dolt/cli/arg_parser_helpers.go b/go/cmd/dolt/cli/arg_parser_helpers.go index 2d15db07ee..83e87cbfe3 100644 --- a/go/cmd/dolt/cli/arg_parser_helpers.go +++ b/go/cmd/dolt/cli/arg_parser_helpers.go @@ -282,6 +282,7 @@ func CreateCountCommitsArgParser() *argparser.ArgParser { func CreateReflogArgParser() *argparser.ArgParser { ap := argparser.NewArgParserWithMaxArgs("reflog", 1) + ap.SupportsFlag(AllFlag, "", "Show all refs") return ap } diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go index 9c633394e7..3e49ed0ec2 100644 --- a/go/cmd/dolt/commands/reflog.go +++ b/go/cmd/dolt/commands/reflog.go @@ -107,12 +107,26 @@ func (cmd ReflogCmd) Exec(ctx context.Context, commandStr string, args []string, func constructInterpolatedDoltReflogQuery(apr *argparser.ArgParseResults) (string, error) { var params []interface{} refPlaceholder := "" + allFlag := "" + if apr.NArg() == 1 { params = append(params, apr.Arg(0)) refPlaceholder = "?" } + if apr.Contains(cli.AllFlag) { + allFlag = "'--all'" + } + + args := "" + if refPlaceholder == "" && allFlag != "" { + args = allFlag + } else if refPlaceholder != "" && allFlag == "" { + args = refPlaceholder + } else if refPlaceholder != "" && allFlag != "" { + args = strings.Join([]string{refPlaceholder, allFlag}, ", ") + } - query := strings.Join([]string{"SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(", refPlaceholder, ")"}, "") + query := strings.Join([]string{"SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(", args, ")"}, "") interpolatedQuery, err := dbr.InterpolateForDialect(query, params, dialect.MySQL) if err != nil { return "", err @@ -183,6 +197,8 @@ func processRefForReflog(fullRef string, curBranch string) string { return fmt.Sprintf("\033[33mtag: %s\033[0m", strings.TrimPrefix(fullRef, "refs/tags/")) // tag in yellow (33m) } else if strings.HasPrefix(fullRef, "refs/remotes/") { return fmt.Sprintf("\033[31;1m%s\033[0m", strings.TrimPrefix(fullRef, "refs/remotes/")) // remote in red (31;1m) + } else if strings.HasPrefix(fullRef, "refs/workspaces/") { + return fmt.Sprintf("\033[35;1mworkspace: %s\033[0m", strings.TrimPrefix(fullRef, "refs/workspaces/")) // workspace in magenta (35;1m) } else { return fullRef } diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index 64b23a4de4..9c6edfb9e1 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -20,6 +20,7 @@ import ( "strings" "time" + "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/go-mysql-server/sql/types" @@ -31,7 +32,8 @@ import ( type ReflogTableFunction struct { ctx *sql.Context database sql.Database - refExpr sql.Expression + refName string + showAll bool } var _ sql.TableFunction = (*ReflogTableFunction)(nil) @@ -64,20 +66,6 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row return nil, fmt.Errorf("unexpected database type: %T", rltf.database) } - var refName string - if rltf.refExpr != nil { - target, err := rltf.refExpr.Eval(ctx, row) - if err != nil { - return nil, fmt.Errorf("error evaluating expression (%s): %s", - rltf.refExpr.String(), err.Error()) - } - - refName, ok = target.(string) - if !ok { - return nil, fmt.Errorf("argument (%v) is not a string value, but a %T", target, target) - } - } - ddb := sqlDb.DbData().Ddb journal := ddb.ChunkJournal() if journal == nil { @@ -109,17 +97,23 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row if doltRef.GetType() == ref.InternalRefType { return nil } + // skip workspace refs by default + if doltRef.GetType() == ref.WorkspaceRefType { + if !rltf.showAll { + return nil + } + } // If a ref expression to filter on was specified, see if we match the current ref - if rltf.refExpr != nil { + if rltf.refName != "" { // If the caller has supplied a branch or tag name, without the fully qualified ref path, // take the first match and use that as the canonical ref to filter on - if strings.HasSuffix(strings.ToLower(id), "/"+strings.ToLower(refName)) { - refName = id + if strings.HasSuffix(strings.ToLower(id), "/"+strings.ToLower(rltf.refName)) { + rltf.refName = id } // Skip refs that don't match the target we're looking for - if strings.ToLower(id) != strings.ToLower(refName) { + if strings.ToLower(id) != strings.ToLower(rltf.refName) { return nil } } @@ -172,14 +166,11 @@ func (rltf *ReflogTableFunction) Schema() sql.Schema { } func (rltf *ReflogTableFunction) Resolved() bool { - if rltf.refExpr != nil { - return rltf.refExpr.Resolved() - } return true } func (rltf *ReflogTableFunction) String() string { - return fmt.Sprintf("DOLT_REFLOG(%s)", rltf.refExpr.String()) + return fmt.Sprintf("DOLT_REFLOG(%s)", rltf.refName) } func (rltf *ReflogTableFunction) Children() []sql.Node { @@ -204,20 +195,25 @@ func (rltf *ReflogTableFunction) IsReadOnly() bool { } func (rltf *ReflogTableFunction) Expressions() []sql.Expression { - if rltf.refExpr != nil { - return []sql.Expression{rltf.refExpr} - } return []sql.Expression{} } func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) (sql.Node, error) { - if len(expression) > 1 { - return nil, sql.ErrInvalidArgumentNumber.New(rltf.Name(), "0 or 1", len(expression)) + if len(expression) > 2 { + return nil, sql.ErrInvalidArgumentNumber.New(rltf.Name(), "0 to 2", len(expression)) } new := *rltf - if len(expression) > 0 { - new.refExpr = expression[0] + args, err := getDoltArgs(rltf.ctx, expression, rltf.Name()) + if err != nil { + return nil, err + } + apr, err := cli.CreateReflogArgParser().Parse(args) + if apr.NArg() > 0 { + new.refName = apr.Arg(0) + } + if apr.Contains(cli.AllFlag) { + new.showAll = true } return &new, nil } From 37158e01a6b071d80c29e5699e35c6fa00276e8e Mon Sep 17 00:00:00 2001 From: stephkyou Date: Tue, 21 Nov 2023 21:04:50 +0000 Subject: [PATCH 04/11] [ga-format-pr] Run go/utils/repofmt/format_repo.sh and go/Godeps/update.sh --- go/cmd/dolt/commands/reflog.go | 7 ++++--- go/libraries/doltcore/sqle/reflog_table_function.go | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go index 3e49ed0ec2..14aa3bc71b 100644 --- a/go/cmd/dolt/commands/reflog.go +++ b/go/cmd/dolt/commands/reflog.go @@ -19,15 +19,16 @@ import ( "fmt" "strings" + "github.com/dolthub/go-mysql-server/sql" + "github.com/gocraft/dbr/v2" + "github.com/gocraft/dbr/v2/dialect" + "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/dolt/go/cmd/dolt/errhand" eventsapi "github.com/dolthub/dolt/go/gen/proto/dolt/services/eventsapi/v1alpha1" "github.com/dolthub/dolt/go/libraries/doltcore/env" "github.com/dolthub/dolt/go/libraries/utils/argparser" "github.com/dolthub/dolt/go/store/util/outputpager" - "github.com/dolthub/go-mysql-server/sql" - "github.com/gocraft/dbr/v2" - "github.com/gocraft/dbr/v2/dialect" ) var reflogDocs = cli.CommandDocumentationContent{ diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index 9c6edfb9e1..63c563727f 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -20,10 +20,10 @@ import ( "strings" "time" - "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/go-mysql-server/sql/types" + "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/store/hash" From 6ba1d09cd893d050a1ed826dd4c60e168d0bd179 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Tue, 21 Nov 2023 13:45:35 -0800 Subject: [PATCH 05/11] change error test output --- go/libraries/doltcore/sqle/enginetest/dolt_queries.go | 6 +++--- go/libraries/doltcore/sqle/reflog_table_function.go | 3 +++ 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 91e97f461d..206c9318ed 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -4317,15 +4317,15 @@ var DoltReflogTestScripts = []queries.ScriptTest{ Assertions: []queries.ScriptTestAssertion{ { Query: "select * from dolt_reflog('foo', 'bar');", - ExpectedErrStr: "function 'dolt_reflog' expected 0 or 1 arguments, 2 received", + ExpectedErrStr: "error: reflog has too many positional arguments. Expected at most 1, found 2: foo, bar", }, { Query: "select * from dolt_reflog(NULL);", - ExpectedErrStr: "argument () is not a string value, but a ", + ExpectedErrStr: "Invalid argument to dolt_reflog: NULL", }, { Query: "select * from dolt_reflog(-100);", - ExpectedErrStr: "argument (-100) is not a string value, but a int8", + ExpectedErrStr: "Invalid argument to dolt_reflog: -100", }, }, }, diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index 63c563727f..a8eb79ed8e 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -209,6 +209,9 @@ func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) ( return nil, err } apr, err := cli.CreateReflogArgParser().Parse(args) + if err != nil { + return nil, err + } if apr.NArg() > 0 { new.refName = apr.Arg(0) } From 3e5c6b6bb7365a2d3168c6d14cd6081c49d03ef2 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Tue, 21 Nov 2023 14:20:08 -0800 Subject: [PATCH 06/11] minor test updates --- integration-tests/bats/helper/local-remote.bash | 2 ++ integration-tests/bats/reflog.bats | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/integration-tests/bats/helper/local-remote.bash b/integration-tests/bats/helper/local-remote.bash index f478ffa6b9..e799355407 100644 --- a/integration-tests/bats/helper/local-remote.bash +++ b/integration-tests/bats/helper/local-remote.bash @@ -135,6 +135,8 @@ SKIP_SERVER_TESTS=$(cat <<-EOM ~cli-hosted.bats~ ~profile.bats~ ~ls.bats~ +~reflog.bats~ +~sql-reflog.bats~ EOM ) diff --git a/integration-tests/bats/reflog.bats b/integration-tests/bats/reflog.bats index d8906f4794..bad3273d50 100755 --- a/integration-tests/bats/reflog.bats +++ b/integration-tests/bats/reflog.bats @@ -217,7 +217,7 @@ SQL @test "reflog: too many arguments given" { run dolt reflog foo bar [ "$status" -eq 1 ] - [[ "$output" =~ "error: reflog has too many positional arguments" ]] + [[ "$output" =~ "error: reflog has too many positional arguments" ]] || false } @test "reflog: unknown ref returns nothing" { From 5d24edd9eff559bfde9227d729547a3f8c1ad682 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Wed, 29 Nov 2023 18:43:36 -0800 Subject: [PATCH 07/11] address PR comments --- go/cmd/dolt/commands/reflog.go | 31 +++--- .../doltcore/sqle/enginetest/dolt_queries.go | 6 +- .../doltcore/sqle/reflog_table_function.go | 67 +++++++++---- .../bats/helper/local-remote.bash | 1 - integration-tests/bats/reflog.bats | 96 +++++++++++-------- integration-tests/bats/sql-local-remote.bats | 24 +++++ integration-tests/bats/sql-reflog.bats | 10 +- 7 files changed, 146 insertions(+), 89 deletions(-) diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go index 14aa3bc71b..bc1b85c685 100644 --- a/go/cmd/dolt/commands/reflog.go +++ b/go/cmd/dolt/commands/reflog.go @@ -107,27 +107,17 @@ func (cmd ReflogCmd) Exec(ctx context.Context, commandStr string, args []string, // Also interpolates this query to prevent sql injection func constructInterpolatedDoltReflogQuery(apr *argparser.ArgParseResults) (string, error) { var params []interface{} - refPlaceholder := "" - allFlag := "" + var args []string if apr.NArg() == 1 { params = append(params, apr.Arg(0)) - refPlaceholder = "?" + args = append(args, "?") } if apr.Contains(cli.AllFlag) { - allFlag = "'--all'" + args = append(args, "'--all'") } - args := "" - if refPlaceholder == "" && allFlag != "" { - args = allFlag - } else if refPlaceholder != "" && allFlag == "" { - args = refPlaceholder - } else if refPlaceholder != "" && allFlag != "" { - args = strings.Join([]string{refPlaceholder, allFlag}, ", ") - } - - query := strings.Join([]string{"SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(", args, ")"}, "") + query := fmt.Sprintf("SELECT ref, commit_hash, commit_message FROM DOLT_REFLOG(%s)", strings.Join(args, ", ")) interpolatedQuery, err := dbr.InterpolateForDialect(query, params, dialect.MySQL) if err != nil { return "", err @@ -147,11 +137,12 @@ func printReflog(rows []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) int var reflogInfo []ReflogInfo // Get the current branch + curBranch := "" res, err := GetRowsForSql(queryist, sqlCtx, "SELECT active_branch()") - if err != nil { - return HandleVErrAndExitCode(errhand.VerboseErrorFromError(err), nil) + if err == nil { + // still print the reflog even if we can't get the current branch + curBranch = res[0][0].(string) } - curBranch := res[0][0].(string) for _, row := range rows { ref := row[0].(string) @@ -174,13 +165,13 @@ func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { pager := outputpager.Start() defer pager.Stop() - for pos, info := range reflogInfo { + for _, info := range reflogInfo { // TODO: use short hash instead line := []string{fmt.Sprintf("\033[33m%s\033[0m", info.commitHash)} // commit hash in yellow (33m) processedRef := processRefForReflog(info.ref, curBranch) line = append(line, fmt.Sprintf("\033[33m(%s\033[33m)\033[0m", processedRef)) // () in yellow (33m) - line = append(line, fmt.Sprintf("HEAD@{%d}: %s\n", pos, info.commitMessage)) + line = append(line, fmt.Sprintf("%s\n", info.commitMessage)) pager.Writer.Write([]byte(strings.Join(line, " "))) } }) @@ -190,7 +181,7 @@ func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { func processRefForReflog(fullRef string, curBranch string) string { if strings.HasPrefix(fullRef, "refs/heads/") { branch := strings.TrimPrefix(fullRef, "refs/heads/") - if branch == curBranch { + if curBranch != "" && branch == curBranch { return fmt.Sprintf("\033[36;1mHEAD -> \033[32;1m%s\033[0m", branch) // HEAD in cyan (36;1), branch in green (32;1m) } return fmt.Sprintf("\033[32;1m%s\033[0m", branch) // branch in green (32;1m) diff --git a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go index 206c9318ed..57e1d2ca70 100644 --- a/go/libraries/doltcore/sqle/enginetest/dolt_queries.go +++ b/go/libraries/doltcore/sqle/enginetest/dolt_queries.go @@ -4317,15 +4317,15 @@ var DoltReflogTestScripts = []queries.ScriptTest{ Assertions: []queries.ScriptTestAssertion{ { Query: "select * from dolt_reflog('foo', 'bar');", - ExpectedErrStr: "error: reflog has too many positional arguments. Expected at most 1, found 2: foo, bar", + ExpectedErrStr: "error: dolt_reflog has too many positional arguments. Expected at most 1, found 2: ['foo' 'bar']", }, { Query: "select * from dolt_reflog(NULL);", - ExpectedErrStr: "Invalid argument to dolt_reflog: NULL", + ExpectedErrStr: "argument () is not a string value, but a ", }, { Query: "select * from dolt_reflog(-100);", - ExpectedErrStr: "Invalid argument to dolt_reflog: -100", + ExpectedErrStr: "argument (-100) is not a string value, but a int8", }, }, }, diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index a8eb79ed8e..f0124a84bf 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -23,7 +23,6 @@ import ( "github.com/dolthub/go-mysql-server/sql" "github.com/dolthub/go-mysql-server/sql/types" - "github.com/dolthub/dolt/go/cmd/dolt/cli" "github.com/dolthub/dolt/go/libraries/doltcore/ref" "github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess" "github.com/dolthub/dolt/go/store/hash" @@ -32,7 +31,7 @@ import ( type ReflogTableFunction struct { ctx *sql.Context database sql.Database - refName string + refExpr sql.Expression showAll bool } @@ -66,6 +65,20 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row return nil, fmt.Errorf("unexpected database type: %T", rltf.database) } + var refName string + if rltf.refExpr != nil { + target, err := rltf.refExpr.Eval(ctx, row) + if err != nil { + return nil, fmt.Errorf("error evaluating expression (%s): %s", + rltf.refExpr.String(), err.Error()) + } + + refName, ok = target.(string) + if !ok { + return nil, fmt.Errorf("argument (%v) is not a string value, but a %T", target, target) + } + } + ddb := sqlDb.DbData().Ddb journal := ddb.ChunkJournal() if journal == nil { @@ -105,15 +118,15 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row } // If a ref expression to filter on was specified, see if we match the current ref - if rltf.refName != "" { + if refName != "" { // If the caller has supplied a branch or tag name, without the fully qualified ref path, // take the first match and use that as the canonical ref to filter on - if strings.HasSuffix(strings.ToLower(id), "/"+strings.ToLower(rltf.refName)) { - rltf.refName = id + if strings.HasSuffix(strings.ToLower(id), "/"+strings.ToLower(refName)) { + refName = id } // Skip refs that don't match the target we're looking for - if strings.ToLower(id) != strings.ToLower(rltf.refName) { + if strings.ToLower(id) != strings.ToLower(refName) { return nil } } @@ -166,11 +179,21 @@ func (rltf *ReflogTableFunction) Schema() sql.Schema { } func (rltf *ReflogTableFunction) Resolved() bool { + if rltf.refExpr != nil { + return rltf.refExpr.Resolved() + } return true } func (rltf *ReflogTableFunction) String() string { - return fmt.Sprintf("DOLT_REFLOG(%s)", rltf.refName) + var args []string + if rltf.showAll { + args = append(args, "'--all'") + } + if rltf.refExpr != nil { + args = append(args, rltf.refExpr.String()) + } + return fmt.Sprintf("DOLT_REFLOG(%s)", strings.Join(args, ", ")) } func (rltf *ReflogTableFunction) Children() []sql.Node { @@ -195,6 +218,9 @@ func (rltf *ReflogTableFunction) IsReadOnly() bool { } func (rltf *ReflogTableFunction) Expressions() []sql.Expression { + if rltf.refExpr != nil { + return []sql.Expression{rltf.refExpr} + } return []sql.Expression{} } @@ -204,20 +230,23 @@ func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) ( } new := *rltf - args, err := getDoltArgs(rltf.ctx, expression, rltf.Name()) - if err != nil { - return nil, err - } - apr, err := cli.CreateReflogArgParser().Parse(args) - if err != nil { - return nil, err - } - if apr.NArg() > 0 { - new.refName = apr.Arg(0) + + if len(expression) == 2 { + if expression[0].String() == "'--all'" && expression[1].String() == "'--all'" { + return nil, fmt.Errorf("error: multiple values provided for `all`") + } + if expression[0].String() != "'--all'" && expression[1].String() != "'--all'" { + return nil, fmt.Errorf("error: %s has too many positional arguments. Expected at most %d, found %d: %s", rltf.Name(), 1, 2, expression) + } } - if apr.Contains(cli.AllFlag) { - new.showAll = true + for _, expr := range expression { + if expr.String() != "'--all'" { + new.refExpr = expr + } else { + new.showAll = true + } } + return &new, nil } diff --git a/integration-tests/bats/helper/local-remote.bash b/integration-tests/bats/helper/local-remote.bash index e799355407..5b2ad0e664 100644 --- a/integration-tests/bats/helper/local-remote.bash +++ b/integration-tests/bats/helper/local-remote.bash @@ -136,7 +136,6 @@ SKIP_SERVER_TESTS=$(cat <<-EOM ~profile.bats~ ~ls.bats~ ~reflog.bats~ -~sql-reflog.bats~ EOM ) diff --git a/integration-tests/bats/reflog.bats b/integration-tests/bats/reflog.bats index bad3273d50..93d54fda02 100755 --- a/integration-tests/bats/reflog.bats +++ b/integration-tests/bats/reflog.bats @@ -1,10 +1,6 @@ #!/usr/bin/env bats load $BATS_TEST_DIRNAME/helper/common.bash -setup() { - setup_common -} - teardown() { assert_feature_version teardown_common @@ -13,6 +9,8 @@ teardown() { # Asserts that when DOLT_DISABLE_REFLOG is set, dolt reflog returns nothing with no error. @test "reflog: disabled with DOLT_DISABLE_REFLOG" { export DOLT_DISABLE_REFLOG=true + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -27,6 +25,8 @@ teardown() { # most recent entries and is limited by the env var's value. @test "reflog: set DOLT_REFLOG_RECORD_LIMIT" { export DOLT_REFLOG_RECORD_LIMIT=2 + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -43,6 +43,8 @@ teardown() { } @test "reflog: simple reflog" { + setup_common + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -51,11 +53,13 @@ teardown() { [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(HEAD -> main) HEAD@{0}: initial commit" ]] || false - [[ "$out" =~ "HEAD@{1}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) initial commit" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false } @test "reflog: reflog with ref given" { + setup_common + dolt sql < main) HEAD@{0}: inserting row 1" ]] || false - [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false - [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false # ref is case-insensitive run dolt reflog rEFs/heAdS/MAIN [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false - [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false - [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false run dolt reflog main [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false - [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false - [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false # ref is case-insensitive run dolt reflog MaIn [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(HEAD -> main) HEAD@{0}: inserting row 1" ]] || false - [[ "$out" =~ "HEAD@{1}: creating table t1" ]] || false - [[ "$out" =~ "HEAD@{2}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false run dolt reflog refs/heads/branch1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false - [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false run dolt reflog branch1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false - [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false run dolt reflog refs/tags/tag1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false # ref is case-insensitive run dolt reflog Refs/tAGs/TaG1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false run dolt reflog tag1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false # ref is case-insensitive run dolt reflog TAg1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false dolt branch -D branch1 @@ -160,25 +164,27 @@ SQL [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 3 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(branch1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 2" ]] || false - [[ "$out" =~ "HEAD@{2}: inserting row 1" ]] || false + [[ "$out" =~ "(branch1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false dolt tag -d tag1 run dolt reflog tag1 [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 2 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(tag: tag1) HEAD@{0}: inserting row 3" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false + [[ "$out" =~ "(tag: tag1) inserting row 3" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false } @test "reflog: garbage collection with no newgen data" { + setup_common + run dolt reflog [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 1 ] out=$(echo "$output" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color - [[ "$out" =~ "(HEAD -> main) HEAD@{0}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) Initialize data repository" ]] || false dolt gc @@ -188,6 +194,8 @@ SQL } @test "reflog: garbage collection with newgen data" { + setup_common + dolt sql < main) HEAD@{0}: inserting row 2" ]] || false - [[ "$out" =~ "HEAD@{1}: inserting row 1" ]] || false - [[ "$out" =~ "HEAD@{2}: creating table t1" ]] || false - [[ "$out" =~ "HEAD@{3}: Initialize data repository" ]] || false + [[ "$out" =~ "(HEAD -> main) inserting row 2" ]] || false + [[ "$out" =~ "inserting row 1" ]] || false + [[ "$out" =~ "creating table t1" ]] || false + [[ "$out" =~ "Initialize data repository" ]] || false dolt gc @@ -215,12 +223,16 @@ SQL } @test "reflog: too many arguments given" { + setup_common + run dolt reflog foo bar [ "$status" -eq 1 ] [[ "$output" =~ "error: reflog has too many positional arguments" ]] || false } @test "reflog: unknown ref returns nothing" { + setup_common + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" diff --git a/integration-tests/bats/sql-local-remote.bats b/integration-tests/bats/sql-local-remote.bats index 95366a0239..4190124668 100644 --- a/integration-tests/bats/sql-local-remote.bats +++ b/integration-tests/bats/sql-local-remote.bats @@ -1270,3 +1270,27 @@ SQL [[ "$localOutput" == "$remoteOutput" ]] || false } + +@test "sql-local-remote: verify dolt reflog behavior" { + cd altDB + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt --verbose-engine-setup reflog + [ $status -eq 0 ] + [[ "$output" =~ "starting local mode" ]] || false + [[ "$output" =~ "initial commit" ]] || false + run dolt reflog + localOutput=$output + + start_sql_server altDB + run dolt --verbose-engine-setup reflog + [ $status -eq 0 ] + [[ "$output" =~ "starting remote mode" ]] || false + [[ "$output" =~ "initial commit" ]] || false + run dolt reflog + remoteOutput=$output + + [[ "$localOutput" == "$remoteOutput" ]] || false +} diff --git a/integration-tests/bats/sql-reflog.bats b/integration-tests/bats/sql-reflog.bats index bac628f016..6e52d0a3e1 100644 --- a/integration-tests/bats/sql-reflog.bats +++ b/integration-tests/bats/sql-reflog.bats @@ -1,10 +1,6 @@ #!/usr/bin/env bats load $BATS_TEST_DIRNAME/helper/common.bash -setup() { - setup_common -} - teardown() { assert_feature_version teardown_common @@ -14,6 +10,8 @@ teardown() { # function returns an empty result set with no error. @test "sql-reflog: disabled with DOLT_DISABLE_REFLOG" { export DOLT_DISABLE_REFLOG=true + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -26,6 +24,8 @@ teardown() { # Sanity check for the most basic case of querying the Dolt reflog @test "sql-reflog: enabled by default" { + setup_common + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" @@ -41,6 +41,8 @@ teardown() { # most recent entries and is limited by the env var's value. @test "sql-reflog: set DOLT_REFLOG_RECORD_LIMIT" { export DOLT_REFLOG_RECORD_LIMIT=2 + setup_common # need to set env vars before setup_common for remote tests + dolt sql -q "create table t (i int primary key, j int);" dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; dolt commit -Am "initial commit" From 0f2e9261e351b94de397749070efb3a1be9db974 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Thu, 30 Nov 2023 11:28:52 -0800 Subject: [PATCH 08/11] reflog doc update --- go/cmd/dolt/commands/reflog.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go index bc1b85c685..0653aba3fd 100644 --- a/go/cmd/dolt/commands/reflog.go +++ b/go/cmd/dolt/commands/reflog.go @@ -42,7 +42,7 @@ Dolt's reflog is similar to [Git's reflog](https://git-scm.com/docs/git-reflog), - The Dolt reflog currently only supports named references, such as branches and tags, and not any of Git's special refs (e.g. {{.EmphasisLeft}}HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}FETCH-HEAD{{.EmphasisRight}}, {{.EmphasisLeft}}MERGE-HEAD{{.EmphasisRight}}). - The Dolt reflog can be queried for the log of references, even after a reference has been deleted. In Git, once a branch or tag is deleted, the reflog for that ref is also deleted and to find the last commit a branch or tag pointed to you have to use Git's special {{.EmphasisLeft}}HEAD{{.EmphasisRight}} reflog to find the commit, which can sometimes be challenging. Dolt makes this much easier by allowing you to see the history for a deleted ref so you can easily see the last commit a branch or tag pointed to before it was deleted.`, Synopsis: []string{ - `{{.LessThan}}ref{{.GreaterThan}}`, + `[--all] {{.LessThan}}ref{{.GreaterThan}}`, }, } From e9c63acccd7e13a9c30b7b462bd7c31671445cf9 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Thu, 30 Nov 2023 11:37:12 -0800 Subject: [PATCH 09/11] another description update --- go/cmd/dolt/cli/arg_parser_helpers.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/go/cmd/dolt/cli/arg_parser_helpers.go b/go/cmd/dolt/cli/arg_parser_helpers.go index 83e87cbfe3..4693ba02ff 100644 --- a/go/cmd/dolt/cli/arg_parser_helpers.go +++ b/go/cmd/dolt/cli/arg_parser_helpers.go @@ -282,7 +282,7 @@ func CreateCountCommitsArgParser() *argparser.ArgParser { func CreateReflogArgParser() *argparser.ArgParser { ap := argparser.NewArgParserWithMaxArgs("reflog", 1) - ap.SupportsFlag(AllFlag, "", "Show all refs") + ap.SupportsFlag(AllFlag, "", "Show all refs, including hidden refs, such as DoltHub workspace refs") return ap } From b47eeb4decca51a145ba992e29ad0759fefcbe9a Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Fri, 1 Dec 2023 15:00:05 -0800 Subject: [PATCH 10/11] PR comments --- go/cmd/dolt/commands/reflog.go | 30 ++++----- .../doltcore/sqle/reflog_table_function.go | 66 ++++++++----------- integration-tests/bats/reflog.bats | 17 +++++ 3 files changed, 61 insertions(+), 52 deletions(-) diff --git a/go/cmd/dolt/commands/reflog.go b/go/cmd/dolt/commands/reflog.go index 0653aba3fd..4c07cfe773 100644 --- a/go/cmd/dolt/commands/reflog.go +++ b/go/cmd/dolt/commands/reflog.go @@ -136,12 +136,12 @@ type ReflogInfo struct { func printReflog(rows []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) int { var reflogInfo []ReflogInfo - // Get the current branch - curBranch := "" - res, err := GetRowsForSql(queryist, sqlCtx, "SELECT active_branch()") + // Get the hash of HEAD for the `HEAD ->` decoration + headHash := "" + res, err := GetRowsForSql(queryist, sqlCtx, "SELECT hashof('HEAD')") if err == nil { - // still print the reflog even if we can't get the current branch - curBranch = res[0][0].(string) + // still print the reflog even if we can't get the hash + headHash = res[0][0].(string) } for _, row := range rows { @@ -151,13 +151,13 @@ func printReflog(rows []sql.Row, queryist cli.Queryist, sqlCtx *sql.Context) int reflogInfo = append(reflogInfo, ReflogInfo{ref, commitHash, commitMessage}) } - reflogToStdOut(reflogInfo, curBranch) + reflogToStdOut(reflogInfo, headHash) return 0 } // reflogToStdOut takes a list of ReflogInfo and prints the reflog to stdout -func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { +func reflogToStdOut(reflogInfo []ReflogInfo, headHash string) { if cli.ExecuteWithStdioRestored == nil { return } @@ -169,8 +169,12 @@ func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { // TODO: use short hash instead line := []string{fmt.Sprintf("\033[33m%s\033[0m", info.commitHash)} // commit hash in yellow (33m) - processedRef := processRefForReflog(info.ref, curBranch) - line = append(line, fmt.Sprintf("\033[33m(%s\033[33m)\033[0m", processedRef)) // () in yellow (33m) + processedRef := processRefForReflog(info.ref) + if headHash != "" && headHash == info.commitHash { + line = append(line, fmt.Sprintf("\033[33m(\033[36;1mHEAD -> %s\033[33m)\033[0m", processedRef)) // HEAD in cyan (36;1) + } else { + line = append(line, fmt.Sprintf("\033[33m(%s\033[33m)\033[0m", processedRef)) // () in yellow (33m) + } line = append(line, fmt.Sprintf("%s\n", info.commitMessage)) pager.Writer.Write([]byte(strings.Join(line, " "))) } @@ -178,13 +182,9 @@ func reflogToStdOut(reflogInfo []ReflogInfo, curBranch string) { } // processRefForReflog takes a full ref (e.g. refs/heads/master) or tag name and returns the ref name (e.g. master) with relevant decoration. -func processRefForReflog(fullRef string, curBranch string) string { +func processRefForReflog(fullRef string) string { if strings.HasPrefix(fullRef, "refs/heads/") { - branch := strings.TrimPrefix(fullRef, "refs/heads/") - if curBranch != "" && branch == curBranch { - return fmt.Sprintf("\033[36;1mHEAD -> \033[32;1m%s\033[0m", branch) // HEAD in cyan (36;1), branch in green (32;1m) - } - return fmt.Sprintf("\033[32;1m%s\033[0m", branch) // branch in green (32;1m) + return fmt.Sprintf("\033[32;1m%s\033[0m", strings.TrimPrefix(fullRef, "refs/heads/")) // branch in green (32;1m) } else if strings.HasPrefix(fullRef, "refs/tags/") { return fmt.Sprintf("\033[33mtag: %s\033[0m", strings.TrimPrefix(fullRef, "refs/tags/")) // tag in yellow (33m) } else if strings.HasPrefix(fullRef, "refs/remotes/") { diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index f0124a84bf..5e9ef5f37d 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -29,10 +29,9 @@ import ( ) type ReflogTableFunction struct { - ctx *sql.Context - database sql.Database - refExpr sql.Expression - showAll bool + ctx *sql.Context + database sql.Database + refAndArgExprs []sql.Expression } var _ sql.TableFunction = (*ReflogTableFunction)(nil) @@ -66,17 +65,30 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row } var refName string - if rltf.refExpr != nil { - target, err := rltf.refExpr.Eval(ctx, row) + showAll := false + for _, expr := range rltf.refAndArgExprs { + target, err := expr.Eval(ctx, row) if err != nil { return nil, fmt.Errorf("error evaluating expression (%s): %s", - rltf.refExpr.String(), err.Error()) + expr.String(), err.Error()) } - - refName, ok = target.(string) + targetStr, ok := target.(string) if !ok { return nil, fmt.Errorf("argument (%v) is not a string value, but a %T", target, target) } + + if targetStr == "--all" { + if showAll { + return nil, fmt.Errorf("error: multiple values provided for `all`") + } + showAll = true + } else { + if refName != "" { + return nil, fmt.Errorf("error: %s has too many positional arguments. Expected at most %d, found %d: %s", + rltf.Name(), 1, 2, rltf.refAndArgExprs) + } + refName = targetStr + } } ddb := sqlDb.DbData().Ddb @@ -112,7 +124,7 @@ func (rltf *ReflogTableFunction) RowIter(ctx *sql.Context, row sql.Row) (sql.Row } // skip workspace refs by default if doltRef.GetType() == ref.WorkspaceRefType { - if !rltf.showAll { + if !showAll { return nil } } @@ -179,19 +191,17 @@ func (rltf *ReflogTableFunction) Schema() sql.Schema { } func (rltf *ReflogTableFunction) Resolved() bool { - if rltf.refExpr != nil { - return rltf.refExpr.Resolved() + for _, expr := range rltf.refAndArgExprs { + return expr.Resolved() } return true } func (rltf *ReflogTableFunction) String() string { var args []string - if rltf.showAll { - args = append(args, "'--all'") - } - if rltf.refExpr != nil { - args = append(args, rltf.refExpr.String()) + + for _, expr := range rltf.refAndArgExprs { + args = append(args, expr.String()) } return fmt.Sprintf("DOLT_REFLOG(%s)", strings.Join(args, ", ")) } @@ -218,10 +228,7 @@ func (rltf *ReflogTableFunction) IsReadOnly() bool { } func (rltf *ReflogTableFunction) Expressions() []sql.Expression { - if rltf.refExpr != nil { - return []sql.Expression{rltf.refExpr} - } - return []sql.Expression{} + return rltf.refAndArgExprs } func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) (sql.Node, error) { @@ -230,22 +237,7 @@ func (rltf *ReflogTableFunction) WithExpressions(expression ...sql.Expression) ( } new := *rltf - - if len(expression) == 2 { - if expression[0].String() == "'--all'" && expression[1].String() == "'--all'" { - return nil, fmt.Errorf("error: multiple values provided for `all`") - } - if expression[0].String() != "'--all'" && expression[1].String() != "'--all'" { - return nil, fmt.Errorf("error: %s has too many positional arguments. Expected at most %d, found %d: %s", rltf.Name(), 1, 2, expression) - } - } - for _, expr := range expression { - if expr.String() != "'--all'" { - new.refExpr = expr - } else { - new.showAll = true - } - } + new.refAndArgExprs = expression return &new, nil } diff --git a/integration-tests/bats/reflog.bats b/integration-tests/bats/reflog.bats index 93d54fda02..942f983466 100755 --- a/integration-tests/bats/reflog.bats +++ b/integration-tests/bats/reflog.bats @@ -241,3 +241,20 @@ SQL [ "$status" -eq 0 ] [ "${#lines[@]}" -eq 0 ] } + +@test "reflog: 'HEAD -> ' decoration only appears on HEAD entries" { + setup_common + + dolt sql -q "create table t (i int primary key, j int);" + dolt sql -q "insert into t values (1, 1), (2, 2), (3, 3)"; + dolt commit -Am "initial commit" + + run dolt reflog + [ "$status" -eq 0 ] + [ "${#lines[@]}" -eq 2 ] + line1=$(echo "${lines[0]}" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + line2=$(echo "${lines[1]}" | sed -E 's/\x1b\[[0-9;]*m//g') # remove special characters for color + [[ "$line1" =~ "(HEAD -> main) initial commit" ]] || false + [[ "$line2" =~ "Initialize data repository" ]] || false + [[ ! "$line2" =~ "HEAD" ]] || false +} From a99b52dc9e841c5a48797a62b5d3e2523cc04e15 Mon Sep 17 00:00:00 2001 From: Stephanie You Date: Fri, 1 Dec 2023 15:44:34 -0800 Subject: [PATCH 11/11] small fix --- go/libraries/doltcore/sqle/reflog_table_function.go | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/go/libraries/doltcore/sqle/reflog_table_function.go b/go/libraries/doltcore/sqle/reflog_table_function.go index 46b89a2abd..874f7d786c 100644 --- a/go/libraries/doltcore/sqle/reflog_table_function.go +++ b/go/libraries/doltcore/sqle/reflog_table_function.go @@ -214,7 +214,9 @@ func (rltf *ReflogTableFunction) Schema() sql.Schema { func (rltf *ReflogTableFunction) Resolved() bool { for _, expr := range rltf.refAndArgExprs { - return expr.Resolved() + if !expr.Resolved() { + return false + } } return true }