github.com/dolthub/dolt/go@v0.40.5-0.20240520175717-68db7794bea6/libraries/doltcore/env/remotes.go (about) 1 // Copyright 2019 Dolthub, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package env 16 17 import ( 18 "context" 19 "errors" 20 "fmt" 21 "net/url" 22 "path" 23 "path/filepath" 24 "sort" 25 "strings" 26 27 goerrors "gopkg.in/src-d/go-errors.v1" 28 29 "github.com/dolthub/dolt/go/libraries/doltcore/dbfactory" 30 "github.com/dolthub/dolt/go/libraries/doltcore/doltdb" 31 "github.com/dolthub/dolt/go/libraries/doltcore/ref" 32 "github.com/dolthub/dolt/go/libraries/utils/argparser" 33 "github.com/dolthub/dolt/go/libraries/utils/concurrentmap" 34 "github.com/dolthub/dolt/go/libraries/utils/config" 35 "github.com/dolthub/dolt/go/libraries/utils/earl" 36 filesys2 "github.com/dolthub/dolt/go/libraries/utils/filesys" 37 "github.com/dolthub/dolt/go/store/types" 38 ) 39 40 var NoRemote = Remote{} 41 42 var ErrBranchDoesNotMatchUpstream = errors.New("the upstream branch of your current branch does not match the name of your current branch") 43 var ErrFailedToReadDb = errors.New("failed to read from the db") 44 var ErrUnknownBranch = errors.New("unknown branch") 45 var ErrCannotSetUpstreamForTag = errors.New("cannot set upstream for tag") 46 var ErrCannotPushRef = errors.New("cannot push ref") 47 var ErrNoRefSpecForRemote = errors.New("no refspec for remote") 48 var ErrInvalidFetchSpec = errors.New("invalid fetch spec") 49 var ErrPullWithRemoteNoUpstream = errors.New("You asked to pull from the remote '%s', but did not specify a branch. Because this is not the default configured remote for your current branch, you must specify a branch.") 50 var ErrPullWithNoRemoteAndNoUpstream = errors.New("There is no tracking information for the current branch.\nPlease specify which branch you want to merge with.\n\n\tdolt pull <remote> <branch>\n\nIf you wish to set tracking information for this branch you can do so with:\n\n\t dolt push --set-upstream <remote> <branch>\n") 51 52 var ErrCurrentBranchHasNoUpstream = goerrors.NewKind("fatal: The current branch %s has no upstream branch.\n" + 53 "To push the current branch and set the remote as upstream, use\n" + 54 "\tdolt push --set-upstream %s %s\n" + 55 "To have this happen automatically for branches without a tracking\n" + 56 "upstream, see 'push.autoSetupRemote' in 'dolt config --help'.") 57 var ErrInvalidRepository = goerrors.NewKind("fatal: remote '%s' not found.\n" + 58 "Please make sure the remote exists.") 59 var ErrAllFlagCannotBeUsedWithRefSpec = goerrors.NewKind("fatal: --all can't be combined with refspecs") 60 var ErrNoPushDestination = goerrors.NewKind("fatal: No configured push destination.\n" + 61 "Either specify the URL from the command-line or configure a remote repository using\n\n" + 62 "\tdolt remote add <name> <url>\n\n" + 63 "and then push using the remote name\n\n" + 64 "\tdolt push <name>\n\n") 65 var ErrFailedToPush = goerrors.NewKind("error: failed to push some refs to '%s'\n" + 66 "hint: Updates were rejected because the tip of your current branch is behind\n" + 67 "hint: its remote counterpart. Integrate the remote changes (e.g.\n" + 68 "hint: 'dolt pull ...') before pushing again.\n") 69 70 func IsEmptyRemote(r Remote) bool { 71 return len(r.Name) == 0 && len(r.Url) == 0 && r.FetchSpecs == nil && r.Params == nil 72 } 73 74 type Remote struct { 75 Name string `json:"name"` 76 Url string `json:"url"` 77 FetchSpecs []string `json:"fetch_specs"` 78 Params map[string]string `json:"params"` 79 } 80 81 func NewRemote(name, url string, params map[string]string) Remote { 82 return Remote{name, url, []string{"refs/heads/*:refs/remotes/" + name + "/*"}, params} 83 } 84 85 func (r *Remote) GetParam(pName string) (string, bool) { 86 val, ok := r.Params[pName] 87 return val, ok 88 } 89 90 func (r *Remote) GetParamOrDefault(pName, defVal string) string { 91 val, ok := r.Params[pName] 92 93 if !ok { 94 return defVal 95 } 96 97 return val 98 } 99 100 func (r *Remote) GetRemoteDB(ctx context.Context, nbf *types.NomsBinFormat, dialer dbfactory.GRPCDialProvider) (*doltdb.DoltDB, error) { 101 params := make(map[string]interface{}) 102 for k, v := range r.Params { 103 params[k] = v 104 } 105 106 params[dbfactory.GRPCDialProviderParam] = dialer 107 108 return doltdb.LoadDoltDBWithParams(ctx, nbf, r.Url, filesys2.LocalFS, params) 109 } 110 111 // Prepare does whatever work is necessary to prepare the remote given to receive pushes. Not all remote types can 112 // support this operations and must be prepared manually. For existing remotes, no work is done. 113 func (r *Remote) Prepare(ctx context.Context, nbf *types.NomsBinFormat, dialer dbfactory.GRPCDialProvider) error { 114 params := make(map[string]interface{}) 115 for k, v := range r.Params { 116 params[k] = v 117 } 118 119 params[dbfactory.GRPCDialProviderParam] = dialer 120 121 return dbfactory.PrepareDB(ctx, nbf, r.Url, params) 122 } 123 124 func (r *Remote) GetRemoteDBWithoutCaching(ctx context.Context, nbf *types.NomsBinFormat, dialer dbfactory.GRPCDialProvider) (*doltdb.DoltDB, error) { 125 params := make(map[string]interface{}) 126 for k, v := range r.Params { 127 params[k] = v 128 } 129 params[dbfactory.NoCachingParameter] = "true" 130 params[dbfactory.GRPCDialProviderParam] = dialer 131 132 return doltdb.LoadDoltDBWithParams(ctx, nbf, r.Url, filesys2.LocalFS, params) 133 } 134 135 func (r Remote) WithParams(params map[string]string) Remote { 136 fetchSpecs := make([]string, len(r.FetchSpecs)) 137 copy(fetchSpecs, r.FetchSpecs) 138 for k, v := range r.Params { 139 params[k] = v 140 } 141 r.Params = params 142 return r 143 } 144 145 // PushOptions contains information needed for push for 146 // one or more branches or a tag for a specific remote database. 147 type PushOptions struct { 148 Targets []*PushTarget 149 Rsr RepoStateReader 150 Rsw RepoStateWriter 151 Remote *Remote 152 SrcDb *doltdb.DoltDB 153 DestDb *doltdb.DoltDB 154 TmpDir string 155 } 156 157 // PushTarget contains information needed for push per branch or tag. 158 type PushTarget struct { 159 SrcRef ref.DoltRef 160 DestRef ref.DoltRef 161 RemoteRef ref.DoltRef 162 Mode ref.UpdateMode 163 SetUpstream bool 164 HasUpstream bool 165 } 166 167 func NewPushOpts(ctx context.Context, apr *argparser.ArgParseResults, rsr RepoStateReader, ddb *doltdb.DoltDB, force, setUpstream, pushAutoSetupRemote, all bool) ([]*PushTarget, *Remote, error) { 168 if apr.NArg() == 0 { 169 return getPushTargetsAndRemoteFromNoArg(ctx, rsr, ddb, force, setUpstream, pushAutoSetupRemote, all) 170 } 171 172 rsrBranches, err := rsr.GetBranches() 173 if err != nil { 174 return nil, nil, err 175 } 176 177 currentBranch, err := rsr.CWBHeadRef() 178 if err != nil { 179 return nil, nil, err 180 } 181 182 // the first argument defines the remote name 183 remoteName := apr.Arg(0) 184 if apr.NArg() == 1 { 185 remote, err := getRemote(rsr, remoteName) 186 if err != nil { 187 return nil, nil, err 188 } 189 190 if all { 191 return getPushTargetsAndRemoteForAllBranches(ctx, rsrBranches, currentBranch, &remote, ddb, force, setUpstream) 192 } else { 193 defaultRemote, err := GetDefaultRemote(rsr) 194 if err != nil { 195 return nil, nil, err 196 } 197 198 refSpec, _, hasUpstream, err := getCurrentBranchRefSpec(ctx, rsrBranches, rsr, ddb, remoteName, defaultRemote.Name == remoteName, true, setUpstream, pushAutoSetupRemote) 199 if err != nil { 200 return nil, nil, err 201 } 202 203 opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, &remote, force, setUpstream, hasUpstream) 204 if err != nil { 205 return nil, nil, err 206 } 207 return []*PushTarget{opts}, &remote, nil 208 } 209 } else { 210 if all { 211 return nil, nil, ErrAllFlagCannotBeUsedWithRefSpec.New() 212 } 213 214 refSpecNames := apr.Args[1:] 215 // validate given refSpec names 216 for _, refSpecName := range refSpecNames { 217 if len(refSpecName) == 0 { 218 return nil, nil, fmt.Errorf("%w: '%s'", ref.ErrInvalidRefSpec, refSpecName) 219 } 220 } 221 222 remote, err := getRemote(rsr, apr.Arg(0)) 223 if err != nil { 224 return nil, nil, err 225 } 226 return getPushTargetsAndRemoteForBranchRefs(ctx, rsrBranches, refSpecNames, currentBranch, &remote, ddb, force, setUpstream) 227 } 228 } 229 230 func getRemote(rsr RepoStateReader, name string) (Remote, error) { 231 remotes, err := rsr.GetRemotes() 232 if err != nil { 233 return NoRemote, err 234 } 235 236 remote, ok := remotes.Get(name) 237 if !ok { 238 return NoRemote, ErrInvalidRepository.New(name) 239 } 240 return remote, nil 241 } 242 243 // getPushTargetsAndRemoteFromNoArg pushes the current branch on default remote if upstream is set or `-u` is defined; 244 // otherwise, all branches of default remote if `--all` flag is used. 245 func getPushTargetsAndRemoteFromNoArg(ctx context.Context, rsr RepoStateReader, ddb *doltdb.DoltDB, force, setUpstream, pushAutoSetupRemote, all bool) ([]*PushTarget, *Remote, error) { 246 rsrBranches, err := rsr.GetBranches() 247 if err != nil { 248 return nil, nil, err 249 } 250 251 currentBranch, err := rsr.CWBHeadRef() 252 if err != nil { 253 return nil, nil, err 254 } 255 256 remote, err := GetDefaultRemote(rsr) 257 if err != nil { 258 if err == ErrNoRemote { 259 err = ErrNoPushDestination.New() 260 } 261 return nil, nil, err 262 } 263 if all { 264 return getPushTargetsAndRemoteForAllBranches(ctx, rsrBranches, currentBranch, &remote, ddb, force, setUpstream) 265 } else { 266 refSpec, remoteName, hasUpstream, err := getCurrentBranchRefSpec(ctx, rsrBranches, rsr, ddb, remote.Name, true, false, setUpstream, pushAutoSetupRemote) 267 if err != nil { 268 return nil, nil, err 269 } 270 if remoteName != remote.Name { 271 remote, err = getRemote(rsr, remoteName) 272 if err != nil { 273 return nil, nil, err 274 } 275 } 276 277 opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, &remote, force, setUpstream, hasUpstream) 278 if err != nil { 279 return nil, nil, err 280 } 281 return []*PushTarget{opts}, &remote, nil 282 } 283 } 284 285 func getPushTargetsAndRemoteForAllBranches(ctx context.Context, rsrBranches *concurrentmap.Map[string, BranchConfig], currentBranch ref.DoltRef, remote *Remote, ddb *doltdb.DoltDB, force, setUpstream bool) ([]*PushTarget, *Remote, error) { 286 localBranches, err := ddb.GetBranches(ctx) 287 if err != nil { 288 return nil, nil, err 289 } 290 var lbNames = make([]string, len(localBranches)) 291 for i, branch := range localBranches { 292 lbNames[i] = branch.GetPath() 293 } 294 return getPushTargetsAndRemoteForBranchRefs(ctx, rsrBranches, lbNames, currentBranch, remote, ddb, force, setUpstream) 295 } 296 297 func getPushTargetsAndRemoteForBranchRefs(ctx context.Context, rsrBranches *concurrentmap.Map[string, BranchConfig], localBranches []string, currentBranch ref.DoltRef, remote *Remote, ddb *doltdb.DoltDB, force, setUpstream bool) ([]*PushTarget, *Remote, error) { 298 var pushOptsList []*PushTarget 299 for _, refSpecName := range localBranches { 300 refSpec, err := getRefSpecFromStr(ctx, ddb, refSpecName) 301 if err != nil { 302 return nil, nil, err 303 } 304 305 // if the remote of upstream does not match the remote given, 306 // it should push to the given remote creating new remote branch 307 upstream, hasUpstream := rsrBranches.Get(refSpecName) 308 hasUpstream = hasUpstream && upstream.Remote == remote.Name 309 310 opts, err := getPushTargetFromRefSpec(refSpec, currentBranch, remote, force, setUpstream, hasUpstream) 311 if err != nil { 312 return nil, nil, err 313 } 314 315 pushOptsList = append(pushOptsList, opts) 316 } 317 return pushOptsList, remote, nil 318 } 319 320 func getPushTargetFromRefSpec(refSpec ref.RefSpec, currentBranch ref.DoltRef, remote *Remote, force, setUpstream, hasUpstream bool) (*PushTarget, error) { 321 src := refSpec.SrcRef(currentBranch) 322 dest := refSpec.DestRef(src) 323 324 var remoteRef ref.DoltRef 325 var err error 326 switch src.GetType() { 327 case ref.BranchRefType: 328 remoteRef, err = GetTrackingRef(dest, *remote) 329 case ref.TagRefType: 330 if setUpstream { 331 err = ErrCannotSetUpstreamForTag 332 } 333 default: 334 err = fmt.Errorf("%w: '%s' of type '%s'", ErrCannotPushRef, src.String(), src.GetType()) 335 } 336 if err != nil { 337 return nil, err 338 } 339 340 return &PushTarget{ 341 SrcRef: src, 342 DestRef: dest, 343 RemoteRef: remoteRef, 344 Mode: ref.UpdateMode{ 345 Force: force, 346 }, 347 SetUpstream: setUpstream, 348 HasUpstream: hasUpstream, 349 }, nil 350 } 351 352 // getCurrentBranchRefSpec is called when refSpec is NOT specified. Whether to push depends on the specified remote. 353 // If the specified remote is the default or the only remote, then it cannot push without its upstream set. 354 // If the specified remote is one of many and non-default remote, then it pushes regardless of upstream is set. 355 // If there is no remote specified, the current branch needs to have upstream set to push; otherwise, returns error. 356 // This function returns |refSpec| for current branch, name of the remote the branch is associated with and 357 // whether the current branch has upstream set. 358 func getCurrentBranchRefSpec(ctx context.Context, branches *concurrentmap.Map[string, BranchConfig], rsr RepoStateReader, ddb *doltdb.DoltDB, remoteName string, isDefaultRemote, remoteSpecified, setUpstream, pushAutoSetupRemote bool) (ref.RefSpec, string, bool, error) { 359 var refSpec ref.RefSpec 360 currentBranch, err := rsr.CWBHeadRef() 361 if err != nil { 362 return nil, "", false, err 363 } 364 365 currentBranchName := currentBranch.GetPath() 366 upstream, hasUpstream := branches.Get(currentBranchName) 367 368 if remoteSpecified || pushAutoSetupRemote { 369 if isDefaultRemote && !pushAutoSetupRemote { 370 return nil, "", false, ErrCurrentBranchHasNoUpstream.New(currentBranchName, remoteName, currentBranchName) 371 } 372 setUpstream = true 373 refSpec, err = getRefSpecFromStr(ctx, ddb, currentBranchName) 374 if err != nil { 375 return nil, "", false, err 376 } 377 } else if hasUpstream { 378 remoteName = upstream.Remote 379 refSpec, err = getCurrentBranchRefSpecFromUpstream(currentBranch, upstream) 380 if err != nil { 381 return nil, "", false, err 382 } 383 } else { 384 return nil, "", false, ErrCurrentBranchHasNoUpstream.New(currentBranchName, remoteName, currentBranchName) 385 } 386 return refSpec, remoteName, hasUpstream && upstream.Remote == remoteName, nil 387 } 388 389 // RemoteForFetchArgs returns the remote and remaining arg strings for a fetch command 390 func RemoteForFetchArgs(args []string, rsr RepoStateReader) (Remote, []string, error) { 391 var err error 392 remotes, err := rsr.GetRemotes() 393 if err != nil { 394 return NoRemote, nil, err 395 } 396 397 if remotes.Len() == 0 { 398 return NoRemote, nil, ErrNoRemote 399 } 400 401 var remName string 402 if len(args) == 0 { 403 remName = "origin" 404 } else { 405 remName = args[0] 406 args = args[1:] 407 } 408 409 remote, ok := remotes.Get(remName) 410 if !ok { 411 msg := "does not appear to be a dolt database. could not read from the remote database. please make sure you have the correct access rights and the database exists" 412 return NoRemote, nil, fmt.Errorf("%w; '%s' %s", ErrUnknownRemote, remName, msg) 413 } 414 415 return remote, args, nil 416 } 417 418 // ParseRefSpecs returns the ref specs for the string arguments given for the remote provided, or the default ref 419 // specs for that remote if no arguments are provided. 420 func ParseRefSpecs(args []string, rsr RepoStateReader, remote Remote) ([]ref.RemoteRefSpec, error) { 421 if len(args) != 0 { 422 return ParseRSFromArgs(remote.Name, args) 423 } else { 424 return GetRefSpecs(rsr, remote.Name) 425 } 426 } 427 428 func ParseRSFromArgs(remName string, args []string) ([]ref.RemoteRefSpec, error) { 429 var refSpecs []ref.RemoteRefSpec 430 for i := 0; i < len(args); i++ { 431 rsStr := args[i] 432 rs, err := ref.ParseRefSpec(rsStr) 433 434 if err != nil { 435 return nil, fmt.Errorf("%w: '%s'", ErrInvalidFetchSpec, rsStr) 436 } 437 438 if _, ok := rs.(ref.BranchToBranchRefSpec); ok { 439 local := "refs/heads/" + rsStr 440 remTracking := "remotes/" + remName + "/" + rsStr 441 rs2, err := ref.ParseRefSpec(local + ":" + remTracking) 442 443 if err == nil { 444 rs = rs2 445 } 446 } 447 448 if rrs, ok := rs.(ref.RemoteRefSpec); !ok { 449 return nil, fmt.Errorf("%w: '%s'", ErrInvalidFetchSpec, rsStr) 450 451 } else { 452 refSpecs = append(refSpecs, rrs) 453 } 454 } 455 456 return refSpecs, nil 457 } 458 459 // if possible, convert refs to full spec names. prefer branches over tags. 460 // eg "main" -> "refs/heads/main", "v1" -> "refs/tags/v1" 461 func disambiguateRefSpecStr(ctx context.Context, ddb *doltdb.DoltDB, refSpecStr string) (string, error) { 462 brachRefs, err := ddb.GetBranches(ctx) 463 464 if err != nil { 465 return "", err 466 } 467 468 for _, br := range brachRefs { 469 if br.GetPath() == refSpecStr { 470 return br.String(), nil 471 } 472 } 473 474 tagRefs, err := ddb.GetTags(ctx) 475 476 if err != nil { 477 return "", err 478 } 479 480 for _, tr := range tagRefs { 481 if tr.GetPath() == refSpecStr { 482 return tr.String(), nil 483 } 484 } 485 486 return refSpecStr, nil 487 } 488 489 // getRefSpecFromStr returns ref.RefSpec object using given branch/refSpec name 490 func getRefSpecFromStr(ctx context.Context, ddb *doltdb.DoltDB, refSpecStr string) (ref.RefSpec, error) { 491 refSpecStr, err := disambiguateRefSpecStr(ctx, ddb, refSpecStr) 492 if err != nil { 493 return nil, err 494 } 495 496 refSpec, err := ref.ParseRefSpec(refSpecStr) 497 if err != nil { 498 return nil, fmt.Errorf("%w: '%s'", err, refSpecStr) 499 } 500 501 return refSpec, nil 502 } 503 504 // getCurrentBranchRefSpecFromUpstream validates the number of args defined and returns ref.RefSpec object of 505 // current branch corresponding to the given upstream. 506 func getCurrentBranchRefSpecFromUpstream(currentBranch ref.DoltRef, upstream BranchConfig) (ref.RefSpec, error) { 507 if currentBranch.GetPath() != upstream.Merge.Ref.GetPath() { 508 return nil, ErrBranchDoesNotMatchUpstream 509 } 510 511 refSpec, _ := ref.NewBranchToBranchRefSpec(currentBranch.(ref.BranchRef), upstream.Merge.Ref.(ref.BranchRef)) 512 return refSpec, nil 513 } 514 515 func GetTrackingRef(branchRef ref.DoltRef, remote Remote) (ref.DoltRef, error) { 516 for _, fsStr := range remote.FetchSpecs { 517 fs, err := ref.ParseRefSpecForRemote(remote.Name, fsStr) 518 519 if err != nil { 520 return nil, fmt.Errorf("%w '%s' for remote '%s'", ErrInvalidFetchSpec, fsStr, remote.Name) 521 } 522 523 remoteRef := fs.DestRef(branchRef) 524 525 if remoteRef != nil { 526 return remoteRef, nil 527 } 528 } 529 530 return nil, nil 531 } 532 533 type PullSpec struct { 534 Squash bool 535 NoFF bool 536 NoCommit bool 537 NoEdit bool 538 Force bool 539 RemoteName string 540 Remote Remote 541 RefSpecs []ref.RemoteRefSpec 542 Branch ref.DoltRef 543 } 544 545 type PullSpecOpt func(*PullSpec) 546 547 func WithSquash(squash bool) PullSpecOpt { 548 return func(ps *PullSpec) { 549 ps.Squash = squash 550 } 551 } 552 553 func WithNoFF(noff bool) PullSpecOpt { 554 return func(ps *PullSpec) { 555 ps.NoFF = noff 556 } 557 } 558 559 func WithNoCommit(nocommit bool) PullSpecOpt { 560 return func(ps *PullSpec) { 561 ps.NoCommit = nocommit 562 } 563 } 564 565 func WithNoEdit(noedit bool) PullSpecOpt { 566 return func(ps *PullSpec) { 567 ps.NoEdit = noedit 568 } 569 } 570 571 func WithForce(force bool) PullSpecOpt { 572 return func(ps *PullSpec) { 573 ps.Force = force 574 } 575 } 576 577 // NewPullSpec returns a PullSpec for the arguments given. This function validates remote and gets remoteRef 578 // for given remoteRefName; if it's not defined, it uses current branch to get its upstream branch if it exists. 579 func NewPullSpec( 580 _ context.Context, 581 rsr RepoStateReader, 582 remoteName, remoteRefName string, 583 remoteOnly bool, 584 opts ...PullSpecOpt, 585 ) (*PullSpec, error) { 586 refSpecs, err := GetRefSpecs(rsr, remoteName) 587 if err != nil { 588 return nil, err 589 } 590 if len(refSpecs) == 0 { 591 return nil, ErrNoRefSpecForRemote 592 } 593 594 remotes, err := rsr.GetRemotes() 595 if err != nil { 596 return nil, err 597 } 598 remote, found := remotes.Get(refSpecs[0].GetRemote()) 599 if !found { 600 return nil, ErrPullWithNoRemoteAndNoUpstream 601 } 602 603 var remoteRef ref.DoltRef 604 if remoteRefName == "" { 605 branch, err := rsr.CWBHeadRef() 606 if err != nil { 607 return nil, err 608 } 609 trackedBranches, err := rsr.GetBranches() 610 if err != nil { 611 return nil, err 612 } 613 614 trackedBranch, hasUpstream := trackedBranches.Get(branch.GetPath()) 615 if !hasUpstream { 616 if remoteOnly { 617 return nil, fmt.Errorf(ErrPullWithRemoteNoUpstream.Error(), remoteName) 618 } else { 619 return nil, ErrPullWithNoRemoteAndNoUpstream 620 } 621 } 622 623 remoteRef = trackedBranch.Merge.Ref 624 } else { 625 remoteRef = ref.NewBranchRef(remoteRefName) 626 } 627 628 spec := &PullSpec{ 629 RemoteName: remoteName, 630 Remote: remote, 631 RefSpecs: refSpecs, 632 Branch: remoteRef, 633 } 634 635 for _, opt := range opts { 636 opt(spec) 637 } 638 639 return spec, nil 640 } 641 642 func GetAbsRemoteUrl(fs filesys2.Filesys, cfg config.ReadableConfig, urlArg string) (string, string, error) { 643 u, err := earl.Parse(urlArg) 644 if err != nil { 645 return "", "", err 646 } 647 648 if u.Scheme != "" && fs != nil { 649 if u.Scheme == dbfactory.FileScheme || u.Scheme == dbfactory.LocalBSScheme { 650 absUrl, err := getAbsFileRemoteUrl(u, fs) 651 652 if err != nil { 653 return "", "", err 654 } 655 656 return u.Scheme, absUrl, err 657 } 658 659 return u.Scheme, urlArg, nil 660 } else if u.Host != "" { 661 return dbfactory.HTTPSScheme, "https://" + urlArg, nil 662 } 663 664 hostName, err := cfg.GetString(config.RemotesApiHostKey) 665 666 if err != nil { 667 if err != config.ErrConfigParamNotFound { 668 return "", "", err 669 } 670 671 hostName = DefaultRemotesApiHost 672 } 673 674 hostName = strings.TrimSpace(hostName) 675 676 return dbfactory.HTTPSScheme, "https://" + path.Join(hostName, u.Path), nil 677 } 678 679 func getAbsFileRemoteUrl(u *url.URL, fs filesys2.Filesys) (string, error) { 680 urlStr := u.Host + u.Path 681 scheme := u.Scheme 682 683 var err error 684 urlStr = filepath.Clean(urlStr) 685 urlStr, err = fs.Abs(urlStr) 686 687 if err != nil { 688 return "", err 689 } 690 691 exists, isDir := fs.Exists(urlStr) 692 693 if !exists { 694 // TODO: very odd that GetAbsRemoteUrl will create a directory if it doesn't exist. 695 // This concern should be separated 696 err = fs.MkDirs(urlStr) 697 if err != nil { 698 return "", fmt.Errorf("failed to create directory '%s': %w", urlStr, err) 699 } 700 } else if !isDir { 701 return "", filesys2.ErrIsFile 702 } 703 704 urlStr = filepath.ToSlash(urlStr) 705 return scheme + "://" + urlStr, nil 706 } 707 708 // GetDefaultBranch returns the default branch from among the branches given, returning 709 // the configs default config branch first, then init branch main, then the old init branch master, 710 // and finally the first lexicographical branch if none of the others are found 711 func GetDefaultBranch(dEnv *DoltEnv, branches []ref.DoltRef) string { 712 if len(branches) == 0 { 713 return DefaultInitBranch 714 } 715 716 sort.Slice(branches, func(i, j int) bool { 717 return branches[i].GetPath() < branches[j].GetPath() 718 }) 719 720 branchMap := make(map[string]ref.DoltRef) 721 for _, b := range branches { 722 branchMap[b.GetPath()] = b 723 } 724 725 if _, ok := branchMap[DefaultInitBranch]; ok { 726 return DefaultInitBranch 727 } 728 if _, ok := branchMap["master"]; ok { 729 return "master" 730 } 731 732 // todo: do we care about this during clone? 733 defaultOrMain := GetDefaultInitBranch(dEnv.Config) 734 if _, ok := branchMap[defaultOrMain]; ok { 735 return defaultOrMain 736 } 737 738 return branches[0].GetPath() 739 } 740 741 // SetRemoteUpstreamForRefSpec set upstream for given RefSpec, remote name and branch ref. It uses given RepoStateWriter 742 // to persist upstream tracking branch information. 743 func SetRemoteUpstreamForRefSpec(rsw RepoStateWriter, refSpec ref.RefSpec, remote string, branchRef ref.DoltRef) error { 744 src := refSpec.SrcRef(branchRef) 745 dest := refSpec.DestRef(src) 746 747 return rsw.UpdateBranch(branchRef.GetPath(), BranchConfig{ 748 Merge: ref.MarshalableRef{ 749 Ref: dest, 750 }, 751 Remote: remote, 752 }) 753 }