mirror of
https://github.com/x-motemen/ghq.git
synced 2026-09-10 07:26:27 -04:00
Merge pull request #412 from RShirohara/add-partial-clone-support
feat: support partial clone on Git repository
This commit is contained in:
commit
e0e71a3449
|
|
@ -16,7 +16,7 @@ You can also list local repositories (+ghq list+).
|
|||
== SYNOPSIS
|
||||
|
||||
[verse]
|
||||
ghq get [-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch] [--no-recursive] [--bare] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
|
||||
ghq get [-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch] [--no-recursive] [--bare] [--partial blobless|treeless] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
|
||||
ghq list [-p] [-e] [<query>]
|
||||
ghq create [--vcs <vcs>] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
|
||||
ghq rm [--dry-run] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
|
||||
|
|
@ -44,7 +44,10 @@ get::
|
|||
The 'ghq' gets the git repository recursively by default. +
|
||||
We can prevent it with '--no-recursive' option.
|
||||
With '--bare' option, a "bare clone" will be performed (for Git
|
||||
repositories only, 'git clone --bare ...' eg.).
|
||||
repositories only, 'git clone --bare ...' eg.). +
|
||||
With '--partial' option, a "partial clone" will be performed (for Git
|
||||
repositories only, in 'blobless' mode, 'git clone --filter=blob:none ...',
|
||||
in 'treeless' mode, 'git clone --filter=tree:0 ...' eg.).
|
||||
|
||||
list::
|
||||
List locally cloned repositories. If a query argument is given, only
|
||||
|
|
|
|||
|
|
@ -35,6 +35,7 @@ func doGet(c *cli.Context) error {
|
|||
branch: c.String("branch"),
|
||||
recursive: !c.Bool("no-recursive"),
|
||||
bare: c.Bool("bare"),
|
||||
partial: c.String("partial"),
|
||||
}
|
||||
if parallel {
|
||||
// force silent in parallel import
|
||||
|
|
|
|||
|
|
@ -248,6 +248,52 @@ func TestCommandGet(t *testing.T) {
|
|||
t.Errorf("silent mode should not output any logs, but got: %s", out)
|
||||
}
|
||||
},
|
||||
}, {
|
||||
name: "[partial] blobless",
|
||||
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
|
||||
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
|
||||
|
||||
app.Run([]string{"", "get", "--partial", "blobless", "motemen/ghq-test-repo"})
|
||||
|
||||
expect := "https://github.com/motemen/ghq-test-repo"
|
||||
if cloneArgs.remote.String() != expect {
|
||||
t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
|
||||
}
|
||||
if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
|
||||
t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
|
||||
}
|
||||
if cloneArgs.partial != "blobless" {
|
||||
t.Errorf("cloneArgs.partial should be \"blobless\"")
|
||||
}
|
||||
},
|
||||
}, {
|
||||
name: "[partial] treeless",
|
||||
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
|
||||
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
|
||||
|
||||
app.Run([]string{"", "get", "--partial", "treeless", "motemen/ghq-test-repo"})
|
||||
|
||||
expect := "https://github.com/motemen/ghq-test-repo"
|
||||
if cloneArgs.remote.String() != expect {
|
||||
t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
|
||||
}
|
||||
if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
|
||||
t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
|
||||
}
|
||||
if cloneArgs.partial != "treeless" {
|
||||
t.Errorf("cloneArgs.partial should be \"treeless\"")
|
||||
}
|
||||
},
|
||||
}, {
|
||||
name: "[partial] unacceptable value",
|
||||
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
|
||||
err := app.Run([]string{"", "get", "--partial", "unacceptable", "motemen/ghq-test-repo"})
|
||||
|
||||
expect := "flag partial value \"unacceptable\" is not allowed"
|
||||
if err.Error() != expect {
|
||||
t.Errorf("got: %s, expect: %s", err.Error(), expect)
|
||||
}
|
||||
},
|
||||
}}
|
||||
|
||||
for _, tc := range testCases {
|
||||
|
|
|
|||
13
commands.go
13
commands.go
|
|
@ -2,6 +2,7 @@ package main
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"slices"
|
||||
"strings"
|
||||
|
||||
"github.com/urfave/cli/v2"
|
||||
|
|
@ -38,6 +39,16 @@ 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: "partial",
|
||||
Usage: "Do a partial clone. Can specify either \"blobless\" or \"treeless\"",
|
||||
Action: func(ctx *cli.Context, v string) error {
|
||||
expected := []string{"blobless", "treeless"}
|
||||
if !slices.Contains(expected, v) {
|
||||
return fmt.Errorf("flag partial value \"%v\" is not allowed", v)
|
||||
}
|
||||
return nil
|
||||
}},
|
||||
},
|
||||
}
|
||||
|
||||
|
|
@ -94,7 +105,7 @@ type commandDoc struct {
|
|||
}
|
||||
|
||||
var commandDocs = map[string]commandDoc{
|
||||
"get": {"", "[-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch <branch>] [--no-recursive] [--bare] <repository URL>|<project>|<user>/<project>|<host>/<user>/<project>"},
|
||||
"get": {"", "[-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch <branch>] [--no-recursive] [--bare] [--partial blobless|treeless] <repository URL>|<project>|<user>/<project>|<host>/<user>/<project>"},
|
||||
"list": {"", "[-p] [-e] [<query>]"},
|
||||
"create": {"", "<project>|<user>/<project>|<host>/<user>/<project>"},
|
||||
"rm": {"", "<project>|<user>/<project>|<host>/<user>/<project>"},
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ type _cloneArgs struct {
|
|||
recursive bool
|
||||
bare bool
|
||||
silent bool
|
||||
partial string
|
||||
}
|
||||
|
||||
type _updateArgs struct {
|
||||
|
|
@ -41,6 +42,7 @@ func withFakeGitBackend(t *testing.T, block func(*testing.T, string, *_cloneArgs
|
|||
recursive: vg.recursive,
|
||||
bare: vg.bare,
|
||||
silent: vg.silent,
|
||||
partial: vg.partial,
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ type getInfo struct {
|
|||
|
||||
type getter struct {
|
||||
update, shallow, silent, ssh, recursive, bare bool
|
||||
vcs, branch string
|
||||
vcs, branch, partial string
|
||||
}
|
||||
|
||||
func (g *getter) get(argURL string) (getInfo, error) {
|
||||
|
|
@ -113,6 +113,7 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) (ge
|
|||
branch: branch,
|
||||
recursive: g.recursive,
|
||||
bare: g.bare,
|
||||
partial: g.partial,
|
||||
})
|
||||
}
|
||||
return info, nil
|
||||
|
|
|
|||
|
|
@ -24,6 +24,8 @@ export GHQ_ROOT=$tmpdir
|
|||
ghq get https://svn.apache.org/repos/asf/subversion
|
||||
ghq get --shallow hub.darcs.net/byorgey/split
|
||||
ghq get --bare x-motemen/gore
|
||||
ghq get --partial blobless x-motemen/blogsync
|
||||
ghq get --partial treeless x-motemen/gobump
|
||||
|
||||
test -d $tmpdir/github.com/x-motemen/ghq/.git
|
||||
test -d $tmpdir/www.mercurial-scm.org/repo/hello/.hg
|
||||
|
|
@ -34,11 +36,15 @@ export GHQ_ROOT=$tmpdir
|
|||
test -d $tmpdir/svn.apache.org/repos/asf/subversion/.svn
|
||||
test -d $tmpdir/hub.darcs.net/byorgey/split/_darcs
|
||||
test -d $tmpdir/github.com/x-motemen/gore.git/refs
|
||||
grep --quiet "partialclonefilter = blob:none" $tmpdir/github.com/x-motemen/blogsync/.git/config
|
||||
grep --quiet "partialclonefilter = tree:0" $tmpdir/github.com/x-motemen/gobump/.git/config
|
||||
|
||||
: testing 'ghq list'
|
||||
cat <<EOF | sort > $tmpdir/expect
|
||||
chiselapp.com/user/sti/repository/fossil-gui
|
||||
github.com/x-motemen/blogsync
|
||||
github.com/x-motemen/ghq
|
||||
github.com/x-motemen/gobump
|
||||
github.com/x-motemen/gore.git
|
||||
www.mercurial-scm.org/repo/hello
|
||||
launchpad.net/shutter
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ complete -c ghq -n '__fish_seen_subcommand_from get' -l no-recursive -d 'Prevent
|
|||
complete -c ghq -n '__fish_seen_subcommand_from get' -s b -l branch -d 'Specify branch name. This flag implies --single-branch on Git'
|
||||
complete -c ghq -n '__fish_seen_subcommand_from get' -s P -l parallel -d 'Import parallelly'
|
||||
complete -c ghq -n '__fish_seen_subcommand_from get' -l bare -d 'Do a bare clone'
|
||||
function __complete_get_partial
|
||||
printf '%s\t%s\n' 'blobless' 'Do a blobless clone'
|
||||
printf '%s\t%s\n' 'treeless' 'Do a treeless clone'
|
||||
end
|
||||
complete -c ghq -n '__fish_seen_subcommand_from get' -l partial -d 'Do a partial clone' -xa '(__complete_get_partial)'
|
||||
|
||||
complete -c ghq -n '__fish_seen_subcommand_from list' -s e -l exact -d 'Perform an exact match'
|
||||
complete -c ghq -n '__fish_seen_subcommand_from list' -l vcs -d 'Specify vcs backend for matching'
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ function _ghq () {
|
|||
'--bare[Do a bare clone]' \
|
||||
'(-b --branch)'{-b,--branch}'[Specify branch name]' \
|
||||
'(-P --parallel)'{-P,--parallel}'[Import parallelly]' \
|
||||
'--partial[Do a partial clone]: :(blobless treeless)' \
|
||||
'(-)*:: :->null_state' \
|
||||
&& ret=0
|
||||
;;
|
||||
|
|
|
|||
7
vcs.go
7
vcs.go
|
|
@ -44,7 +44,7 @@ type vcsGetOption struct {
|
|||
url *url.URL
|
||||
dir string
|
||||
recursive, shallow, silent, bare bool
|
||||
branch string
|
||||
branch, partial string
|
||||
}
|
||||
|
||||
// GitBackend is the VCSBackend of git
|
||||
|
|
@ -70,6 +70,11 @@ var GitBackend = &VCSBackend{
|
|||
if vg.bare {
|
||||
args = append(args, "--bare")
|
||||
}
|
||||
if vg.partial == "blobless" {
|
||||
args = append(args, "--filter=blob:none")
|
||||
} else if vg.partial == "treeless" {
|
||||
args = append(args, "--filter=tree:0")
|
||||
}
|
||||
args = append(args, vg.url.String(), vg.dir)
|
||||
|
||||
return run(vg.silent)("git", args...)
|
||||
|
|
|
|||
20
vcs_test.go
20
vcs_test.go
|
|
@ -136,6 +136,26 @@ func TestVCSBackend(t *testing.T) {
|
|||
})
|
||||
},
|
||||
expect: []string{"git", "clone", "--bare", remoteDummyURL.String(), localDir},
|
||||
}, {
|
||||
name: "[git] (partial) blobless clone",
|
||||
f: func() error {
|
||||
return GitBackend.Clone(&vcsGetOption{
|
||||
url: remoteDummyURL,
|
||||
dir: localDir,
|
||||
partial: "blobless",
|
||||
})
|
||||
},
|
||||
expect: []string{"git", "clone", "--filter=blob:none", remoteDummyURL.String(), localDir},
|
||||
}, {
|
||||
name: "[git] (partial) treeless clone",
|
||||
f: func() error {
|
||||
return GitBackend.Clone(&vcsGetOption{
|
||||
url: remoteDummyURL,
|
||||
dir: localDir,
|
||||
partial: "treeless",
|
||||
})
|
||||
},
|
||||
expect: []string{"git", "clone", "--filter=tree:0", remoteDummyURL.String(), localDir},
|
||||
}, {
|
||||
name: "[git] switch git-svn on update",
|
||||
f: func() error {
|
||||
|
|
|
|||
Loading…
Reference in a new issue