Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions go/cmd/dolt/cli/arg_parser_helpers.go
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ func CreateCloneArgParser() *argparser.ArgParser {
ap.SupportsString(RemoteParam, "", "name", "Name of the remote to be added to the cloned database. The default is 'origin'.")
ap.SupportsString(BranchParam, "b", "branch", "The branch to be cloned. If not specified all branches will be cloned.")
ap.SupportsString(DepthFlag, "", "depth", "Clone a single branch and limit history to the given commit depth.")
ap.SupportsString("ref", "", "ref", "Git ref to use as the Dolt data ref for git remotes (default: refs/dolt/data).")
ap.SupportsString(dbfactory.AWSRegionParam, "", "region", "")
ap.SupportsValidatedString(dbfactory.AWSCredsTypeParam, "", "creds-type", "", argparser.ValidatorFromStrList(dbfactory.AWSCredsTypeParam, dbfactory.AWSCredTypes))
ap.SupportsString(dbfactory.AWSCredsFileParam, "", "file", "AWS credentials file.")
Expand All @@ -166,6 +167,7 @@ func CreateResetArgParser() *argparser.ArgParser {

func CreateRemoteArgParser() *argparser.ArgParser {
ap := argparser.NewArgParserWithVariableArgs("remote")
ap.SupportsString("ref", "", "ref", "Git ref to use as the Dolt data ref for git remotes (default: refs/dolt/data).")
return ap
}

Expand Down Expand Up @@ -266,6 +268,7 @@ func CreateBackupArgParser() *argparser.ArgParser {
ap.ArgListHelp = append(ap.ArgListHelp, [2]string{"profile", "AWS profile to use."})
ap.SupportsFlag(VerboseFlag, "v", "When printing the list of backups adds additional details.")
ap.SupportsFlag(ForceFlag, "f", "When restoring a backup, overwrite the contents of the existing database with the same name.")
ap.SupportsString("ref", "", "ref", "Git ref to use as the Dolt data ref for git remotes (default: refs/dolt/data).")
ap.SupportsString(dbfactory.AWSRegionParam, "", "region", "")
ap.SupportsValidatedString(dbfactory.AWSCredsTypeParam, "", "creds-type", "", argparser.ValidatorFromStrList(dbfactory.AWSCredsTypeParam, dbfactory.AWSCredTypes))
ap.SupportsString(dbfactory.AWSCredsFileParam, "", "file", "AWS credentials file")
Expand Down
34 changes: 29 additions & 5 deletions go/cmd/dolt/commands/clone.go
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,18 @@ This default configuration is achieved by creating references to the remote bran
},
}

type remoteDialerWithGitCacheRoot struct {
dbfactory.GRPCDialProvider
root string
}

func (d remoteDialerWithGitCacheRoot) GitCacheRoot() (string, bool) {
if strings.TrimSpace(d.root) == "" {
return "", false
}
return d.root, true
}

type CloneCmd struct{}

// Name is returns the name of the Dolt cli command. This is what is used on the command line to invoke the command
Expand Down Expand Up @@ -130,7 +142,11 @@ func clone(ctx context.Context, apr *argparser.ArgParseResults, dEnv *env.DoltEn

var r env.Remote
var srcDB *doltdb.DoltDB
r, srcDB, verr = createRemote(ctx, remoteName, remoteUrl, params, dEnv)
cloneRoot, err := dEnv.FS.Abs(dir)
if err != nil {
return errhand.VerboseErrorFromError(err)
}
r, srcDB, verr = createRemote(ctx, remoteName, remoteUrl, params, dEnv, cloneRoot)
if verr != nil {
return verr
}
Expand Down Expand Up @@ -187,15 +203,19 @@ func parseArgs(apr *argparser.ArgParseResults) (string, string, errhand.VerboseE

urlStr := apr.Arg(0)
_, err := earl.Parse(urlStr)

if err != nil {
return "", "", errhand.BuildDError("error: invalid remote url: %s", urlStr).Build()
if normalized, ok, nerr := env.NormalizeGitRemoteUrl(urlStr); nerr == nil && ok {
urlStr = normalized
} else {
return "", "", errhand.BuildDError("error: invalid remote url: %s", urlStr).Build()
}
}

var dir string
if apr.NArg() == 2 {
dir = apr.Arg(1)
} else {
// Infer directory name from the URL.
dir = path.Base(urlStr)
if dir == "." {
dir = path.Dir(urlStr)
Expand All @@ -207,11 +227,15 @@ func parseArgs(apr *argparser.ArgParseResults) (string, string, errhand.VerboseE
return dir, urlStr, nil
}

func createRemote(ctx context.Context, remoteName, remoteUrl string, params map[string]string, dEnv *env.DoltEnv) (env.Remote, *doltdb.DoltDB, errhand.VerboseError) {
func createRemote(ctx context.Context, remoteName, remoteUrl string, params map[string]string, dEnv *env.DoltEnv, cloneRoot string) (env.Remote, *doltdb.DoltDB, errhand.VerboseError) {
cli.Printf("cloning %s\n", remoteUrl)

r := env.NewRemote(remoteName, remoteUrl, params)
ddb, err := r.GetRemoteDB(ctx, types.Format_Default, dEnv)
dialer := dbfactory.GRPCDialProvider(dEnv)
if strings.TrimSpace(cloneRoot) != "" {
dialer = remoteDialerWithGitCacheRoot{GRPCDialProvider: dEnv, root: cloneRoot}
}
ddb, err := r.GetRemoteDB(ctx, types.Format_Default, dialer)
if err != nil {
bdr := errhand.BuildDError("error: failed to get remote db").AddCause(err)
return env.NoRemote, nil, bdr.Build()
Expand Down
11 changes: 11 additions & 0 deletions go/cmd/dolt/commands/clone_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -65,3 +65,14 @@ func TestParseDolthubRepos(t *testing.T) {
}

}

func TestCloneParseArgs_InferDir(t *testing.T) {
ap := CloneCmd{}.ArgParser()
apr, err := ap.Parse([]string{"https://example.com/org/repo.git"})
require.NoError(t, err)

dir, urlStr, verr := parseArgs(apr)
require.Nil(t, verr)
require.Equal(t, "repo.git", dir)
require.Equal(t, "https://example.com/org/repo.git", urlStr)
}
10 changes: 8 additions & 2 deletions go/cmd/dolt/commands/read_tables.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,7 @@ func (cmd ReadTablesCmd) ArgParser() *argparser.ArgParser {
{"table", " Optional tables to retrieve. If omitted, all tables are retrieved."},
}
ap.SupportsString(dirParamName, "d", "directory", "directory to create and put retrieved table data.")
ap.SupportsString(gitRefFlag, "", "ref", "Git ref to use as the Dolt data ref for git remotes (default: refs/dolt/data).")
return ap
}

Expand All @@ -99,7 +100,11 @@ func (cmd ReadTablesCmd) Exec(ctx context.Context, commandStr string, args []str
_, err := earl.Parse(urlStr)

if err != nil {
return HandleVErrAndExitCode(errhand.BuildDError("Invalid remote url").AddCause(err).Build(), usage)
if normalized, ok, nerr := env.NormalizeGitRemoteUrl(urlStr); nerr == nil && ok {
urlStr = normalized
} else {
return HandleVErrAndExitCode(errhand.BuildDError("Invalid remote url").AddCause(err).Build(), usage)
}
}

dir := apr.GetValueOrDefault(dirParamName, path.Base(urlStr))
Expand Down Expand Up @@ -203,7 +208,8 @@ func pullTableValue(ctx context.Context, dEnv *env.DoltEnv, srcDB *doltdb.DoltDB
}

func getRemoteDBAtCommit(ctx context.Context, remoteUrl string, remoteUrlParams map[string]string, commitStr string, dEnv *env.DoltEnv) (*doltdb.DoltDB, doltdb.RootValue, errhand.VerboseError) {
_, srcDB, verr := createRemote(ctx, "temp", remoteUrl, remoteUrlParams, dEnv)
cacheRoot, _ := dEnv.GitCacheRoot()
_, srcDB, verr := createRemote(ctx, "temp", remoteUrl, remoteUrlParams, dEnv, cacheRoot)

if verr != nil {
return nil, nil, verr
Expand Down
31 changes: 31 additions & 0 deletions go/cmd/dolt/commands/read_tables_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
// Copyright 2026 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 (
"testing"

"github.com/stretchr/testify/require"
)

func TestReadTablesArgParser_AcceptsGitFlags(t *testing.T) {
ap := ReadTablesCmd{}.ArgParser()
apr, err := ap.Parse([]string{
"--" + gitRefFlag, "refs/dolt/custom",
"git+file:///tmp/remote.git", "main",
})
require.NoError(t, err)
require.Equal(t, "refs/dolt/custom", apr.GetValueOrDefault(gitRefFlag, ""))
}
26 changes: 26 additions & 0 deletions go/cmd/dolt/commands/remote.go
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,7 @@ const (
addRemoteId = "add"
removeRemoteId = "remove"
removeRemoteShortId = "rm"
gitRefFlag = "ref"
)

type RemoteCmd struct{}
Expand Down Expand Up @@ -212,16 +213,41 @@ func parseRemoteArgs(apr *argparser.ArgParseResults, scheme, remoteUrl string) (
err = cli.AddAWSParams(remoteUrl, apr, params)
case dbfactory.OSSScheme:
err = cli.AddOSSParams(remoteUrl, apr, params)
case dbfactory.GitFileScheme, dbfactory.GitHTTPScheme, dbfactory.GitHTTPSScheme, dbfactory.GitSSHScheme:
verr := addGitRemoteParams(apr, params)
if verr != nil {
return nil, verr
}
default:
err = cli.VerifyNoAwsParams(apr)
}
if err != nil {
return nil, errhand.VerboseErrorFromError(err)
}

// Flags that are only meaningful for git remotes should not be accepted for other schemes.
switch scheme {
case dbfactory.GitFileScheme, dbfactory.GitHTTPScheme, dbfactory.GitHTTPSScheme, dbfactory.GitSSHScheme:
default:
if _, ok := apr.GetValue(gitRefFlag); ok {
return nil, errhand.BuildDError("error: --%s is only supported for git remotes", gitRefFlag).Build()
}
}

return params, nil
}

func addGitRemoteParams(apr *argparser.ArgParseResults, params map[string]string) errhand.VerboseError {
if v, ok := apr.GetValue(gitRefFlag); ok {
v = strings.TrimSpace(v)
if v == "" {
return errhand.BuildDError("error: --%s cannot be empty", gitRefFlag).Build()
}
params[dbfactory.GitRefParam] = v
}
return nil
}

// callSQLRemoteAdd calls the SQL function `call `dolt_remote('add', remoteName, remoteUrl)`
func callSQLRemoteAdd(sqlCtx *sql.Context, queryist cli.Queryist, remoteName, remoteUrl string) error {
qry, err := dbr.InterpolateForDialect("call dolt_remote('add', ?, ?)", []interface{}{remoteName, remoteUrl}, dialect.MySQL)
Expand Down
11 changes: 11 additions & 0 deletions go/cmd/dolt/commands/remote_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import (

"github.com/stretchr/testify/assert"

"github.com/dolthub/dolt/go/libraries/doltcore/dbfactory"
"github.com/dolthub/dolt/go/libraries/doltcore/env"
"github.com/dolthub/dolt/go/libraries/utils/config"
"github.com/dolthub/dolt/go/libraries/utils/filesys"
Expand Down Expand Up @@ -135,3 +136,13 @@ func TestGetAbsRemoteUrl(t *testing.T) {
})
}
}

func TestParseRemoteArgs_GitRef(t *testing.T) {
ap := RemoteCmd{}.ArgParser()
apr, err := ap.Parse([]string{"add", "origin", "git+file:///tmp/remote.git", "--" + gitRefFlag, "refs/dolt/custom"})
assert.NoError(t, err)

params, verr := parseRemoteArgs(apr, dbfactory.GitFileScheme, "git+file:///tmp/remote.git")
assert.Nil(t, verr)
assert.Equal(t, "refs/dolt/custom", params[dbfactory.GitRefParam])
}
28 changes: 19 additions & 9 deletions go/libraries/doltcore/dbfactory/factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,12 @@ const (

OSSScheme = "oss"

// Git remote dbfactory schemes (Git remotes as Dolt remotes)
GitFileScheme = "git+file"
GitHTTPScheme = "git+http"
GitHTTPSScheme = "git+https"
GitSSHScheme = "git+ssh"

defaultScheme = HTTPSScheme
defaultMemTableSize = 256 * 1024 * 1024
)
Expand All @@ -69,15 +75,19 @@ type DBFactory interface {
// DBFactories is a map from url scheme name to DBFactory. Additional factories can be added to the DBFactories map
// from external packages.
var DBFactories = map[string]DBFactory{
AWSScheme: AWSFactory{},
OSSScheme: OSSFactory{},
GSScheme: GSFactory{},
OCIScheme: OCIFactory{},
FileScheme: FileFactory{},
MemScheme: MemFactory{},
LocalBSScheme: LocalBSFactory{},
HTTPScheme: NewDoltRemoteFactory(true),
HTTPSScheme: NewDoltRemoteFactory(false),
AWSScheme: AWSFactory{},
OSSScheme: OSSFactory{},
GSScheme: GSFactory{},
OCIScheme: OCIFactory{},
FileScheme: FileFactory{},
MemScheme: MemFactory{},
LocalBSScheme: LocalBSFactory{},
HTTPScheme: NewDoltRemoteFactory(true),
HTTPSScheme: NewDoltRemoteFactory(false),
GitFileScheme: GitRemoteFactory{},
GitHTTPScheme: GitRemoteFactory{},
GitHTTPSScheme: GitRemoteFactory{},
GitSSHScheme: GitRemoteFactory{},
}

// CreateDB creates a database based on the supplied urlStr, and creation params. The DBFactory used for creation is
Expand Down
Loading
Loading