Append ".git" to directory name for bare git repositories

This commit is contained in:
Wade Zhang 2023-04-03 21:34:16 +08:00
parent 67a22bf1f7
commit e883e7d4d6
9 changed files with 99 additions and 12 deletions

View file

@ -13,6 +13,7 @@ func doCreate(c *cli.Context) error {
name = c.Args().First()
vcs = c.String("vcs")
w = c.App.Writer
bare = c.Bool("bare")
)
if name == "" {
@ -24,7 +25,7 @@ func doCreate(c *cli.Context) error {
return err
}
localRepo, err := LocalRepositoryFromURL(u)
localRepo, err := LocalRepositoryFromURL(u, bare)
if err != nil {
return err
}

View file

@ -81,7 +81,7 @@ func doGet(c *cli.Context) error {
return err
}
if andLook && firstArg != "" {
return look(firstArg)
return look(firstArg, g.bare)
}
return nil
}
@ -121,7 +121,7 @@ func detectShell() string {
return "/bin/sh"
}
func look(name string) error {
func look(name string, bare bool) error {
var (
reposFound []*LocalRepository
mu sync.Mutex
@ -138,7 +138,7 @@ func look(name string) error {
if len(reposFound) == 0 {
if url, err := newURL(name, false, false); err == nil {
repo, err := LocalRepositoryFromURL(url)
repo, err := LocalRepositoryFromURL(url, bare)
if err != nil {
return err
}

View file

@ -206,7 +206,7 @@ func TestCommandGet(t *testing.T) {
}, {
name: "bare",
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo.git")
app.Run([]string{"", "get", "--bare", "motemen/ghq-test-repo"})
@ -263,13 +263,13 @@ func TestLook(t *testing.T) {
t.Errorf("lastCmd.Env[len(lastCmd.Env)-1]: got: %s, expect: %s", gotEnv, expectEnv)
}
err = look("github.com/motemen/_unknown")
err = look("github.com/motemen/_unknown", false)
expect := "no repository found"
if !strings.HasPrefix(fmt.Sprintf("%s", err), expect) {
t.Errorf("error should has prefix %q, but: %s", expect, err)
}
err = look("gobump")
err = look("gobump", false)
expect = "More than one repositories are found; Try more precise name"
if !strings.HasPrefix(fmt.Sprintf("%s", err), expect) {
t.Errorf("error should has prefix %q, but: %s", expect, err)
@ -277,6 +277,52 @@ func TestLook(t *testing.T) {
})
}
func TestBareLook(t *testing.T) {
withFakeGitBackend(t, func(t *testing.T, tmproot string, _ *_cloneArgs, _ *_updateArgs) {
os.MkdirAll(filepath.Join(tmproot, "github.com", "motemen", "ghq.git"), 0o755)
os.MkdirAll(filepath.Join(tmproot, "github.com", "motemen", "gobump", ".git"), 0o755)
defer func(orig func(cmd *exec.Cmd) error) {
cmdutil.CommandRunner = orig
}(cmdutil.CommandRunner)
var lastCmd *exec.Cmd
cmdutil.CommandRunner = func(cmd *exec.Cmd) error {
lastCmd = cmd
return nil
}
sh := detectShell()
err := newApp().Run([]string{"", "get", "--bare", "--look", "https://github.com/motemen/ghq.git"})
if err != nil {
t.Errorf("error should be nil, but: %s", err)
}
if !reflect.DeepEqual(lastCmd.Args, []string{sh}) {
t.Errorf("lastCmd.Args: got: %v, expect: %v", lastCmd.Args, []string{sh})
}
dir := filepath.Join(tmproot, "github.com", "motemen", "ghq.git")
if filepath.Clean(lastCmd.Dir) != dir {
t.Errorf("lastCmd.Dir: got: %s, expect: %s", lastCmd.Dir, dir)
}
gotEnv := lastCmd.Env[len(lastCmd.Env)-1]
expectEnv := "GHQ_LOOK=github.com/motemen/ghq.git"
if gotEnv != expectEnv {
t.Errorf("lastCmd.Env[len(lastCmd.Env)-1]: got: %s, expect: %s", gotEnv, expectEnv)
}
err = look("github.com/motemen/ghq", false)
expect := "no repository found"
if !strings.HasPrefix(fmt.Sprintf("%s", err), expect) {
t.Errorf("error should has prefix %q, but: %s", expect, err)
}
err = look("github.com/motemen/gobump.git", true)
expect = "no repository found"
if !strings.HasPrefix(fmt.Sprintf("%s", err), expect) {
t.Errorf("error should has prefix %q, but: %s", expect, err)
}
})
}
func TestDoGet_bulk(t *testing.T) {
in := []string{
"github.com/x-motemen/ghq",

View file

@ -18,6 +18,7 @@ func doList(c *cli.Context) error {
vcsBackend = c.String("vcs")
printFullPaths = c.Bool("full-path")
printUniquePaths = c.Bool("unique")
bare = c.Bool("bare")
)
filterByQuery := func(_ *LocalRepository) bool {
@ -26,7 +27,7 @@ func doList(c *cli.Context) error {
if query != "" {
if hasSchemePattern.MatchString(query) || scpLikeURLPattern.MatchString(query) {
if url, err := newURL(query, false, false); err == nil {
if repo, err := LocalRepositoryFromURL(url); err == nil {
if repo, err := LocalRepositoryFromURL(url, bare); err == nil {
query = filepath.ToSlash(repo.RelPath)
}
}

View file

@ -54,6 +54,7 @@ var commandList = &cli.Command{
&cli.StringFlag{Name: "vcs", Usage: "Specify `vcs` backend for matching"},
&cli.BoolFlag{Name: "full-path", Aliases: []string{"p"}, Usage: "Print full paths"},
&cli.BoolFlag{Name: "unique", Usage: "Print unique subpaths"},
&cli.BoolFlag{Name: "bare", Usage: "Query bare repositories"},
},
}
@ -72,6 +73,7 @@ var commandCreate = &cli.Command{
Action: doCreate,
Flags: []cli.Flag{
&cli.StringFlag{Name: "vcs", Usage: "Specify `vcs` backend explicitly"},
&cli.BoolFlag{Name: "bare", Usage: "Create a bare repository"},
},
}

View file

@ -46,7 +46,7 @@ func (g *getter) get(argURL string) error {
// If isShallow is true, does shallow cloning. (no effect if already cloned or the VCS is Mercurial and git-svn)
func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) error {
remoteURL := remote.URL()
local, err := LocalRepositoryFromURL(remoteURL)
local, err := LocalRepositoryFromURL(remoteURL, g.bare)
if err != nil {
return err
}
@ -89,6 +89,10 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err
localRepoRoot = filepath.Join(local.RootPath, remoteURL.Hostname(), l)
}
if g.bare {
localRepoRoot = localRepoRoot + ".git"
}
if remoteURL.Scheme == "codecommit" {
repoURL, _ = url.Parse(remoteURL.Opaque)
}
@ -110,11 +114,17 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) err
if vcs == nil {
return fmt.Errorf("failed to detect VCS for %q", fpath)
}
repoURL := remoteURL
if remoteURL.Scheme == "codecommit" {
repoURL, _ = url.Parse(remoteURL.Opaque)
}
if getRepoLock(localRepoRoot) {
return vcs.Update(&vcsGetOption{
url: repoURL,
dir: localRepoRoot,
silent: g.silent,
recursive: g.recursive,
bare: g.bare,
})
}
return nil

View file

@ -72,12 +72,17 @@ func LocalRepositoryFromFullPath(fullPath string, backend *VCSBackend) (*LocalRe
}
// LocalRepositoryFromURL resolve LocalRepository from URL
func LocalRepositoryFromURL(remoteURL *url.URL) (*LocalRepository, error) {
func LocalRepositoryFromURL(remoteURL *url.URL, bare bool) (*LocalRepository, error) {
pathParts := append(
[]string{remoteURL.Hostname()}, strings.Split(remoteURL.Path, "/")...,
)
relPath := strings.TrimSuffix(filepath.Join(pathParts...), ".git")
pathParts[len(pathParts)-1] = strings.TrimSuffix(pathParts[len(pathParts)-1], ".git")
if bare {
// Force to append ".git" even if remoteURL does not end with ".git".
relPath = relPath + ".git"
pathParts[len(pathParts)-1] = pathParts[len(pathParts)-1] + ".git"
}
var (
localRepository *LocalRepository
@ -232,6 +237,9 @@ func findVCSBackend(fpath, vcs string) *VCSBackend {
if !ok {
return nil
}
if vcsBackend == GitBackend && strings.HasSuffix(fpath, ".git") {
return vcsBackend
}
for _, d := range vcsBackend.Contents {
if _, err := os.Stat(filepath.Join(fpath, d)); err == nil {
return vcsBackend
@ -239,6 +247,9 @@ func findVCSBackend(fpath, vcs string) *VCSBackend {
}
return nil
}
if strings.HasSuffix(fpath, ".git") {
return GitBackend
}
for _, d := range vcsContents {
if _, err := os.Stat(filepath.Join(fpath, d)); err == nil {
return vcsContentsMap[d]

View file

@ -123,7 +123,7 @@ func TestNewLocalRepository(t *testing.T) {
defer func(orig string) { _home = orig }(_home)
_home = ""
homeOnce = &sync.Once{}
r, err := LocalRepositoryFromURL(mustParseURL(tc.url))
r, err := LocalRepositoryFromURL(mustParseURL(tc.url), false)
if err != nil {
t.Errorf("error should be nil but: %s", err)
}
@ -249,6 +249,15 @@ func TestFindVCSBackend(t *testing.T) {
setup func(t *testing.T) (string, string)
expect *VCSBackend
}{{
name: "git-bare",
setup: func(t *testing.T) (string, string) {
dir := newTempDir(t)
dir = dir + ".git"
os.MkdirAll(dir, 0o755)
return dir, ""
},
expect: GitBackend,
}, {
name: "git",
setup: func(t *testing.T) (string, string) {
dir := newTempDir(t)

9
vcs.go
View file

@ -78,6 +78,9 @@ var GitBackend = &VCSBackend{
if _, err := os.Stat(filepath.Join(vg.dir, ".git/svn")); err == nil {
return GitsvnBackend.Update(vg)
}
if vg.bare {
return runInDir(true)(vg.dir, "git", "fetch", vg.url.String(), "*:*")
}
err := runInDir(true)(vg.dir, "git", "rev-parse", "@{upstream}")
if err != nil {
err := runInDir(vg.silent)(vg.dir, "git", "fetch")
@ -96,7 +99,11 @@ var GitBackend = &VCSBackend{
return nil
},
Init: func(dir string) error {
return cmdutil.RunInDir(dir, "git", "init")
args := []string{"init"}
if strings.HasSuffix(dir, ".git") {
args = append(args, "--bare")
}
return cmdutil.RunInDir(dir, "git", args...)
},
Contents: []string{".git"},
}