mirror of
https://github.com/grdl/git-get.git
synced 2026-02-04 15:39:46 +00:00
Update help commands and errors messages
This commit is contained in:
@@ -7,11 +7,12 @@
|
|||||||
`git-get` is a better way to clone, organize and manage multiple git repositories.
|
`git-get` is a better way to clone, organize and manage multiple git repositories.
|
||||||
|
|
||||||
It gives you two new git commands:
|
It gives you two new git commands:
|
||||||
- **`git get`** clones repositories into an organized directory structure (like golang's [`go get`](https://golang.org/cmd/go/)). It's dotfiles friendly - you can clone multiple repositories listed in a file.
|
- **`git get`** clones repositories into an automatically created directory tree based on repo's URL (like golang's [`go get`](https://golang.org/cmd/go/)). It's dotfiles friendly, you can clone multiple repositories listed in a file.
|
||||||
- **`git list`** shows status of all your git repositories and their branches.
|
- **`git list`** shows status of all your git repositories and their branches.
|
||||||
|
|
||||||

|

|
||||||
|
|
||||||
|
|
||||||
## Installation
|
## Installation
|
||||||
|
|
||||||
Using Homebrew:
|
Using Homebrew:
|
||||||
@@ -23,6 +24,7 @@ Or grab the [latest release](https://github.com/grdl/git-get/releases) and put t
|
|||||||
|
|
||||||
Each release contains two binaries: `git-get` and `git-list`. When put on PATH, git automatically recognizes them as custom commands and allows to call them as `git get` or `git list`.
|
Each release contains two binaries: `git-get` and `git-list`. When put on PATH, git automatically recognizes them as custom commands and allows to call them as `git get` or `git list`.
|
||||||
|
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -8,25 +8,35 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
const example = ` git get grdl/git-get
|
||||||
|
git get https://github.com/grdl/git-get.git
|
||||||
|
git get git@github.com:grdl/git-get.git
|
||||||
|
git get -d path/to/dump/file`
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
var cmd = &cobra.Command{
|
||||||
Use: "git-get <repo>",
|
Use: "git get <REPO>",
|
||||||
Short: "git get",
|
Short: "Clone git repository into an automatically created directory tree based on the repo's URL.",
|
||||||
|
Example: example,
|
||||||
RunE: run,
|
RunE: run,
|
||||||
Args: cobra.MaximumNArgs(1), // TODO: add custom validator
|
Args: cobra.MaximumNArgs(1), // TODO: add custom validator
|
||||||
Version: cfg.Version(),
|
Version: cfg.Version(),
|
||||||
SilenceUsage: true,
|
SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", "", "repos root")
|
cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", cfg.DefBranch, "Branch (or tag) to checkout after cloning. Tag name needs to be prefixed with 'refs/tags/'.")
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyPrivateKey, "p", "", "SSH private key path")
|
cmd.PersistentFlags().StringP(cfg.KeyDefaultHost, "t", cfg.DefDefaultHost, "Host to use when <REPO> doesn't have a specified host.")
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyDump, "d", "", "Dump file path")
|
cmd.PersistentFlags().StringP(cfg.KeyDump, "d", "", "Path to a dump file listing repos to clone. Ignored when <REPO> argument is used.")
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", cfg.DefBranch, "Branch (or tag) to checkout after cloning")
|
cmd.PersistentFlags().StringP(cfg.KeyPrivateKey, "p", "", "Path to SSH private key. (default \"~/.ssh/id_rsa\")")
|
||||||
|
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", "", "Path to repos root where repositories are cloned. (default \"~/repositories\")")
|
||||||
|
cmd.PersistentFlags().BoolP("help", "h", false, "Print this help and exit.")
|
||||||
|
cmd.PersistentFlags().BoolP("version", "v", false, "Print version and exit.")
|
||||||
|
|
||||||
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
|
||||||
viper.BindPFlag(cfg.KeyPrivateKey, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
|
||||||
viper.BindPFlag(cfg.KeyDump, cmd.PersistentFlags().Lookup(cfg.KeyDump))
|
|
||||||
viper.BindPFlag(cfg.KeyBranch, cmd.PersistentFlags().Lookup(cfg.KeyBranch))
|
viper.BindPFlag(cfg.KeyBranch, cmd.PersistentFlags().Lookup(cfg.KeyBranch))
|
||||||
|
viper.BindPFlag(cfg.KeyDefaultHost, cmd.PersistentFlags().Lookup(cfg.KeyDefaultHost))
|
||||||
|
viper.BindPFlag(cfg.KeyDump, cmd.PersistentFlags().Lookup(cfg.KeyDump))
|
||||||
|
viper.BindPFlag(cfg.KeyPrivateKey, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
||||||
|
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) error {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
|
|||||||
@@ -1,32 +1,37 @@
|
|||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"git-get/pkg"
|
"git-get/pkg"
|
||||||
"git-get/pkg/cfg"
|
"git-get/pkg/cfg"
|
||||||
|
"strings"
|
||||||
|
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
var cmd = &cobra.Command{
|
var cmd = &cobra.Command{
|
||||||
Use: "git-list",
|
Use: "git list",
|
||||||
Short: "git list",
|
Short: "List all repositories cloned by 'git get' and their status.",
|
||||||
RunE: run,
|
RunE: run,
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
Version: cfg.Version(),
|
Version: cfg.Version(),
|
||||||
SilenceUsage: true,
|
SilenceUsage: true, // We don't want to show usage on legit errors (eg, wrong path, repo already existing etc.)
|
||||||
}
|
}
|
||||||
|
|
||||||
func init() {
|
func init() {
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", "", "repos root")
|
cmd.PersistentFlags().BoolP(cfg.KeyFetch, "f", false, "First fetch from remotes before listing repositories.")
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyPrivateKey, "p", "", "SSH private key path")
|
cmd.PersistentFlags().StringP(cfg.KeyOutput, "o", cfg.DefOutput, fmt.Sprintf("Output format. Allowed values: [%s].", strings.Join(cfg.AllowedOut, ", ")))
|
||||||
cmd.PersistentFlags().BoolP(cfg.KeyFetch, "f", false, "Fetch from remotes when listing repositories")
|
cmd.PersistentFlags().StringP(cfg.KeyPrivateKey, "p", "", "Path to SSH private key. (default \"~/.ssh/id_rsa\")")
|
||||||
cmd.PersistentFlags().StringP(cfg.KeyOutput, "o", cfg.DefOutput, "output format.")
|
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", "", "Path to repos root where repositories are cloned. (default \"~/repositories\")")
|
||||||
|
cmd.PersistentFlags().BoolP("help", "h", false, "Print this help and exit.")
|
||||||
|
cmd.PersistentFlags().BoolP("version", "v", false, "Print version and exit.")
|
||||||
|
|
||||||
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
|
||||||
viper.BindPFlag(cfg.KeyPrivateKey, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
|
||||||
viper.BindPFlag(cfg.KeyFetch, cmd.PersistentFlags().Lookup(cfg.KeyFetch))
|
viper.BindPFlag(cfg.KeyFetch, cmd.PersistentFlags().Lookup(cfg.KeyFetch))
|
||||||
viper.BindPFlag(cfg.KeyOutput, cmd.PersistentFlags().Lookup(cfg.KeyOutput))
|
viper.BindPFlag(cfg.KeyOutput, cmd.PersistentFlags().Lookup(cfg.KeyOutput))
|
||||||
|
viper.BindPFlag(cfg.KeyPrivateKey, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
||||||
|
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func run(cmd *cobra.Command, args []string) error {
|
func run(cmd *cobra.Command, args []string) error {
|
||||||
|
|||||||
@@ -11,33 +11,36 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Gitconfig section name and env var prefix
|
// GitgetPrefix is the name of the gitconfig section name and the env var prefix.
|
||||||
const GitgetPrefix = "gitget"
|
const GitgetPrefix = "gitget"
|
||||||
|
|
||||||
// Flag keys and their default values
|
// CLI flag keys and their default values.
|
||||||
const (
|
const (
|
||||||
KeyBranch = "branch"
|
KeyBranch = "branch"
|
||||||
DefBranch = "master"
|
DefBranch = "master"
|
||||||
KeyDump = "dump"
|
KeyDump = "dump"
|
||||||
KeyDefaultHost = "defaultHost"
|
KeyDefaultHost = "host"
|
||||||
DefDefaultHost = "github.com"
|
DefDefaultHost = "github.com"
|
||||||
KeyFetch = "fetch"
|
KeyFetch = "fetch"
|
||||||
KeyOutput = "out"
|
KeyOutput = "out"
|
||||||
DefOutput = OutTree
|
DefOutput = OutTree
|
||||||
KeyPrivateKey = "privateKey"
|
KeyPrivateKey = "privateKey"
|
||||||
DefPrivateKey = "id_rsa"
|
DefPrivateKey = "id_rsa"
|
||||||
KeyReposRoot = "reposRoot"
|
KeyReposRoot = "root"
|
||||||
DefReposRoot = "repositories"
|
DefReposRoot = "repositories"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Allowed values for the --out flag
|
// Values for the --out flag.
|
||||||
const (
|
const (
|
||||||
OutDump = "dump"
|
OutDump = "dump"
|
||||||
OutFlat = "flat"
|
OutFlat = "flat"
|
||||||
OutTree = "tree"
|
|
||||||
OutSmart = "smart"
|
OutSmart = "smart"
|
||||||
|
OutTree = "tree"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// AllowedOut are allowed values for the --out flag.
|
||||||
|
var AllowedOut = []string{OutDump, OutFlat, OutSmart, OutTree}
|
||||||
|
|
||||||
// Version metadata set by ldflags during the build.
|
// Version metadata set by ldflags during the build.
|
||||||
var (
|
var (
|
||||||
version string
|
version string
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package cfg
|
package cfg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path"
|
"path"
|
||||||
"strings"
|
"strings"
|
||||||
@@ -10,9 +11,9 @@ import (
|
|||||||
"github.com/spf13/viper"
|
"github.com/spf13/viper"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
var (
|
||||||
EnvDefaultHost = "GITGET_DEFAULTHOST"
|
envDefaultHost = strings.ToUpper(fmt.Sprintf("%s_%s", GitgetPrefix, KeyDefaultHost))
|
||||||
EnvReposRoot = "GITGET_REPOSROOT"
|
envReposRoot = strings.ToUpper(fmt.Sprintf("%s_%s", GitgetPrefix, KeyReposRoot))
|
||||||
)
|
)
|
||||||
|
|
||||||
func newConfigWithFullGitconfig() *gitconfig {
|
func newConfigWithFullGitconfig() *gitconfig {
|
||||||
@@ -64,8 +65,8 @@ func newConfigWithEmptyGitconfig() *gitconfig {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func newConfigWithEnvVars() *gitconfig {
|
func newConfigWithEnvVars() *gitconfig {
|
||||||
_ = os.Setenv(EnvDefaultHost, "env.host")
|
_ = os.Setenv(envDefaultHost, "env.host")
|
||||||
_ = os.Setenv(EnvReposRoot, "env.root")
|
_ = os.Setenv(envReposRoot, "env.root")
|
||||||
|
|
||||||
return &gitconfig{
|
return &gitconfig{
|
||||||
Config: nil,
|
Config: nil,
|
||||||
@@ -79,8 +80,8 @@ func newConfigWithGitconfigAndEnvVars() *gitconfig {
|
|||||||
gitget.AddOption(KeyReposRoot, "file.root")
|
gitget.AddOption(KeyReposRoot, "file.root")
|
||||||
gitget.AddOption(KeyDefaultHost, "file.host")
|
gitget.AddOption(KeyDefaultHost, "file.host")
|
||||||
|
|
||||||
_ = os.Setenv(EnvDefaultHost, "env.host")
|
_ = os.Setenv(envDefaultHost, "env.host")
|
||||||
_ = os.Setenv(EnvReposRoot, "env.root")
|
_ = os.Setenv(envReposRoot, "env.root")
|
||||||
|
|
||||||
return &gitconfig{
|
return &gitconfig{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
@@ -92,8 +93,8 @@ func newConfigWithEmptySectionAndEnvVars() *gitconfig {
|
|||||||
|
|
||||||
_ = cfg.Raw.Section(GitgetPrefix)
|
_ = cfg.Raw.Section(GitgetPrefix)
|
||||||
|
|
||||||
_ = os.Setenv(EnvDefaultHost, "env.host")
|
_ = os.Setenv(envDefaultHost, "env.host")
|
||||||
_ = os.Setenv(EnvReposRoot, "env.root")
|
_ = os.Setenv(envReposRoot, "env.root")
|
||||||
|
|
||||||
return &gitconfig{
|
return &gitconfig{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
@@ -107,7 +108,7 @@ func newConfigWithMixed() *gitconfig {
|
|||||||
gitget.AddOption(KeyReposRoot, "file.root")
|
gitget.AddOption(KeyReposRoot, "file.root")
|
||||||
gitget.AddOption(KeyDefaultHost, "file.host")
|
gitget.AddOption(KeyDefaultHost, "file.host")
|
||||||
|
|
||||||
_ = os.Setenv(EnvDefaultHost, "env.host")
|
_ = os.Setenv(envDefaultHost, "env.host")
|
||||||
|
|
||||||
return &gitconfig{
|
return &gitconfig{
|
||||||
Config: cfg,
|
Config: cfg,
|
||||||
@@ -150,9 +151,9 @@ func TestConfig(t *testing.T) {
|
|||||||
|
|
||||||
// Unset env variables and reset viper registry after each test
|
// Unset env variables and reset viper registry after each test
|
||||||
viper.Reset()
|
viper.Reset()
|
||||||
err := os.Unsetenv(EnvDefaultHost)
|
err := os.Unsetenv(envDefaultHost)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
err = os.Unsetenv(EnvReposRoot)
|
err = os.Unsetenv(envReposRoot)
|
||||||
checkFatal(t, err)
|
checkFatal(t, err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,8 +9,8 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
var (
|
var (
|
||||||
errInvalidNumberOfElements = errors.New("More than two space-separated 2 elements on the line")
|
errInvalidNumberOfElements = errors.New("more than two space-separated 2 elements on the line")
|
||||||
errEmptyLine = errors.New("Empty line")
|
errEmptyLine = errors.New("empty line")
|
||||||
)
|
)
|
||||||
|
|
||||||
type parsedLine struct {
|
type parsedLine struct {
|
||||||
@@ -22,7 +22,7 @@ type parsedLine struct {
|
|||||||
func parseDumpFile(path string) ([]parsedLine, error) {
|
func parseDumpFile(path string) ([]parsedLine, error) {
|
||||||
file, err := os.Open(path)
|
file, err := os.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failed opening dump file %s", path)
|
return nil, errors.Wrapf(err, "failed opening dump file %s", path)
|
||||||
}
|
}
|
||||||
defer file.Close()
|
defer file.Close()
|
||||||
|
|
||||||
@@ -34,7 +34,7 @@ func parseDumpFile(path string) ([]parsedLine, error) {
|
|||||||
line++
|
line++
|
||||||
parsed, err := parseLine(scanner.Text())
|
parsed, err := parseLine(scanner.Text())
|
||||||
if err != nil && !errors.Is(errEmptyLine, err) {
|
if err != nil && !errors.Is(errEmptyLine, err) {
|
||||||
return nil, errors.Wrapf(err, "Failed parsing line %d", line)
|
return nil, errors.Wrapf(err, "failed parsing dump file line %d", line)
|
||||||
}
|
}
|
||||||
|
|
||||||
parsedLines = append(parsedLines, parsed)
|
parsedLines = append(parsedLines, parsed)
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
package pkg
|
package pkg
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"fmt"
|
||||||
"git-get/pkg/repo"
|
"git-get/pkg/repo"
|
||||||
"path"
|
"path"
|
||||||
)
|
)
|
||||||
@@ -16,7 +17,9 @@ type GetCfg struct {
|
|||||||
|
|
||||||
// Get executes the "git get" command.
|
// Get executes the "git get" command.
|
||||||
func Get(c *GetCfg) error {
|
func Get(c *GetCfg) error {
|
||||||
// TODO: show something when no args
|
if c.URL == "" && c.Dump == "" {
|
||||||
|
return fmt.Errorf("missing <REPO> argument or --dump flag")
|
||||||
|
}
|
||||||
|
|
||||||
if c.URL != "" {
|
if c.URL != "" {
|
||||||
return cloneSingleRepo(c)
|
return cloneSingleRepo(c)
|
||||||
|
|||||||
@@ -51,8 +51,7 @@ func List(c *ListCfg) error {
|
|||||||
case cfg.OutDump:
|
case cfg.OutDump:
|
||||||
printer = &print.DumpPrinter{}
|
printer = &print.DumpPrinter{}
|
||||||
default:
|
default:
|
||||||
// TODO: fix
|
return fmt.Errorf("invalid --out flag; allowed values: [%s]", strings.Join(cfg.AllowedOut, ", "))
|
||||||
return fmt.Errorf("invalid --out flag; allowed values: %v", []string{cfg.OutFlat, cfg.OutTree, cfg.OutSmart})
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fmt.Println(printer.Print(c.Root, repos))
|
fmt.Println(printer.Print(c.Root, repos))
|
||||||
@@ -63,7 +62,7 @@ func findRepos(root string) ([]string, error) {
|
|||||||
repos = []string{}
|
repos = []string{}
|
||||||
|
|
||||||
if _, err := os.Stat(root); err != nil {
|
if _, err := os.Stat(root); err != nil {
|
||||||
return nil, fmt.Errorf("Repos root %s does not exist or can't be accessed", root)
|
return nil, fmt.Errorf("repos root %s doesn't exist or can't be accessed", root)
|
||||||
}
|
}
|
||||||
|
|
||||||
walkOpts := &godirwalk.Options{
|
walkOpts := &godirwalk.Options{
|
||||||
@@ -79,7 +78,7 @@ func findRepos(root string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if len(repos) == 0 {
|
if len(repos) == 0 {
|
||||||
return nil, fmt.Errorf("No git repos found in repos root %s", root)
|
return nil, fmt.Errorf("no git repos found in root path %s", root)
|
||||||
}
|
}
|
||||||
|
|
||||||
return repos, nil
|
return repos, nil
|
||||||
|
|||||||
@@ -35,6 +35,7 @@ type CloneOpts struct {
|
|||||||
IgnoreExisting bool
|
IgnoreExisting bool
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clone clones repository specified in CloneOpts.
|
||||||
func Clone(opts *CloneOpts) (*Repo, error) {
|
func Clone(opts *CloneOpts) (*Repo, error) {
|
||||||
var progress io.Writer
|
var progress io.Writer
|
||||||
if !opts.Quiet {
|
if !opts.Quiet {
|
||||||
@@ -82,25 +83,27 @@ func Clone(opts *CloneOpts) (*Repo, error) {
|
|||||||
return nil, nil
|
return nil, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil, errors.Wrap(err, "Failed cloning repo")
|
return nil, errors.Wrapf(err, "failed cloning %s", opts.URL.String())
|
||||||
}
|
}
|
||||||
|
|
||||||
return New(repo, opts.Path), nil
|
return New(repo, opts.Path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func Open(repoPath string) (*Repo, error) {
|
// Open opens a repository on a given path.
|
||||||
repo, err := git.PlainOpen(repoPath)
|
func Open(path string) (*Repo, error) {
|
||||||
|
repo, err := git.PlainOpen(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Failed opening repo")
|
return nil, errors.Wrapf(err, "failed opening repo %s", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
return New(repo, repoPath), nil
|
return New(repo, path), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func New(repo *git.Repository, repoPath string) *Repo {
|
// New returns a new Repo instance from a given go-git Repository.
|
||||||
|
func New(repo *git.Repository, path string) *Repo {
|
||||||
return &Repo{
|
return &Repo{
|
||||||
Repository: repo,
|
Repository: repo,
|
||||||
Path: repoPath,
|
Path: path,
|
||||||
Status: &RepoStatus{},
|
Status: &RepoStatus{},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -109,13 +112,13 @@ func New(repo *git.Repository, repoPath string) *Repo {
|
|||||||
func (r *Repo) Fetch() error {
|
func (r *Repo) Fetch() error {
|
||||||
remotes, err := r.Remotes()
|
remotes, err := r.Remotes()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed getting remotes")
|
return errors.Wrapf(err, "failed getting remotes of repo %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, remote := range remotes {
|
for _, remote := range remotes {
|
||||||
err = remote.Fetch(&git.FetchOptions{})
|
err = remote.Fetch(&git.FetchOptions{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrapf(err, "Failed fetching remote %s", remote.Config().Name)
|
return errors.Wrapf(err, "failed fetching remote %s", remote.Config().Name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -126,12 +129,12 @@ func sshKeyAuth() (transport.AuthMethod, error) {
|
|||||||
privateKey := viper.GetString(cfg.KeyPrivateKey)
|
privateKey := viper.GetString(cfg.KeyPrivateKey)
|
||||||
sshKey, err := ioutil.ReadFile(privateKey)
|
sshKey, err := ioutil.ReadFile(privateKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failed to open ssh private key %s", privateKey)
|
return nil, errors.Wrapf(err, "failed to open ssh private key %s", privateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
signer, err := ssh.ParsePrivateKey([]byte(sshKey))
|
signer, err := ssh.ParsePrivateKey([]byte(sshKey))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "Failed to parse ssh private key %s", privateKey)
|
return nil, errors.Wrapf(err, "failed to parse ssh private key %s", privateKey)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: can it ba a different user
|
// TODO: can it ba a different user
|
||||||
@@ -139,6 +142,7 @@ func sshKeyAuth() (transport.AuthMethod, error) {
|
|||||||
return auth, nil
|
return auth, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CurrentBranchStatus returns the BranchStatus of a currently checked out branch.
|
||||||
func (r *Repo) CurrentBranchStatus() *BranchStatus {
|
func (r *Repo) CurrentBranchStatus() *BranchStatus {
|
||||||
if r.Status.CurrentBranch == StatusDetached || r.Status.CurrentBranch == StatusUnknown {
|
if r.Status.CurrentBranch == StatusDetached || r.Status.CurrentBranch == StatusUnknown {
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -48,13 +48,13 @@ func (r *Repo) LoadStatus() error {
|
|||||||
if viper.GetBool(cfg.KeyFetch) {
|
if viper.GetBool(cfg.KeyFetch) {
|
||||||
err := r.Fetch()
|
err := r.Fetch()
|
||||||
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
if err != nil && !errors.Is(err, git.NoErrAlreadyUpToDate) {
|
||||||
return errors.Wrap(err, "Failed fetching from remotes")
|
return errors.Wrapf(err, "failed running git fetch on a repo %s", r.Path)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
wt, err := r.Worktree()
|
wt, err := r.Worktree()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed getting worktree")
|
return errors.Wrapf(err, "failed getting worktree %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// worktree.Status doesn't load gitignore patterns that are defined outside of .gitignore file using excludesfile.
|
// worktree.Status doesn't load gitignore patterns that are defined outside of .gitignore file using excludesfile.
|
||||||
@@ -62,19 +62,19 @@ func (r *Repo) LoadStatus() error {
|
|||||||
// TODO: variables are not expanded so if excludesfile is declared like "~/gitignore_global" or "$HOME/gitignore_global", this will fail to open it
|
// TODO: variables are not expanded so if excludesfile is declared like "~/gitignore_global" or "$HOME/gitignore_global", this will fail to open it
|
||||||
globalPatterns, err := gitignore.LoadGlobalPatterns(osfs.New(""))
|
globalPatterns, err := gitignore.LoadGlobalPatterns(osfs.New(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed loading global gitignore patterns")
|
return errors.Wrap(err, "failed loading global gitignore patterns")
|
||||||
}
|
}
|
||||||
wt.Excludes = append(wt.Excludes, globalPatterns...)
|
wt.Excludes = append(wt.Excludes, globalPatterns...)
|
||||||
|
|
||||||
systemPatterns, err := gitignore.LoadSystemPatterns(osfs.New(""))
|
systemPatterns, err := gitignore.LoadSystemPatterns(osfs.New(""))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed loading system gitignore patterns")
|
return errors.Wrap(err, "failed loading system gitignore patterns")
|
||||||
}
|
}
|
||||||
wt.Excludes = append(wt.Excludes, systemPatterns...)
|
wt.Excludes = append(wt.Excludes, systemPatterns...)
|
||||||
|
|
||||||
status, err := wt.Status()
|
status, err := wt.Status()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed getting worktree status")
|
return errors.Wrapf(err, "failed getting status of worktree %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.Status.HasUncommittedChanges = hasUncommitted(status)
|
r.Status.HasUncommittedChanges = hasUncommitted(status)
|
||||||
@@ -133,7 +133,7 @@ func currentBranch(r *Repo) string {
|
|||||||
func (r *Repo) loadBranchesStatus() error {
|
func (r *Repo) loadBranchesStatus() error {
|
||||||
iter, err := r.Branches()
|
iter, err := r.Branches()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed getting branches iterator")
|
return errors.Wrapf(err, "failed getting branches iterator for repo %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = iter.ForEach(func(reference *plumbing.Reference) error {
|
err = iter.ForEach(func(reference *plumbing.Reference) error {
|
||||||
@@ -146,12 +146,12 @@ func (r *Repo) loadBranchesStatus() error {
|
|||||||
return nil
|
return nil
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.Wrap(err, "Failed iterating over branches")
|
return errors.Wrapf(err, "failed iterating over branches of repo %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sort branches by name (but with "master" always at the top). It's useful to have them sorted for printing and testing.
|
// Sort branches by name (but with "master" always at the top). It's useful to have them sorted for printing and testing.
|
||||||
sort.Slice(r.Status.Branches, func(i, j int) bool {
|
sort.Slice(r.Status.Branches, func(i, j int) bool {
|
||||||
if r.Status.Branches[i].Name == "master" {
|
if r.Status.Branches[i].Name == cfg.DefBranch {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,7 +196,7 @@ func (r *Repo) newBranchStatus(branch string) (*BranchStatus, error) {
|
|||||||
func (r *Repo) upstream(branch string) (string, error) {
|
func (r *Repo) upstream(branch string) (string, error) {
|
||||||
cfg, err := r.Config()
|
cfg, err := r.Config()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", errors.Wrap(err, "Failed getting repo config")
|
return "", errors.Wrapf(err, "failed getting config of repo %s", r.Path)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check if our branch exists in "branch" config sections. If not, it doesn't have an upstream configured.
|
// Check if our branch exists in "branch" config sections. If not, it doesn't have an upstream configured.
|
||||||
@@ -220,22 +220,22 @@ func (r *Repo) upstream(branch string) (string, error) {
|
|||||||
func (r *Repo) aheadBehind(localBranch string, upstreamBranch string) (ahead int, behind int, err error) {
|
func (r *Repo) aheadBehind(localBranch string, upstreamBranch string) (ahead int, behind int, err error) {
|
||||||
localHash, err := r.ResolveRevision(plumbing.Revision(localBranch))
|
localHash, err := r.ResolveRevision(plumbing.Revision(localBranch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, errors.Wrapf(err, "Failed resolving revision %s", localBranch)
|
return 0, 0, errors.Wrapf(err, "failed resolving revision %s", localBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
upstreamHash, err := r.ResolveRevision(plumbing.Revision(upstreamBranch))
|
upstreamHash, err := r.ResolveRevision(plumbing.Revision(upstreamBranch))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, errors.Wrapf(err, "Failed resolving revision %s", upstreamBranch)
|
return 0, 0, errors.Wrapf(err, "failed resolving revision %s", upstreamBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
behind, err = r.revlistCount(*localHash, *upstreamHash)
|
behind, err = r.revlistCount(*localHash, *upstreamHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, errors.Wrapf(err, "Failed counting commits behind %s", upstreamBranch)
|
return 0, 0, errors.Wrapf(err, "failed counting commits behind %s", upstreamBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
ahead, err = r.revlistCount(*upstreamHash, *localHash)
|
ahead, err = r.revlistCount(*upstreamHash, *localHash)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return 0, 0, errors.Wrapf(err, "Failed counting commits ahead of %s", upstreamBranch)
|
return 0, 0, errors.Wrapf(err, "failed counting commits ahead of %s", upstreamBranch)
|
||||||
}
|
}
|
||||||
|
|
||||||
return ahead, behind, nil
|
return ahead, behind, nil
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import (
|
|||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
|
|
||||||
var errEmptyURLPath = errors.New("Parsed URL path is empty")
|
var errEmptyURLPath = errors.New("parsed URL path is empty")
|
||||||
|
|
||||||
// scpSyntax matches the SCP-like addresses used by the ssh protocol (eg, [user@]host.xz:path/to/repo.git/).
|
// scpSyntax matches the SCP-like addresses used by the ssh protocol (eg, [user@]host.xz:path/to/repo.git/).
|
||||||
// See: https://golang.org/src/cmd/go/internal/get/vcs.go
|
// See: https://golang.org/src/cmd/go/internal/get/vcs.go
|
||||||
@@ -30,7 +30,7 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
|
|||||||
} else {
|
} else {
|
||||||
url, err = urlpkg.Parse(rawURL)
|
url, err = urlpkg.Parse(rawURL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrap(err, "Failed parsing URL")
|
return nil, errors.Wrapf(err, "failed parsing URL %s", rawURL)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user