mirror of
https://github.com/x-motemen/ghq.git
synced 2026-09-10 07:26:27 -04:00
feat: add --worktree flag for bare clone with worktree
Closes #428 Signed-off-by: David Jeong <drumrobot43@gmail.com>
This commit is contained in:
parent
4f4abb143d
commit
d243f00208
|
|
@ -26,6 +26,7 @@ func doGet(c *cli.Context) error {
|
|||
parallel = c.Bool("parallel")
|
||||
silent = c.Bool("silent")
|
||||
)
|
||||
worktreePath := c.String("worktree")
|
||||
g := &getter{
|
||||
update: c.Bool("update"),
|
||||
shallow: c.Bool("shallow"),
|
||||
|
|
@ -34,8 +35,9 @@ func doGet(c *cli.Context) error {
|
|||
silent: silent,
|
||||
branch: c.String("branch"),
|
||||
recursive: !c.Bool("no-recursive"),
|
||||
bare: c.Bool("bare"),
|
||||
bare: c.Bool("bare") || worktreePath != "", // --worktree implies --bare
|
||||
partial: c.String("partial"),
|
||||
worktree: worktreePath,
|
||||
}
|
||||
if parallel {
|
||||
// force silent in parallel import
|
||||
|
|
|
|||
|
|
@ -39,6 +39,7 @@ var commandGet = &cli.Command{
|
|||
Usage: "Specify `branch` name. This flag implies --single-branch on Git"},
|
||||
&cli.BoolFlag{Name: "parallel", Aliases: []string{"P"}, Usage: "Import parallelly"},
|
||||
&cli.BoolFlag{Name: "bare", Usage: "Do a bare clone"},
|
||||
&cli.StringFlag{Name: "worktree", Usage: "Create worktree at specified `path` (implies --bare)"},
|
||||
&cli.StringFlag{
|
||||
Name: "partial",
|
||||
Usage: "Do a partial clone. Can specify either \"blobless\" or \"treeless\"",
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type getInfo struct {
|
|||
|
||||
type getter struct {
|
||||
update, shallow, silent, ssh, recursive, bare bool
|
||||
vcs, branch, partial string
|
||||
vcs, branch, partial, worktree string
|
||||
}
|
||||
|
||||
func (g *getter) get(argURL string) (getInfo, error) {
|
||||
|
|
@ -114,6 +114,7 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) (ge
|
|||
recursive: g.recursive,
|
||||
bare: g.bare,
|
||||
partial: g.partial,
|
||||
worktree: g.worktree,
|
||||
})
|
||||
}
|
||||
return info, nil
|
||||
|
|
|
|||
64
vcs.go
64
vcs.go
|
|
@ -44,7 +44,7 @@ type vcsGetOption struct {
|
|||
url *url.URL
|
||||
dir string
|
||||
recursive, shallow, silent, bare bool
|
||||
branch, partial string
|
||||
branch, partial, worktree string
|
||||
}
|
||||
|
||||
// GitBackend is the VCSBackend of git
|
||||
|
|
@ -77,7 +77,15 @@ var GitBackend = &VCSBackend{
|
|||
}
|
||||
args = append(args, vg.url.String(), vg.dir)
|
||||
|
||||
return run(vg.silent)("git", args...)
|
||||
if err := run(vg.silent)("git", args...); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Create worktree if specified
|
||||
if vg.worktree != "" && vg.bare {
|
||||
return createWorktreeFromBare(vg)
|
||||
}
|
||||
return nil
|
||||
},
|
||||
Update: func(vg *vcsGetOption) error {
|
||||
if _, err := os.Stat(filepath.Join(vg.dir, ".git/svn")); err == nil {
|
||||
|
|
@ -398,6 +406,58 @@ var BazaarBackend = &VCSBackend{
|
|||
Contents: []string{".bzr"},
|
||||
}
|
||||
|
||||
// createWorktreeFromBare creates a worktree from a bare repository
|
||||
func createWorktreeFromBare(vg *vcsGetOption) error {
|
||||
// Configure fetch refspec for bare repo
|
||||
if err := runInDir(true)(vg.dir, "git", "config", "remote.origin.fetch", "+refs/heads/*:refs/remotes/origin/*"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runInDir(vg.silent)(vg.dir, "git", "fetch", "origin"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
branch := vg.branch
|
||||
if branch == "" {
|
||||
branch = detectDefaultBranch(vg.dir)
|
||||
}
|
||||
if branch == "" {
|
||||
return fmt.Errorf("could not determine default branch for worktree")
|
||||
}
|
||||
|
||||
localBranchExists := runInDir(true)(vg.dir, "git", "show-ref", "--verify", "--quiet", "refs/heads/"+branch) == nil
|
||||
if localBranchExists {
|
||||
return runInDir(vg.silent)(vg.dir, "git", "worktree", "add", vg.worktree, branch)
|
||||
}
|
||||
return runInDir(vg.silent)(vg.dir, "git", "worktree", "add", "-b", branch, "--track", vg.worktree, "origin/"+branch)
|
||||
}
|
||||
|
||||
// detectDefaultBranch detects the default branch from a bare repository
|
||||
func detectDefaultBranch(dir string) string {
|
||||
// Try symbolic-ref first
|
||||
if out, err := exec.Command("git", "-C", dir, "symbolic-ref", "refs/remotes/origin/HEAD").Output(); err == nil {
|
||||
parts := strings.Split(strings.TrimSpace(string(out)), "/")
|
||||
if len(parts) > 0 {
|
||||
return parts[len(parts)-1]
|
||||
}
|
||||
}
|
||||
// Check common default branches
|
||||
for _, b := range []string{"main", "master"} {
|
||||
if runInDir(true)(dir, "git", "show-ref", "--verify", "--quiet", "refs/remotes/origin/"+b) == nil {
|
||||
return b
|
||||
}
|
||||
}
|
||||
// Use first remote branch
|
||||
if out, err := exec.Command("git", "-C", dir, "branch", "-r").Output(); err == nil {
|
||||
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
|
||||
line = strings.TrimSpace(line)
|
||||
if strings.HasPrefix(line, "origin/") && !strings.Contains(line, "->") {
|
||||
return strings.TrimPrefix(line, "origin/")
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
var vcsRegistry = map[string]*VCSBackend{
|
||||
"git": GitBackend,
|
||||
"github": GitBackend,
|
||||
|
|
|
|||
Loading…
Reference in a new issue