Skip to content

Commit

Permalink
Merge pull request #8793 from dolthub/macneale4/dirty-branch-column
Browse files Browse the repository at this point in the history
Add the `dirty` column to the dolt_branches table
  • Loading branch information
macneale4 authored Jan 27, 2025
2 parents b830fbc + da7bb5c commit fad9c1d
Show file tree
Hide file tree
Showing 5 changed files with 108 additions and 4 deletions.
64 changes: 62 additions & 2 deletions go/libraries/doltcore/sqle/dtables/branches_table.go
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
package dtables

import (
"errors"
"fmt"
"io"

Expand All @@ -26,6 +27,7 @@ import (
"github.com/dolthub/dolt/go/libraries/doltcore/schema"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/dsess"
"github.com/dolthub/dolt/go/libraries/doltcore/sqle/index"
"github.com/dolthub/dolt/go/store/hash"
)

const branchesDefaultRowCount = 10
Expand Down Expand Up @@ -90,6 +92,7 @@ func (bt *BranchesTable) Schema() sql.Schema {
if !bt.remote {
columns = append(columns, &sql.Column{Name: "remote", Type: types.Text, Source: bt.tableName, PrimaryKey: false, Nullable: true})
columns = append(columns, &sql.Column{Name: "branch", Type: types.Text, Source: bt.tableName, PrimaryKey: false, Nullable: true})
columns = append(columns, &sql.Column{Name: "dirty", Type: types.Boolean, Source: bt.tableName, PrimaryKey: false, Nullable: true})
}
return columns
}
Expand All @@ -114,6 +117,7 @@ type BranchItr struct {
table *BranchesTable
branches []string
commits []*doltdb.Commit
dirty []bool
idx int
}

Expand Down Expand Up @@ -145,26 +149,36 @@ func NewBranchItr(ctx *sql.Context, table *BranchesTable) (*BranchItr, error) {

branchNames := make([]string, len(branchRefs))
commits := make([]*doltdb.Commit, len(branchRefs))
dirtyBits := make([]bool, len(branchRefs))
for i, branch := range branchRefs {
commit, err := ddb.ResolveCommitRefAtRoot(ctx, branch, txRoot)

if err != nil {
return nil, err
}

var dirty bool
if !remote {
dirty, err = isDirty(ctx, ddb, commit, branch, txRoot)
if err != nil {
return nil, err
}
}

if branch.GetType() == ref.RemoteRefType {
branchNames[i] = "remotes/" + branch.GetPath()
} else {
branchNames[i] = branch.GetPath()
}

dirtyBits[i] = dirty
commits[i] = commit
}

return &BranchItr{
table: table,
branches: branchNames,
commits: commits,
dirty: dirtyBits,
idx: 0,
}, nil
}
Expand All @@ -182,6 +196,7 @@ func (itr *BranchItr) Next(ctx *sql.Context) (sql.Row, error) {

name := itr.branches[itr.idx]
cm := itr.commits[itr.idx]
dirty := itr.dirty[itr.idx]
meta, err := cm.GetCommitMeta(ctx)

if err != nil {
Expand Down Expand Up @@ -211,8 +226,53 @@ func (itr *BranchItr) Next(ctx *sql.Context) (sql.Row, error) {
remoteName = branch.Remote
branchName = branch.Merge.Ref.GetPath()
}
return sql.NewRow(name, h.String(), meta.Name, meta.Email, meta.Time(), meta.Description, remoteName, branchName), nil
return sql.NewRow(name, h.String(), meta.Name, meta.Email, meta.Time(), meta.Description, remoteName, branchName, dirty), nil
}
}

// isDirty returns true if the working ref points to a dirty branch.
func isDirty(ctx *sql.Context, ddb *doltdb.DoltDB, commit *doltdb.Commit, branch ref.DoltRef, txRoot hash.Hash) (bool, error) {
wsRef, err := ref.WorkingSetRefForHead(branch)
if err != nil {
return false, err
}
ws, err := ddb.ResolveWorkingSetAtRoot(ctx, wsRef, txRoot)
if err != nil {
if errors.Is(err, doltdb.ErrWorkingSetNotFound) {
// If there is no working set for this branch, then it is never dirty. This happens on servers commonly.
return false, nil
}
return false, err
}

workingRoot := ws.WorkingRoot()
workingRootHash, err := workingRoot.HashOf()
if err != nil {
return false, err
}
stagedRoot := ws.StagedRoot()
stagedRootHash, err := stagedRoot.HashOf()
if err != nil {
return false, err
}

dirty := false
if workingRootHash != stagedRootHash {
dirty = true
} else {
cmRt, err := commit.GetRootValue(ctx)
if err != nil {
return false, err
}
cmRtHash, err := cmRt.HashOf()
if err != nil {
return false, err
}
if cmRtHash != workingRootHash {
dirty = true
}
}
return dirty, nil
}

// Close closes the iterator.
Expand Down
2 changes: 2 additions & 0 deletions go/libraries/doltcore/sqle/sqlselect_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -784,6 +784,7 @@ func BasicSelectTests() []SelectTest {
"Initialize data repository",
"",
"",
true, // Test setup has a dirty workspace.
},
},
ExpectedSqlSchema: sql.Schema{
Expand All @@ -795,6 +796,7 @@ func BasicSelectTests() []SelectTest {
&sql.Column{Name: "latest_commit_message", Type: gmstypes.Text},
&sql.Column{Name: "remote", Type: gmstypes.Text},
&sql.Column{Name: "branch", Type: gmstypes.Text},
&sql.Column{Name: "dirty", Type: gmstypes.Boolean},
},
},
}
Expand Down
2 changes: 1 addition & 1 deletion go/libraries/doltcore/sqle/testutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -452,7 +452,7 @@ func CreateEmptyTestTable(dEnv *env.DoltEnv, tableName string, sch schema.Schema
return dEnv.UpdateWorkingRoot(ctx, newRoot)
}

// CreateTestDatabase creates a test database with the test data set in it.
// CreateTestDatabase creates a test database with the test data set in it. Has a dirty workspace as well.
func CreateTestDatabase() (*env.DoltEnv, error) {
ctx := context.Background()
dEnv, err := CreateEmptyTestDatabase()
Expand Down
42 changes: 41 additions & 1 deletion integration-tests/bats/sql-server.bats
Original file line number Diff line number Diff line change
Expand Up @@ -1996,4 +1996,44 @@ EOF
run dolt --data-dir datadir1 sql-server --data-dir datadir2
[ $status -eq 1 ]
[[ "$output" =~ "cannot specify both global --data-dir argument and --data-dir in sql-server config" ]] || false
}
}

# This is really a test of the dolt_Branches system table, but due to needing a server with multiple dirty branches
# it was easier to test it with a sql-server.
@test "sql-server: dirty branches listed properly in dolt_branches table" {
skiponwindows "Missing dependencies"

cd repo1
dolt checkout main
dolt branch br1 # Will be a clean commit, ahead of main.
dolt branch br2 # will be a dirty branch, on main.
dolt branch br3 # will be a dirty branch, on br1
start_sql_server repo1

dolt --use-db "repo1" --branch br1 sql -q "CREATE TABLE tbl (i int primary key)"
dolt --use-db "repo1" --branch br1 sql -q "CALL DOLT_COMMIT('-Am', 'commit it')"

dolt --use-db "repo1" --branch br2 sql -q "CREATE TABLE tbl (j int primary key)"

# Fast forward br3 to br1, then make it dirty.
dolt --use-db "repo1" --branch br3 sql -q "CALL DOLT_MERGE('br1')"
dolt --use-db "repo1" --branch br3 sql -q "CREATE TABLE othertbl (k int primary key)"

stop_sql_server 1 && sleep 0.5

run dolt sql -q "SELECT name,dirty FROM dolt_branches"
[ "$status" -eq 0 ]
[[ "$output" =~ "br1 | false" ]] || false
[[ "$output" =~ "br2 | true " ]] || false
[[ "$output" =~ "br3 | true" ]] || false
[[ "$output" =~ "main | false" ]] || false

# Verify that the dolt_branches table show the same output, regardless of the checked out branch.
dolt checkout br1
run dolt sql -q "SELECT name,dirty FROM dolt_branches"
[ "$status" -eq 0 ]
[[ "$output" =~ "br1 | false" ]] || false
[[ "$output" =~ "br2 | true " ]] || false
[[ "$output" =~ "br3 | true" ]] || false
[[ "$output" =~ "main | false" ]] || false
}
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ export const branchTests = [
latest_commit_message: "Initialize data repository",
remote: "",
branch: "",
dirty: 0,
},
{
name: "mybranch",
Expand All @@ -68,6 +69,7 @@ export const branchTests = [
latest_commit_message: "Create table test",
remote: "",
branch: "",
dirty: 0,
},
],
matcher: branchesMatcher,
Expand Down

0 comments on commit fad9c1d

Please sign in to comment.