func getWizard(dep *cfg.Dependency) { var remote string if dep.Repository != "" { remote = dep.Repository } else { remote = "https://" + dep.Name } // Lookup dependency info and store in cache. msg.Info("--> Gathering release information for %s", dep.Name) wizardFindVersions(dep) memlatest := cache.MemLatest(remote) if memlatest != "" { dres := wizardAskLatest(memlatest, dep) if dres { dep.Reference = memlatest sv, err := semver.NewVersion(dep.Reference) if err == nil { res := wizardAskRange(sv, dep) if res == "m" { dep.Reference = "^" + sv.String() } else if res == "p" { dep.Reference = "~" + sv.String() } } } } }
// VcsExists checks if the directory has a local VCS checkout. func VcsExists(dep *cfg.Dependency, dest string) bool { repo, err := dep.GetRepo(dest) if err != nil { return false } return repo.CheckLocal() }
// VcsLastCommit gets the last commit ID from the given dependency. func VcsLastCommit(dep *cfg.Dependency, vend string) (string, error) { cwd := filepath.Join(vend, filepath.FromSlash(dep.Name)) repo, err := dep.GetRepo(cwd) if err != nil { return "", err } if repo.CheckLocal() == false { return "", fmt.Errorf("%s is not a VCS repo\n", dep.Name) } version, err := repo.Version() if err != nil { return "", err } return version, nil }
// VcsGet figures out how to fetch a dependency, and then gets it. // // VcsGet installs into the cache. func VcsGet(dep *cfg.Dependency) error { key, err := cp.Key(dep.Remote()) if err != nil { msg.Die("Cache key generation error: %s", err) } location := cp.Location() d := filepath.Join(location, "src", key) repo, err := dep.GetRepo(d) if err != nil { return err } // If the directory does not exist this is a first cache. if _, err = os.Stat(d); os.IsNotExist(err) { msg.Debug("Adding %s to the cache for the first time", dep.Name) err = repo.Get() if err != nil { return err } branch := findCurrentBranch(repo) if branch != "" { msg.Debug("Saving default branch for %s", repo.Remote()) c := cp.RepoInfo{DefaultBranch: branch} err = cp.SaveRepoData(key, c) if err == cp.ErrCacheDisabled { msg.Debug("Unable to cache default branch because caching is disabled") } else if err != nil { msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err) } } } else { msg.Debug("Updating %s in the cache", dep.Name) err = repo.Update() if err != nil { return err } } return nil }
func TestAddPkgsToConfig(t *testing.T) { // Route output to discard so it's not displayed with the test output. o := msg.Default.Stderr msg.Default.Stderr = ioutil.Discard conf := new(cfg.Config) dep := new(cfg.Dependency) dep.Name = "github.com/Masterminds/cookoo" dep.Subpackages = append(dep.Subpackages, "convert") conf.Imports = append(conf.Imports, dep) names := []string{ "github.com/Masterminds/cookoo/fmt", "github.com/Masterminds/semver", } addPkgsToConfig(conf, names, false, true) if !conf.HasDependency("github.com/Masterminds/semver") { t.Error("addPkgsToConfig failed to add github.com/Masterminds/semver") } d := conf.Imports.Get("github.com/Masterminds/cookoo") found := false for _, s := range d.Subpackages { if s == "fmt" { found = true } } if !found { t.Error("addPkgsToConfig failed to add subpackage to existing import") } // Restore messaging to original location msg.Default.Stderr = o }
func determineDependency(v, dep *cfg.Dependency, dest string) *cfg.Dependency { repo, err := v.GetRepo(dest) if err != nil { singleWarn("Unable to access repo for %s\n", v.Name) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } vIsRef := repo.IsReference(v.Reference) depIsRef := repo.IsReference(dep.Reference) // Both are references and they are different ones. if vIsRef && depIsRef { singleWarn("Conflict: %s ref is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if vIsRef { // The current one is a reference and the suggestion is a SemVer constraint. con, err := semver.NewConstraint(dep.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(v.Reference) if err != nil { // The existing version is not a semantic version. singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { singleInfo("Keeping %s %s because it fits constraint '%s'", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s but does not meet constraint '%s'\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } else if depIsRef { con, err := semver.NewConstraint(v.Reference) if err != nil { singleWarn("Version issue for %s: '%s' is neither a reference or semantic version constraint\n", v.Name, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } ver, err := semver.NewVersion(dep.Reference) if err != nil { singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } if con.Check(ver) { v.Reference = dep.Reference singleInfo("Using %s %s because it fits constraint '%s'", v.Name, v.Reference, v.Reference) return v } singleWarn("Conflict: %s semantic version constraint is %s but '%s' does not meet the constraint\n", v.Name, v.Reference, v.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } // Neither is a vcs reference and both could be semantic version // constraints that are different. _, err = semver.NewConstraint(dep.Reference) if err != nil { // dd.Reference is not a reference or a valid constraint. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", dep.Name, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v } _, err = semver.NewConstraint(v.Reference) if err != nil { // existing.Reference is not a reference or a valid constraint. // We really should never end up here. singleWarn("Version %s %s is not a reference or valid semantic version constraint\n", v.Name, v.Reference) v.Reference = dep.Reference v.Pin = "" singleInfo("Using %s %s because it is a valid version", v.Name, v.Reference) return v } // Both versions are constraints. Try to merge them. // If either comparison has an || skip merging. That's complicated. ddor := strings.Index(dep.Reference, "||") eor := strings.Index(v.Reference, "||") if ddor == -1 && eor == -1 { // Add the comparisons together. newRef := v.Reference + ", " + dep.Reference v.Reference = newRef v.Pin = "" singleInfo("Combining %s semantic version constraints %s and %s", v.Name, v.Reference, dep.Reference) return v } singleWarn("Conflict: %s version is %s, but also asked for %s\n", v.Name, v.Reference, dep.Reference) singleInfo("Keeping %s %s", v.Name, v.Reference) return v }
// VcsGet figures out how to fetch a dependency, and then gets it. // // VcsGet installs into the dest. func VcsGet(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath bool) error { // When not skipping the $GOPATH look in it for a copy of the package if useGopath { // Check if the $GOPATH has a viable version to use and if so copy to vendor gps := gpath.Gopaths() for _, p := range gps { d := filepath.Join(p, "src", dep.Name) if _, err := os.Stat(d); err == nil { empty, err := gpath.IsDirectoryEmpty(d) if empty || err != nil { continue } repo, err := dep.GetRepo(d) if err != nil { continue } // Dirty repos have uncomitted changes. if repo.IsDirty() { continue } // Having found a repo we copy it to vendor and update it. msg.Info("Copying package %s from the GOPATH.", dep.Name) msg.Debug("Found %s in GOPATH at %s. Copying to %s", dep.Name, d, dest) err = gpath.CopyDir(d, dest) if err != nil { return err } // Update the repo in the vendor directory msg.Debug("Updating %s, now in the vendor path at %s", dep.Name, dest) repo, err = dep.GetRepo(dest) if err != nil { return err } err = repo.Update() if err != nil { return err } // If there is no reference set on the dep we try to checkout // the default branch. if dep.Reference == "" { db := defaultBranch(repo, home) if db != "" { err = repo.UpdateVersion(db) if err != nil && msg.Default.IsDebugging { msg.Debug("Attempting to set the version on %s to %s failed. Error %s", dep.Name, db, err) } } } return nil } } } // When opting in to cache in the GOPATH attempt to do put a copy there. if cacheGopath { // Since we didn't find an existing copy in the GOPATHs try to clone there. gp := gpath.Gopath() if gp != "" { d := filepath.Join(gp, "src", dep.Name) if _, err := os.Stat(d); os.IsNotExist(err) { // Empty directory so we checkout out the code here. msg.Debug("Retrieving %s to %s before copying to vendor", dep.Name, d) repo, err := dep.GetRepo(d) if err != nil { return err } repo.Get() branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { msg.Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} err = saveCacheRepoData(key, c, home) if msg.Default.IsDebugging && err == errCacheDisabled { msg.Debug("Unable to cache default branch because caching is disabled") } } } msg.Debug("Copying %s from GOPATH at %s to %s", dep.Name, d, dest) err = gpath.CopyDir(d, dest) if err != nil { return err } return nil } } } // If opting in to caching attempt to put it in the cache folder if cache { // Check if the cache has a viable version and try to use that. var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { d := filepath.Join(home, "cache", "src", key) repo, err := dep.GetRepo(d) if err != nil { return err } // If the directory does not exist this is a first cache. if _, err = os.Stat(d); os.IsNotExist(err) { msg.Debug("Adding %s to the cache for the first time", dep.Name) err = repo.Get() if err != nil { return err } branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { msg.Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} err = saveCacheRepoData(key, c, home) if err == errCacheDisabled { msg.Debug("Unable to cache default branch because caching is disabled") } else if err != nil { msg.Debug("Error saving %s to cache. Error: %s", repo.Remote(), err) } } } } else { msg.Debug("Updating %s in the cache", dep.Name) err = repo.Update() if err != nil { return err } } msg.Debug("Copying %s from the cache to %s", dep.Name, dest) err = gpath.CopyDir(d, dest) if err != nil { return err } return nil } else { msg.Warn("Cache key generation error: %s", err) } } // If unable to cache pull directly into the vendor/ directory. repo, err := dep.GetRepo(dest) if err != nil { return err } gerr := repo.Get() // Attempt to cache the default branch branch := findCurrentBranch(repo) if branch != "" { // we know the default branch so we can store it in the cache var loc string if dep.Repository != "" { loc = dep.Repository } else { loc = "https://" + dep.Name } key, err := cacheCreateKey(loc) if err == nil { msg.Debug("Saving default branch for %s", repo.Remote()) c := cacheRepoInfo{DefaultBranch: branch} err = saveCacheRepoData(key, c, home) if err == errCacheDisabled { msg.Debug("Unable to cache default branch because caching is disabled") } else if err != nil { msg.Debug("Error saving %s to cache - Error: %s", repo.Remote(), err) } } } return gerr }
// VcsUpdate updates to a particular checkout based on the VCS setting. func VcsUpdate(dep *cfg.Dependency, dest, home string, cache, cacheGopath, useGopath, force, updateVendored bool) error { // If the dependency has already been pinned we can skip it. This is a // faster path so we don't need to resolve it again. if dep.Pin != "" { msg.Debug("Dependency %s has already been pinned. Fetching updates skipped.", dep.Name) return nil } msg.Info("Fetching updates for %s.\n", dep.Name) if filterArchOs(dep) { msg.Info("%s is not used for %s/%s.\n", dep.Name, runtime.GOOS, runtime.GOARCH) return nil } // If destination doesn't exist we need to perform an initial checkout. if _, err := os.Stat(dest); os.IsNotExist(err) { if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } } else { // At this point we have a directory for the package. // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := gpath.IsDirectoryEmpty(dest) if err != nil { return err } _, err = v.DetectVcsFromFS(dest) if updateVendored == false && empty == false && err == v.ErrCannotDetectVCS { msg.Warn("%s appears to be a vendored package. Unable to update. Consider the '--update-vendored' flag.\n", dep.Name) } else { if updateVendored == true && empty == false && err == v.ErrCannotDetectVCS { // A vendored package, no repo, and updating the vendored packages // has been opted into. msg.Info("%s is a vendored package. Updating.", dep.Name) err = os.RemoveAll(dest) if err != nil { msg.Error("Unable to update vendored dependency %s.\n", dep.Name) return err } else { dep.UpdateAsVendored = true } if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } return nil } repo, err := dep.GetRepo(dest) // Tried to checkout a repo to a path that does not work. Either the // type or endpoint has changed. Force is being passed in so the old // location can be removed and replaced with the new one. // Warning, any changes in the old location will be deleted. // TODO: Put dirty checking in on the existing local checkout. if (err == v.ErrWrongVCS || err == v.ErrWrongRemote) && force == true { var newRemote string if len(dep.Repository) > 0 { newRemote = dep.Repository } else { newRemote = "https://" + dep.Name } msg.Warn("Replacing %s with contents from %s\n", dep.Name, newRemote) rerr := os.RemoveAll(dest) if rerr != nil { return rerr } if err = VcsGet(dep, dest, home, cache, cacheGopath, useGopath); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } } else if err != nil { return err } else if repo.IsDirty() { return fmt.Errorf("%s contains uncommited changes. Skipping update", dep.Name) } else { // Check if the current version is a tag or commit id. If it is // and that version is already checked out we can skip updating // which is faster than going out to the Internet to perform // an update. if dep.Reference != "" { version, err := repo.Version() if err != nil { return err } ib, err := isBranch(dep.Reference, repo) if err != nil { return err } // If the current version equals the ref and it's not a // branch it's a tag or commit id so we can skip // performing an update. if version == dep.Reference && !ib { msg.Info("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference) return nil } } if err := repo.Update(); err != nil { msg.Warn("Download failed.\n") return err } } } } return nil }
// VcsVersion set the VCS version for a checkout. func VcsVersion(dep *cfg.Dependency, vend string) error { // If the dependency has already been pinned we can skip it. This is a // faster path so we don't need to resolve it again. if dep.Pin != "" { msg.Debug("Dependency %s has already been pinned. Setting version skipped.", dep.Name) return nil } cwd := filepath.Join(vend, dep.Name) // If there is no refernece configured there is nothing to set. if dep.Reference == "" { // Before exiting update the pinned version repo, err := dep.GetRepo(cwd) if err != nil { return err } dep.Pin, err = repo.Version() if err != nil { return err } return nil } // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := gpath.IsDirectoryEmpty(cwd) if err != nil { return err } _, err = v.DetectVcsFromFS(cwd) if empty == false && err == v.ErrCannotDetectVCS { msg.Warn("%s appears to be a vendored package. Unable to set new version. Consider the '--update-vendored' flag.\n", dep.Name) } else { repo, err := dep.GetRepo(cwd) if err != nil { return err } ver := dep.Reference // Referenes in Git can begin with a ^ which is similar to semver. // If there is a ^ prefix we assume it's a semver constraint rather than // part of the git/VCS commit id. if repo.IsReference(ver) && !strings.HasPrefix(ver, "^") { msg.Info("Setting version for %s to %s.\n", dep.Name, ver) } else { // Create the constraing first to make sure it's valid before // working on the repo. constraint, err := semver.NewConstraint(ver) // Make sure the constriant is valid. At this point it's not a valid // reference so if it's not a valid constrint we can exit early. if err != nil { msg.Warn("The reference '%s' is not valid\n", ver) return err } // Get the tags and branches (in that order) refs, err := getAllVcsRefs(repo) if err != nil { return err } // Convert and filter the list to semver.Version instances semvers := getSemVers(refs) // Sort semver list sort.Sort(sort.Reverse(semver.Collection(semvers))) found := false for _, v := range semvers { if constraint.Check(v) { found = true // If the constrint passes get the original reference ver = v.Original() break } } if found { msg.Info("Detected semantic version. Setting version for %s to %s.\n", dep.Name, ver) } else { msg.Warn("Unable to find semantic version for constraint %s %s\n", dep.Name, ver) } } if err := repo.UpdateVersion(ver); err != nil { msg.Error("Failed to set version to %s: %s\n", dep.Reference, err) return err } dep.Pin, err = repo.Version() if err != nil { return err } } return nil }
// addPkgsToConfig adds the given packages to the config file. // // Along the way it: // - ensures that this package is not in the ignore list // - checks to see if this is already in the dependency list. // - splits version of of package name and adds the version attribute // - separates repo from packages // - sets up insecure repo URLs where necessary // - generates a list of subpackages func addPkgsToConfig(conf *cfg.Config, names []string, insecure, nonInteract, testDeps bool) (int, error) { if len(names) == 1 { msg.Info("Preparing to install %d package.", len(names)) } else { msg.Info("Preparing to install %d packages.", len(names)) } numAdded := 0 for _, name := range names { var version string parts := strings.Split(name, "#") if len(parts) > 1 { name = parts[0] version = parts[1] } msg.Info("Attempting to get package %s", name) root, subpkg := util.NormalizeName(name) if len(root) == 0 { return 0, fmt.Errorf("Package name is required for %q.", name) } if conf.HasDependency(root) { var moved bool var dep *cfg.Dependency // Move from DevImports to Imports if !testDeps && !conf.Imports.Has(root) && conf.DevImports.Has(root) { dep = conf.DevImports.Get(root) conf.Imports = append(conf.Imports, dep) conf.DevImports = conf.DevImports.Remove(root) moved = true numAdded++ msg.Info("--> Moving %s from testImport to import", root) } else if testDeps && conf.Imports.Has(root) { msg.Warn("--> Test dependency %s already listed as import", root) } // Check if the subpackage is present. if subpkg != "" { if dep == nil { dep = conf.Imports.Get(root) if dep == nil && testDeps { dep = conf.DevImports.Get(root) } } if dep.HasSubpackage(subpkg) { if !moved { msg.Warn("--> Package %q is already in glide.yaml. Skipping", name) } } else { dep.Subpackages = append(dep.Subpackages, subpkg) msg.Info("--> Adding sub-package %s to existing import %s", subpkg, root) numAdded++ } } else if !moved { msg.Warn("--> Package %q is already in glide.yaml. Skipping", root) } continue } if conf.HasIgnore(root) { msg.Warn("--> Package %q is set to be ignored in glide.yaml. Skipping", root) continue } dep := &cfg.Dependency{ Name: root, } // When retriving from an insecure location set the repo to the // insecure location. if insecure { dep.Repository = "http://" + root } if version != "" { dep.Reference = version } else if !nonInteract { getWizard(dep) } if len(subpkg) > 0 { dep.Subpackages = []string{subpkg} } if dep.Reference != "" { msg.Info("--> Adding %s to your configuration with the version %s", dep.Name, dep.Reference) } else { msg.Info("--> Adding %s to your configuration", dep.Name) } if testDeps { conf.DevImports = append(conf.DevImports, dep) } else { conf.Imports = append(conf.Imports, dep) } numAdded++ } return numAdded, nil }
func wizardFindVersions(d *cfg.Dependency) { l := cache.Location() remote := d.Remote() key, err := cache.Key(remote) if err != nil { msg.Debug("Problem generating cache key for %s: %s", remote, err) return } local := filepath.Join(l, "src", key) repo, err := vcs.NewRepo(remote, local) if err != nil { msg.Debug("Problem getting repo instance: %s", err) return } var useLocal bool if _, err = os.Stat(local); err == nil { useLocal = true } // Git endpoints allow for querying without fetching the codebase locally. // We try that first to avoid fetching right away. Is this premature // optimization? cc := true if !useLocal && repo.Vcs() == vcs.Git { out, err2 := exec.Command("git", "ls-remote", remote).CombinedOutput() if err2 == nil { cache.MemTouch(remote) cc = false lines := strings.Split(string(out), "\n") for _, i := range lines { ti := strings.TrimSpace(i) if found := createGitParseVersion.FindString(ti); found != "" { tg := strings.TrimPrefix(strings.TrimSuffix(found, "^{}"), "tags/") cache.MemPut(remote, tg) if d.Reference != "" && strings.HasPrefix(ti, d.Reference) { cache.MemSetCurrent(remote, tg) } } } } } if cc { cache.Lock(key) cache.MemTouch(remote) if _, err = os.Stat(local); os.IsNotExist(err) { repo.Get() branch := findCurrentBranch(repo) c := cache.RepoInfo{DefaultBranch: branch} err = cache.SaveRepoData(key, c) if err != nil { msg.Debug("Error saving cache repo details: %s", err) } } else { repo.Update() } tgs, err := repo.Tags() if err != nil { msg.Debug("Problem getting tags: %s", err) } else { for _, v := range tgs { cache.MemPut(remote, v) } } if d.Reference != "" && repo.IsReference(d.Reference) { tgs, err = repo.TagsFromCommit(d.Reference) if err != nil { msg.Debug("Problem getting tags for commit: %s", err) } else { if len(tgs) > 0 { for _, v := range tgs { if !(repo.Vcs() == vcs.Hg && v == "tip") { cache.MemSetCurrent(remote, v) } } } } } cache.Unlock(key) } }
// VcsUpdate updates to a particular checkout based on the VCS setting. func VcsUpdate(dep *cfg.Dependency, force bool, updated *UpdateTracker) error { // If the dependency has already been pinned we can skip it. This is a // faster path so we don't need to resolve it again. if dep.Pin != "" { msg.Debug("Dependency %s has already been pinned. Fetching updates skipped.", dep.Name) return nil } if updated.Check(dep.Name) { msg.Debug("%s was already updated, skipping.", dep.Name) return nil } updated.Add(dep.Name) if filterArchOs(dep) { msg.Info("%s is not used for %s/%s.\n", dep.Name, runtime.GOOS, runtime.GOARCH) return nil } key, err := cp.Key(dep.Remote()) if err != nil { msg.Die("Cache key generation error: %s", err) } location := cp.Location() dest := filepath.Join(location, "src", key) // If destination doesn't exist we need to perform an initial checkout. if _, err := os.Stat(dest); os.IsNotExist(err) { msg.Info("--> Fetching %s.", dep.Name) if err = VcsGet(dep); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } } else { // At this point we have a directory for the package. msg.Info("--> Fetching updates for %s.", dep.Name) // When the directory is not empty and has no VCS directory it's // a vendored files situation. empty, err := gpath.IsDirectoryEmpty(dest) if err != nil { return err } _, err = v.DetectVcsFromFS(dest) if empty == true && err == v.ErrCannotDetectVCS { msg.Warn("Cached version of %s is an empty directory. Fetching a new copy of the dependency.", dep.Name) msg.Debug("Removing empty directory %s", dest) err := os.RemoveAll(dest) if err != nil { return err } if err = VcsGet(dep); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } } else { repo, err := dep.GetRepo(dest) // Tried to checkout a repo to a path that does not work. Either the // type or endpoint has changed. Force is being passed in so the old // location can be removed and replaced with the new one. // Warning, any changes in the old location will be deleted. // TODO: Put dirty checking in on the existing local checkout. if (err == v.ErrWrongVCS || err == v.ErrWrongRemote) && force == true { newRemote := dep.Remote() msg.Warn("Replacing %s with contents from %s\n", dep.Name, newRemote) rerr := os.RemoveAll(dest) if rerr != nil { return rerr } if err = VcsGet(dep); err != nil { msg.Warn("Unable to checkout %s\n", dep.Name) return err } repo, err = dep.GetRepo(dest) if err != nil { return err } } else if err != nil { return err } else if repo.IsDirty() { return fmt.Errorf("%s contains uncommitted changes. Skipping update", dep.Name) } ver := dep.Reference if ver == "" { ver = defaultBranch(repo) } // Check if the current version is a tag or commit id. If it is // and that version is already checked out we can skip updating // which is faster than going out to the Internet to perform // an update. if ver != "" { version, err := repo.Version() if err != nil { return err } ib, err := isBranch(ver, repo) if err != nil { return err } // If the current version equals the ref and it's not a // branch it's a tag or commit id so we can skip // performing an update. if version == ver && !ib { msg.Debug("%s is already set to version %s. Skipping update.", dep.Name, dep.Reference) return nil } } if err := repo.Update(); err != nil { msg.Warn("Download failed.\n") return err } } } return nil }