mirror of
https://github.com/grdl/git-get.git
synced 2026-02-04 22:04:41 +00:00
Add --skip-host flag to get command (#7)
When set, git-get won't create a directory for the repo host. So instead of `<root>/<host>/<user>/<repo>`, a repo will be cloned into `<root>/<user>/<repo>`. It's useful if all repos some from the same host and that additional folder feels redundant.
This commit is contained in:
committed by
GitHub
parent
0097681f89
commit
0064fc3b8e
@@ -67,6 +67,7 @@ Flags:
|
||||
-h, --help Print this help and exit.
|
||||
-t, --host Host to use when <REPO> doesn't have a specified host. (default "github.com")
|
||||
-r, --root Path to repos root where repositories are cloned. (default "~/repositories")
|
||||
-s, --skip-host Don't create a directory for host.
|
||||
-v, --version Print version and exit.
|
||||
```
|
||||
|
||||
@@ -143,11 +144,14 @@ export GITGET_ROOT=/path/to/my/repos
|
||||
|
||||
You can define a `[gitget]` section inside your global `.gitconfig` file and set the configuration flags there. A recommended pattern is to set `root` and `host` variables there if you don't want to use the defaults.
|
||||
|
||||
If all of your repos come from the same host and you find creating directory for it redundant, you can use the `skip-host` flag to skip creating it.
|
||||
|
||||
Here's an example of a working snippet from `.gitconfig` file:
|
||||
```
|
||||
[gitget]
|
||||
root = /path/to/my/repos
|
||||
host = gitlab.com
|
||||
skip-host = true
|
||||
```
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ func init() {
|
||||
cmd.PersistentFlags().StringP(cfg.KeyBranch, "b", "", "Branch (or tag) to checkout after cloning.")
|
||||
cmd.PersistentFlags().StringP(cfg.KeyDefaultHost, "t", cfg.Defaults[cfg.KeyDefaultHost], "Host to use when <REPO> doesn't have a specified host.")
|
||||
cmd.PersistentFlags().StringP(cfg.KeyDump, "d", "", "Path to a dump file listing repos to clone. Ignored when <REPO> argument is used.")
|
||||
cmd.PersistentFlags().BoolP(cfg.KeySkipHost, "s", false, "Don't create a directory for host.")
|
||||
cmd.PersistentFlags().StringP(cfg.KeyReposRoot, "r", cfg.Defaults[cfg.KeyReposRoot], "Path to repos root where repositories are cloned.")
|
||||
cmd.PersistentFlags().BoolP("help", "h", false, "Print this help and exit.")
|
||||
cmd.PersistentFlags().BoolP("version", "v", false, "Print version and exit.")
|
||||
@@ -36,6 +37,7 @@ func init() {
|
||||
viper.BindPFlag(cfg.KeyDefaultHost, cmd.PersistentFlags().Lookup(cfg.KeyDefaultHost))
|
||||
viper.BindPFlag(cfg.KeyDump, cmd.PersistentFlags().Lookup(cfg.KeyDump))
|
||||
viper.BindPFlag(cfg.KeyReposRoot, cmd.PersistentFlags().Lookup(cfg.KeyReposRoot))
|
||||
viper.BindPFlag(cfg.KeySkipHost, cmd.PersistentFlags().Lookup(cfg.KeySkipHost))
|
||||
|
||||
cfg.Init(&git.ConfigGlobal{})
|
||||
}
|
||||
@@ -49,11 +51,12 @@ func run(cmd *cobra.Command, args []string) error {
|
||||
cfg.Expand(cfg.KeyReposRoot)
|
||||
|
||||
config := &pkg.GetCfg{
|
||||
Branch: viper.GetString(cfg.KeyBranch),
|
||||
DefHost: viper.GetString(cfg.KeyDefaultHost),
|
||||
Dump: viper.GetString(cfg.KeyDump),
|
||||
Root: viper.GetString(cfg.KeyReposRoot),
|
||||
URL: url,
|
||||
Branch: viper.GetString(cfg.KeyBranch),
|
||||
DefHost: viper.GetString(cfg.KeyDefaultHost),
|
||||
Dump: viper.GetString(cfg.KeyDump),
|
||||
SkipHost: viper.GetBool(cfg.KeySkipHost),
|
||||
Root: viper.GetString(cfg.KeyReposRoot),
|
||||
URL: url,
|
||||
}
|
||||
return pkg.Get(config)
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ var (
|
||||
KeyDefaultHost = "host"
|
||||
KeyFetch = "fetch"
|
||||
KeyOutput = "out"
|
||||
KeySkipHost = "skip-host"
|
||||
KeyReposRoot = "root"
|
||||
)
|
||||
|
||||
@@ -30,6 +31,7 @@ var Defaults = map[string]string{
|
||||
KeyDefaultHost: "github.com",
|
||||
KeyOutput: OutTree,
|
||||
KeyReposRoot: fmt.Sprintf("~%c%s", filepath.Separator, "repositories"),
|
||||
// KeySkipHost: "false",
|
||||
}
|
||||
|
||||
// Values for the --out flag.
|
||||
@@ -78,6 +80,7 @@ func Init(cfg Gitconfig) {
|
||||
func readGitconfig(cfg Gitconfig) {
|
||||
var lines []string
|
||||
|
||||
// TODO: Can we somehow iterate over all possible flags?
|
||||
for key := range Defaults {
|
||||
if val := cfg.Get(fmt.Sprintf("%s.%s", GitgetPrefix, key)); val != "" {
|
||||
lines = append(lines, fmt.Sprintf("%s=%s", key, val))
|
||||
@@ -86,6 +89,11 @@ func readGitconfig(cfg Gitconfig) {
|
||||
|
||||
viper.SetConfigType("env")
|
||||
viper.ReadConfig(bytes.NewBuffer([]byte(strings.Join(lines, "\n"))))
|
||||
|
||||
// TODO: A hacky way to read boolean flag from gitconfig. Find a cleaner way.
|
||||
if val := cfg.Get(fmt.Sprintf("%s.%s", GitgetPrefix, KeySkipHost)); strings.ToLower(val) == "true" {
|
||||
viper.Set(KeySkipHost, true)
|
||||
}
|
||||
}
|
||||
|
||||
// Expand applies the variables expansion to a viper config of given key.
|
||||
|
||||
15
pkg/get.go
15
pkg/get.go
@@ -9,11 +9,12 @@ import (
|
||||
|
||||
// GetCfg provides configuration for the Get command.
|
||||
type GetCfg struct {
|
||||
Branch string
|
||||
DefHost string
|
||||
Dump string
|
||||
Root string
|
||||
URL string
|
||||
Branch string
|
||||
DefHost string
|
||||
Dump string
|
||||
Root string
|
||||
SkipHost bool
|
||||
URL string
|
||||
}
|
||||
|
||||
// Get executes the "git get" command.
|
||||
@@ -40,7 +41,7 @@ func cloneSingleRepo(c *GetCfg) error {
|
||||
|
||||
opts := &git.CloneOpts{
|
||||
URL: url,
|
||||
Path: filepath.Join(c.Root, URLToPath(url)),
|
||||
Path: filepath.Join(c.Root, URLToPath(*url, c.SkipHost)),
|
||||
Branch: c.Branch,
|
||||
}
|
||||
|
||||
@@ -63,7 +64,7 @@ func cloneDumpFile(c *GetCfg) error {
|
||||
|
||||
opts := &git.CloneOpts{
|
||||
URL: url,
|
||||
Path: filepath.Join(c.Root, URLToPath(url)),
|
||||
Path: filepath.Join(c.Root, URLToPath(*url, c.SkipHost)),
|
||||
Branch: line.branch,
|
||||
}
|
||||
|
||||
|
||||
39
pkg/url.go
39
pkg/url.go
@@ -2,6 +2,7 @@ package pkg
|
||||
|
||||
import (
|
||||
urlpkg "net/url"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strings"
|
||||
@@ -52,6 +53,12 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
|
||||
url.Host = defaultHost
|
||||
}
|
||||
|
||||
// Don't use host when scheme is file://. The fragment detected as url.Host should be a first directory of url.Path
|
||||
if url.Scheme == "file" && url.Host != "" {
|
||||
url.Path = path.Join(url.Host, url.Path)
|
||||
url.Host = ""
|
||||
}
|
||||
|
||||
// Default to https when scheme is empty
|
||||
if url.Scheme == "" {
|
||||
url.Scheme = "https"
|
||||
@@ -60,18 +67,30 @@ func ParseURL(rawURL string, defaultHost string) (url *urlpkg.URL, err error) {
|
||||
return url, nil
|
||||
}
|
||||
|
||||
// URLToPath cleans up the URL and converts it into a path string.
|
||||
// URLToPath cleans up the URL and converts it into a path string with correct separators for the current OS.
|
||||
// Eg, ssh://git@github.com:22/~user/repo.git => github.com/user/repo
|
||||
func URLToPath(url *urlpkg.URL) (repoPath string) {
|
||||
// Remove port numbers from host
|
||||
repoHost := strings.Split(url.Host, ":")[0]
|
||||
//
|
||||
// If skipHost is true, it removes the host part from the path.
|
||||
// Eg, ssh://git@github.com:22/~user/repo.git => user/repo
|
||||
func URLToPath(url urlpkg.URL, skipHost bool) string {
|
||||
// Remove port numbers from host.
|
||||
url.Host = strings.Split(url.Host, ":")[0]
|
||||
|
||||
// Remove trailing ".git" from repo name
|
||||
repoPath = filepath.Join(repoHost, url.Path)
|
||||
repoPath = strings.TrimSuffix(repoPath, ".git")
|
||||
// Remove tilde (~) char from username.
|
||||
url.Path = strings.ReplaceAll(url.Path, "~", "")
|
||||
|
||||
// Remove tilde (~) char from username
|
||||
repoPath = strings.ReplaceAll(repoPath, "~", "")
|
||||
// Remove leading and trailing slashes (correct separator is added by the filepath.Join() below).
|
||||
url.Path = strings.Trim(url.Path, "/")
|
||||
|
||||
return repoPath
|
||||
// Remove trailing ".git" from repo name.
|
||||
url.Path = strings.TrimSuffix(url.Path, ".git")
|
||||
|
||||
// Replace slashes with separator correct for the current OS.
|
||||
url.Path = strings.ReplaceAll(url.Path, "/", string(filepath.Separator))
|
||||
|
||||
if skipHost {
|
||||
url.Host = ""
|
||||
}
|
||||
|
||||
return filepath.Join(url.Host, url.Path)
|
||||
}
|
||||
|
||||
@@ -52,13 +52,58 @@ func TestURLParse(t *testing.T) {
|
||||
for _, test := range tests {
|
||||
url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost])
|
||||
if err != nil {
|
||||
t.Errorf("Error parsing Path: %+v", err)
|
||||
t.Fatalf("got error: %+v", err)
|
||||
}
|
||||
|
||||
got := URLToPath(url)
|
||||
got := URLToPath(*url, false)
|
||||
|
||||
if got != test.want {
|
||||
t.Errorf("Wrong result of parsing Path: %s, got: %s; wantBranch: %s", test.in, got, test.want)
|
||||
t.Errorf("wrong result for %q; expected %q; got %q", test.in, test.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
func TestURLParseSkipHost(t *testing.T) {
|
||||
tests := []struct {
|
||||
in string
|
||||
want string
|
||||
}{
|
||||
{"ssh://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"ssh://user@github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"ssh://user@github.com:1234/grdl/git-get.git", "grdl/git-get"},
|
||||
{"ssh://user@github.com/~user/grdl/git-get.git", "user/grdl/git-get"},
|
||||
{"git+ssh://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"git@github.com:grdl/git-get.git", "grdl/git-get"},
|
||||
{"git@github.com:/~user/grdl/git-get.git", "user/grdl/git-get"},
|
||||
{"git://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"git://github.com/~user/grdl/git-get.git", "user/grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"http://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get", "grdl/git-get"},
|
||||
{"https://github.com/git-get.git", "git-get"},
|
||||
{"https://github.com/git-get", "git-get"},
|
||||
{"https://github.com/grdl/sub/path/git-get.git", "grdl/sub/path/git-get"},
|
||||
{"https://github.com:1234/grdl/git-get.git", "grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get.git/", "grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get/", "grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get/////", "grdl/git-get"},
|
||||
{"https://github.com/grdl/git-get.git/////", "grdl/git-get"},
|
||||
{"ftp://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"ftps://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"rsync://github.com/grdl/git-get.git", "grdl/git-get"},
|
||||
{"local/grdl/git-get/", "local/grdl/git-get"},
|
||||
{"file://local/grdl/git-get", "local/grdl/git-get"},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
url, err := ParseURL(test.in, cfg.Defaults[cfg.KeyDefaultHost])
|
||||
if err != nil {
|
||||
t.Fatalf("got error: %+v", err)
|
||||
}
|
||||
|
||||
got := URLToPath(*url, true)
|
||||
|
||||
if got != test.want {
|
||||
t.Errorf("wrong result for %q; expected %q; got %q", test.in, test.want, got)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,10 +118,10 @@ func TestInvalidURLParse(t *testing.T) {
|
||||
//"git@github.com:1234:grdl/git-get.git",
|
||||
}
|
||||
|
||||
for _, in := range invalidURLs {
|
||||
got, err := ParseURL(in, cfg.Defaults[cfg.KeyDefaultHost])
|
||||
for _, test := range invalidURLs {
|
||||
got, err := ParseURL(test, cfg.Defaults[cfg.KeyDefaultHost])
|
||||
if err == nil {
|
||||
t.Errorf("Wrong result of parsing invalid Path: %s, got: %s, wantBranch: error", in, got)
|
||||
t.Errorf("expected error; got %q", got)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user