1 Star 0 Fork 0

liboxwz/dep

加入 Gitee
与超过 1200万 开发者一起发现、参与优秀开源项目,私有仓库也完全免费 :)
免费加入
克隆/下载
vcs_repo.go 10.87 KB
一键复制 编辑 原始数据 按行查看 历史
// Copyright 2017 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package gps
import (
"bytes"
"context"
"encoding/xml"
"os"
"path/filepath"
"runtime"
"strings"
"time"
"github.com/Masterminds/vcs"
"github.com/pkg/errors"
)
type ctxRepo interface {
vcs.Repo
get(context.Context) error
fetch(context.Context) error
updateVersion(context.Context, string) error
//ping(context.Context) (bool, error)
}
// ensureCleaner is an optional extension of ctxRepo.
type ensureCleaner interface {
// ensureClean ensures a repository is clean and in working order,
// or returns an error if the adaptive recovery attempts fail.
ensureClean(context.Context) error
}
// original implementation of these methods come from
// https://github.com/Masterminds/vcs
type gitRepo struct {
*vcs.GitRepo
}
func newVcsRemoteErrorOr(err error, args []string, out, msg string) error {
if err == context.Canceled || err == context.DeadlineExceeded {
return err
}
return vcs.NewRemoteError(msg, errors.Wrapf(err, "command failed: %v", args), out)
}
func newVcsLocalErrorOr(err error, args []string, out, msg string) error {
if err == context.Canceled || err == context.DeadlineExceeded {
return err
}
return vcs.NewLocalError(msg, errors.Wrapf(err, "command failed: %v", args), out)
}
func (r *gitRepo) get(ctx context.Context) error {
cmd := commandContext(
ctx,
"git",
"clone",
"--recursive",
"-v",
"--progress",
r.Remote(),
r.LocalPath(),
)
// Ensure no prompting for PWs
cmd.SetEnv(append([]string{"GIT_ASKPASS=", "GIT_TERMINAL_PROMPT=0"}, os.Environ()...))
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to get repository")
}
return nil
}
func (r *gitRepo) fetch(ctx context.Context) error {
cmd := commandContext(
ctx,
"git",
"fetch",
"--tags",
"--prune",
r.RemoteLocation,
)
cmd.SetDir(r.LocalPath())
// Ensure no prompting for PWs
cmd.SetEnv(append([]string{"GIT_ASKPASS=", "GIT_TERMINAL_PROMPT=0"}, os.Environ()...))
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to update repository")
}
return nil
}
func (r *gitRepo) updateVersion(ctx context.Context, v string) error {
cmd := commandContext(ctx, "git", "checkout", v)
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to update checked out version")
}
return r.defendAgainstSubmodules(ctx)
}
// defendAgainstSubmodules tries to keep repo state sane in the event of
// submodules. Or nested submodules. What a great idea, submodules.
func (r *gitRepo) defendAgainstSubmodules(ctx context.Context) error {
// First, update them to whatever they should be, if there should happen to be any.
{
cmd := commandContext(
ctx,
"git",
"submodule",
"update",
"--init",
"--recursive",
)
cmd.SetDir(r.LocalPath())
// Ensure no prompting for PWs
cmd.SetEnv(append([]string{"GIT_ASKPASS=", "GIT_TERMINAL_PROMPT=0"}, os.Environ()...))
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unexpected error while defensively updating submodules")
}
}
// Now, do a special extra-aggressive clean in case changing versions caused
// one or more submodules to go away.
{
cmd := commandContext(ctx, "git", "clean", "-x", "-d", "-f", "-f")
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unexpected error while defensively cleaning up after possible derelict submodule directories")
}
}
// Then, repeat just in case there are any nested submodules that went away.
{
cmd := commandContext(
ctx,
"git",
"submodule",
"foreach",
"--recursive",
"git",
"clean", "-x", "-d", "-f", "-f",
)
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unexpected error while defensively cleaning up after possible derelict nested submodule directories")
}
}
return nil
}
func (r *gitRepo) ensureClean(ctx context.Context) error {
cmd := commandContext(
ctx,
"git",
"status",
"--porcelain",
)
cmd.SetDir(r.LocalPath())
out, err := cmd.CombinedOutput()
if err != nil {
// An error on simple git status indicates some aggressive repository
// corruption, outside of the purview that we can deal with here.
return err
}
if len(bytes.TrimSpace(out)) == 0 {
// No output from status indicates a clean tree, without any modified or
// untracked files - we're in good shape.
return nil
}
// We could be more parsimonious about this, but it's probably not worth it
// - it's a rare case to have to do any cleanup anyway, so when we do, we
// might as well just throw the kitchen sink at it.
cmd = commandContext(
ctx,
"git",
"reset",
"--hard",
)
cmd.SetDir(r.LocalPath())
_, err = cmd.CombinedOutput()
if err != nil {
return err
}
// We also need to git clean -df; just reuse defendAgainstSubmodules here,
// even though it's a bit layer-breaky.
err = r.defendAgainstSubmodules(ctx)
if err != nil {
return err
}
// Check status one last time. If it's still not clean, give up.
cmd = commandContext(
ctx,
"git",
"status",
"--porcelain",
)
cmd.SetDir(r.LocalPath())
out, err = cmd.CombinedOutput()
if err != nil {
return err
}
if len(bytes.TrimSpace(out)) != 0 {
return errors.Errorf("failed to clean up git repository at %s - dirty? corrupted? status output: \n%s", r.LocalPath(), string(out))
}
return nil
}
type bzrRepo struct {
*vcs.BzrRepo
}
func (r *bzrRepo) get(ctx context.Context) error {
basePath := filepath.Dir(filepath.FromSlash(r.LocalPath()))
if _, err := os.Stat(basePath); os.IsNotExist(err) {
err = os.MkdirAll(basePath, 0755)
if err != nil {
return newVcsLocalErrorOr(err, nil, "", "unable to create directory")
}
}
cmd := commandContext(ctx, "bzr", "branch", r.Remote(), r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to get repository")
}
return nil
}
func (r *bzrRepo) fetch(ctx context.Context) error {
cmd := commandContext(ctx, "bzr", "pull")
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to update repository")
}
return nil
}
func (r *bzrRepo) updateVersion(ctx context.Context, version string) error {
cmd := commandContext(ctx, "bzr", "update", "-r", version)
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to update checked out version")
}
return nil
}
type hgRepo struct {
*vcs.HgRepo
}
func (r *hgRepo) get(ctx context.Context) error {
cmd := commandContext(ctx, "hg", "clone", r.Remote(), r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to get repository")
}
return nil
}
func (r *hgRepo) fetch(ctx context.Context) error {
cmd := commandContext(ctx, "hg", "pull")
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to fetch latest changes")
}
return nil
}
func (r *hgRepo) updateVersion(ctx context.Context, version string) error {
cmd := commandContext(ctx, "hg", "update", version)
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to update checked out version")
}
return nil
}
type svnRepo struct {
*vcs.SvnRepo
}
func (r *svnRepo) get(ctx context.Context) error {
remote := r.Remote()
if strings.HasPrefix(remote, "/") {
remote = "file://" + remote
} else if runtime.GOOS == "windows" && filepath.VolumeName(remote) != "" {
remote = "file:///" + remote
}
cmd := commandContext(ctx, "svn", "checkout", remote, r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to get repository")
}
return nil
}
func (r *svnRepo) fetch(ctx context.Context) error {
cmd := commandContext(ctx, "svn", "update")
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to update repository")
}
return nil
}
func (r *svnRepo) updateVersion(ctx context.Context, version string) error {
cmd := commandContext(ctx, "svn", "update", "-r", version)
cmd.SetDir(r.LocalPath())
if out, err := cmd.CombinedOutput(); err != nil {
return newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to update checked out version")
}
return nil
}
func (r *svnRepo) CommitInfo(id string) (*vcs.CommitInfo, error) {
ctx := context.TODO()
// There are cases where Svn log doesn't return anything for HEAD or BASE.
// svn info does provide details for these but does not have elements like
// the commit message.
if id == "HEAD" || id == "BASE" {
type commit struct {
Revision string `xml:"revision,attr"`
}
type info struct {
Commit commit `xml:"entry>commit"`
}
cmd := commandContext(ctx, "svn", "info", "-r", id, "--xml")
cmd.SetDir(r.LocalPath())
out, err := cmd.CombinedOutput()
if err != nil {
return nil, newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to retrieve commit information")
}
infos := new(info)
if err := xml.Unmarshal(out, &infos); err != nil {
return nil, newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to retrieve commit information")
}
id = infos.Commit.Revision
if id == "" {
return nil, vcs.ErrRevisionUnavailable
}
}
cmd := commandContext(ctx, "svn", "log", "-r", id, "--xml")
cmd.SetDir(r.LocalPath())
out, err := cmd.CombinedOutput()
if err != nil {
return nil, newVcsRemoteErrorOr(err, cmd.Args(), string(out),
"unable to retrieve commit information")
}
type logentry struct {
Author string `xml:"author"`
Date string `xml:"date"`
Msg string `xml:"msg"`
}
type log struct {
XMLName xml.Name `xml:"log"`
Logs []logentry `xml:"logentry"`
}
logs := new(log)
if err := xml.Unmarshal(out, &logs); err != nil {
return nil, newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to retrieve commit information")
}
if len(logs.Logs) == 0 {
return nil, vcs.ErrRevisionUnavailable
}
ci := &vcs.CommitInfo{
Commit: id,
Author: logs.Logs[0].Author,
Message: logs.Logs[0].Msg,
}
if len(logs.Logs[0].Date) > 0 {
ci.Date, err = time.Parse(time.RFC3339Nano, logs.Logs[0].Date)
if err != nil {
return nil, newVcsLocalErrorOr(err, cmd.Args(), string(out),
"unable to retrieve commit information")
}
}
return ci, nil
}
马建仓 AI 助手
尝试更多
代码解读
代码找茬
代码优化
1
https://gitee.com/liboxwz/dep.git
git@gitee.com:liboxwz/dep.git
liboxwz
dep
dep
v0.5.1

搜索帮助