Skip to content

Commit e0e71a3

Browse files
authored
Merge pull request #412 from RShirohara/add-partial-clone-support
feat: support partial clone on Git repository
2 parents e69c7b1 + 39c5d07 commit e0e71a3

File tree

11 files changed

+106
-5
lines changed

11 files changed

+106
-5
lines changed

README.adoc

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ You can also list local repositories (+ghq list+).
1616
== SYNOPSIS
1717

1818
[verse]
19-
ghq get [-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch] [--no-recursive] [--bare] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
19+
ghq get [-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch] [--no-recursive] [--bare] [--partial blobless|treeless] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
2020
ghq list [-p] [-e] [<query>]
2121
ghq create [--vcs <vcs>] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
2222
ghq rm [--dry-run] <repository URL>|<host>/<user>/<project>|<user>/<project>|<project>
@@ -44,7 +44,10 @@ get::
4444
The 'ghq' gets the git repository recursively by default. +
4545
We can prevent it with '--no-recursive' option.
4646
With '--bare' option, a "bare clone" will be performed (for Git
47-
repositories only, 'git clone --bare ...' eg.).
47+
repositories only, 'git clone --bare ...' eg.). +
48+
With '--partial' option, a "partial clone" will be performed (for Git
49+
repositories only, in 'blobless' mode, 'git clone --filter=blob:none ...',
50+
in 'treeless' mode, 'git clone --filter=tree:0 ...' eg.).
4851

4952
list::
5053
List locally cloned repositories. If a query argument is given, only

cmd_get.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ func doGet(c *cli.Context) error {
3535
branch: c.String("branch"),
3636
recursive: !c.Bool("no-recursive"),
3737
bare: c.Bool("bare"),
38+
partial: c.String("partial"),
3839
}
3940
if parallel {
4041
// force silent in parallel import

cmd_get_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,52 @@ func TestCommandGet(t *testing.T) {
248248
t.Errorf("silent mode should not output any logs, but got: %s", out)
249249
}
250250
},
251+
}, {
252+
name: "[partial] blobless",
253+
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
254+
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
255+
256+
app.Run([]string{"", "get", "--partial", "blobless", "motemen/ghq-test-repo"})
257+
258+
expect := "https://github.com/motemen/ghq-test-repo"
259+
if cloneArgs.remote.String() != expect {
260+
t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
261+
}
262+
if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
263+
t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
264+
}
265+
if cloneArgs.partial != "blobless" {
266+
t.Errorf("cloneArgs.partial should be \"blobless\"")
267+
}
268+
},
269+
}, {
270+
name: "[partial] treeless",
271+
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
272+
localDir := filepath.Join(tmpRoot, "github.com", "motemen", "ghq-test-repo")
273+
274+
app.Run([]string{"", "get", "--partial", "treeless", "motemen/ghq-test-repo"})
275+
276+
expect := "https://github.com/motemen/ghq-test-repo"
277+
if cloneArgs.remote.String() != expect {
278+
t.Errorf("got: %s, expect: %s", cloneArgs.remote, expect)
279+
}
280+
if filepath.ToSlash(cloneArgs.local) != filepath.ToSlash(localDir) {
281+
t.Errorf("got: %s, expect: %s", filepath.ToSlash(cloneArgs.local), filepath.ToSlash(localDir))
282+
}
283+
if cloneArgs.partial != "treeless" {
284+
t.Errorf("cloneArgs.partial should be \"treeless\"")
285+
}
286+
},
287+
}, {
288+
name: "[partial] unacceptable value",
289+
scenario: func(t *testing.T, tmpRoot string, cloneArgs *_cloneArgs, updateArgs *_updateArgs) {
290+
err := app.Run([]string{"", "get", "--partial", "unacceptable", "motemen/ghq-test-repo"})
291+
292+
expect := "flag partial value \"unacceptable\" is not allowed"
293+
if err.Error() != expect {
294+
t.Errorf("got: %s, expect: %s", err.Error(), expect)
295+
}
296+
},
251297
}}
252298

253299
for _, tc := range testCases {

commands.go

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package main
22

33
import (
44
"fmt"
5+
"slices"
56
"strings"
67

78
"github.com/urfave/cli/v2"
@@ -38,6 +39,16 @@ var commandGet = &cli.Command{
3839
Usage: "Specify `branch` name. This flag implies --single-branch on Git"},
3940
&cli.BoolFlag{Name: "parallel", Aliases: []string{"P"}, Usage: "Import parallelly"},
4041
&cli.BoolFlag{Name: "bare", Usage: "Do a bare clone"},
42+
&cli.StringFlag{
43+
Name: "partial",
44+
Usage: "Do a partial clone. Can specify either \"blobless\" or \"treeless\"",
45+
Action: func(ctx *cli.Context, v string) error {
46+
expected := []string{"blobless", "treeless"}
47+
if !slices.Contains(expected, v) {
48+
return fmt.Errorf("flag partial value \"%v\" is not allowed", v)
49+
}
50+
return nil
51+
}},
4152
},
4253
}
4354

@@ -94,7 +105,7 @@ type commandDoc struct {
94105
}
95106

96107
var commandDocs = map[string]commandDoc{
97-
"get": {"", "[-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch <branch>] [--no-recursive] [--bare] <repository URL>|<project>|<user>/<project>|<host>/<user>/<project>"},
108+
"get": {"", "[-u] [-p] [--shallow] [--vcs <vcs>] [--look] [--silent] [--branch <branch>] [--no-recursive] [--bare] [--partial blobless|treeless] <repository URL>|<project>|<user>/<project>|<host>/<user>/<project>"},
98109
"list": {"", "[-p] [-e] [<query>]"},
99110
"create": {"", "<project>|<user>/<project>|<host>/<user>/<project>"},
100111
"rm": {"", "<project>|<user>/<project>|<host>/<user>/<project>"},

commands_test.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ type _cloneArgs struct {
1515
recursive bool
1616
bare bool
1717
silent bool
18+
partial string
1819
}
1920

2021
type _updateArgs struct {
@@ -41,6 +42,7 @@ func withFakeGitBackend(t *testing.T, block func(*testing.T, string, *_cloneArgs
4142
recursive: vg.recursive,
4243
bare: vg.bare,
4344
silent: vg.silent,
45+
partial: vg.partial,
4446
}
4547
return nil
4648
},

getter.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ type getInfo struct {
2525

2626
type getter struct {
2727
update, shallow, silent, ssh, recursive, bare bool
28-
vcs, branch string
28+
vcs, branch, partial string
2929
}
3030

3131
func (g *getter) get(argURL string) (getInfo, error) {
@@ -113,6 +113,7 @@ func (g *getter) getRemoteRepository(remote RemoteRepository, branch string) (ge
113113
branch: branch,
114114
recursive: g.recursive,
115115
bare: g.bare,
116+
partial: g.partial,
116117
})
117118
}
118119
return info, nil

misc/author/integration_test.sh

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,8 @@ export GHQ_ROOT=$tmpdir
2424
ghq get https://svn.apache.org/repos/asf/subversion
2525
ghq get --shallow hub.darcs.net/byorgey/split
2626
ghq get --bare x-motemen/gore
27+
ghq get --partial blobless x-motemen/blogsync
28+
ghq get --partial treeless x-motemen/gobump
2729

2830
test -d $tmpdir/github.com/x-motemen/ghq/.git
2931
test -d $tmpdir/www.mercurial-scm.org/repo/hello/.hg
@@ -34,11 +36,15 @@ export GHQ_ROOT=$tmpdir
3436
test -d $tmpdir/svn.apache.org/repos/asf/subversion/.svn
3537
test -d $tmpdir/hub.darcs.net/byorgey/split/_darcs
3638
test -d $tmpdir/github.com/x-motemen/gore.git/refs
39+
grep --quiet "partialclonefilter = blob:none" $tmpdir/github.com/x-motemen/blogsync/.git/config
40+
grep --quiet "partialclonefilter = tree:0" $tmpdir/github.com/x-motemen/gobump/.git/config
3741

3842
: testing 'ghq list'
3943
cat <<EOF | sort > $tmpdir/expect
4044
chiselapp.com/user/sti/repository/fossil-gui
45+
github.com/x-motemen/blogsync
4146
github.com/x-motemen/ghq
47+
github.com/x-motemen/gobump
4248
github.com/x-motemen/gore.git
4349
www.mercurial-scm.org/repo/hello
4450
launchpad.net/shutter

misc/fish/ghq.fish

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ complete -c ghq -n '__fish_seen_subcommand_from get' -l no-recursive -d 'Prevent
3636
complete -c ghq -n '__fish_seen_subcommand_from get' -s b -l branch -d 'Specify branch name. This flag implies --single-branch on Git'
3737
complete -c ghq -n '__fish_seen_subcommand_from get' -s P -l parallel -d 'Import parallelly'
3838
complete -c ghq -n '__fish_seen_subcommand_from get' -l bare -d 'Do a bare clone'
39+
function __complete_get_partial
40+
printf '%s\t%s\n' 'blobless' 'Do a blobless clone'
41+
printf '%s\t%s\n' 'treeless' 'Do a treeless clone'
42+
end
43+
complete -c ghq -n '__fish_seen_subcommand_from get' -l partial -d 'Do a partial clone' -xa '(__complete_get_partial)'
3944

4045
complete -c ghq -n '__fish_seen_subcommand_from list' -s e -l exact -d 'Perform an exact match'
4146
complete -c ghq -n '__fish_seen_subcommand_from list' -l vcs -d 'Specify vcs backend for matching'

misc/zsh/_ghq

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ function _ghq () {
2727
'--bare[Do a bare clone]' \
2828
'(-b --branch)'{-b,--branch}'[Specify branch name]' \
2929
'(-P --parallel)'{-P,--parallel}'[Import parallelly]' \
30+
'--partial[Do a partial clone]: :(blobless treeless)' \
3031
'(-)*:: :->null_state' \
3132
&& ret=0
3233
;;

vcs.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ type vcsGetOption struct {
4444
url *url.URL
4545
dir string
4646
recursive, shallow, silent, bare bool
47-
branch string
47+
branch, partial string
4848
}
4949

5050
// GitBackend is the VCSBackend of git
@@ -70,6 +70,11 @@ var GitBackend = &VCSBackend{
7070
if vg.bare {
7171
args = append(args, "--bare")
7272
}
73+
if vg.partial == "blobless" {
74+
args = append(args, "--filter=blob:none")
75+
} else if vg.partial == "treeless" {
76+
args = append(args, "--filter=tree:0")
77+
}
7378
args = append(args, vg.url.String(), vg.dir)
7479

7580
return run(vg.silent)("git", args...)

0 commit comments

Comments
 (0)