Skip to content

Commit 5eba315

Browse files
authored
Support to clone from a mirror and fork it (#43)
1 parent 2268167 commit 5eba315

10 files changed

+202
-8
lines changed

Makefile

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,8 @@ goreleaser:
1616

1717
copy: build
1818
sudo cp bin/cgit /usr/local/bin/cgit
19+
20+
# Install golang-lint via https://golangci-lint.run/usage/install/#local-installation
21+
# or via 'hd install golangci-lint'
22+
lint:
23+
golangci-lint run ./...

cmd/clone.go

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package cmd
2+
3+
import (
4+
"github.com/AlecAivazis/survey/v2"
5+
"github.com/linuxsuren/cgit/pkg"
6+
"github.com/spf13/cobra"
7+
"github.com/spf13/viper"
8+
"os/exec"
9+
"path"
10+
"strings"
11+
)
12+
13+
type cloneOption struct {
14+
ws bool
15+
}
16+
17+
// NewCloneCommand returns the clone command
18+
func NewCloneCommand() (cmd *cobra.Command) {
19+
opt := &cloneOption{}
20+
21+
cmd = &cobra.Command{
22+
Use: "clone",
23+
Short: "A smart way to clone repositories from GitHub",
24+
RunE: opt.runE,
25+
}
26+
27+
flags := cmd.Flags()
28+
flags.BoolVarP(&opt.ws, "ws", "", false, "Clone the code into ~/ws/github/org/repo if it is true")
29+
return
30+
}
31+
32+
func (o *cloneOption) runE(_ *cobra.Command, args []string) (err error) {
33+
output := func(arg string) string {
34+
if orgAndRepo := strings.Split(arg, "/"); len(orgAndRepo) == 2 {
35+
return path.Join(viper.GetString("ws"), arg)
36+
}
37+
return ""
38+
}
39+
if !o.ws {
40+
output = nil
41+
}
42+
args = pkg.ParseShortCode(args, output)
43+
44+
var targetDir string
45+
gitAddress := args[0]
46+
if len(args) >= 2 {
47+
targetDir = args[1]
48+
}
49+
50+
var gitBinary string
51+
if gitBinary, err = exec.LookPath("git"); err == nil {
52+
gitArgs := []string{"clone"}
53+
gitArgs = append(gitArgs, args...)
54+
pkg.UseMirror(gitArgs)
55+
if err = pkg.ExecCommandInDir(gitBinary, "", gitArgs...); err == nil {
56+
err = pkg.ExecCommandInDir(gitBinary, targetDir, "remote", "set-url", "origin", gitAddress)
57+
}
58+
}
59+
if err != nil {
60+
return
61+
}
62+
63+
var ghBinary string
64+
if ghBinary, err = exec.LookPath("gh"); err == nil {
65+
prompt := &survey.Confirm{
66+
Message: "do you want to fork it?",
67+
}
68+
var ok bool
69+
if err = survey.AskOne(prompt, &ok); err == nil && ok {
70+
err = pkg.ExecCommandInDir(ghBinary, targetDir, "repo", "fork", "--remote")
71+
}
72+
}
73+
return
74+
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ module github.com/linuxsuren/cgit
33
go 1.16
44

55
require (
6+
github.com/AlecAivazis/survey/v2 v2.3.2
67
github.com/linuxsuren/cobra-extension v0.0.12
78
github.com/linuxsuren/go-cli-alias v0.0.9
89
github.com/linuxsuren/http-downloader v0.0.52

main.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,8 @@ func main() {
4545

4646
ctx := context.TODO()
4747
command.AddCommand(ext.NewCompletionCmd(command),
48-
cmd.NewMirrorCmd(ctx))
48+
cmd.NewMirrorCmd(ctx),
49+
cmd.NewCloneCommand())
4950

5051
// do the dep checking
5152
if err := installDepTools(); err != nil {
@@ -129,7 +130,6 @@ func init() {
129130
}
130131
}
131132
loadDefaults()
132-
return
133133
}
134134

135135
func loadDefaults() {

mirror_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import (
88
func TestMirror(t *testing.T) {
99
abc := []string{"a", "b", "github.com"}
1010
useMirror(abc)
11-
assert.Equal(t, abc[2], "github.com.cnpmjs.org")
11+
assert.Equal(t, abc[2], "github.com")
1212

1313
abc = []string{"clone", "https://github.com/cli/cli"}
1414
useMirror(abc)

pkg/exec.go

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,13 +65,9 @@ func ExecCommandInDir(name, dir string, args ...string) (err error) {
6565
return
6666
}
6767

68-
func execCommand(name string, arg ...string) (err error) {
69-
return ExecCommandInDir(name, "", arg...)
70-
}
71-
7268
func copyAndCapture(w io.Writer, r io.Reader) ([]byte, error) {
7369
var out []byte
74-
buf := make([]byte, 1024, 1024)
70+
buf := make([]byte, 1024)
7571
for {
7672
n, err := r.Read(buf[:])
7773
if n > 0 {

pkg/mirror.go

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
package pkg
2+
3+
import "strings"
4+
5+
// UseMirror modify the args with a GitHub mirror
6+
func UseMirror(args []string) {
7+
// only for git clone
8+
if len(args) < 2 || args[0] != "clone" {
9+
return
10+
}
11+
for i, arg := range args {
12+
if strings.Contains(arg, "github.com") && !strings.Contains(arg, "github.com.cnpmjs.org") {
13+
args[i] = strings.ReplaceAll(arg, "github.com", "github.com.cnpmjs.org")
14+
break
15+
}
16+
}
17+
}

pkg/mirror_test.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package pkg
2+
3+
import (
4+
"github.com/magiconair/properties/assert"
5+
"testing"
6+
)
7+
8+
func TestUseMirror(t *testing.T) {
9+
type args struct {
10+
args []string
11+
}
12+
tests := []struct {
13+
name string
14+
args args
15+
verify func(*testing.T, []string)
16+
}{{
17+
name: "normal case",
18+
args: args{
19+
[]string{"clone", "https://github.com/a/b"},
20+
},
21+
verify: func(t *testing.T, args []string) {
22+
assert.Equal(t, args[0], "clone")
23+
assert.Equal(t, args[1], "https://github.com.cnpmjs.org/a/b")
24+
},
25+
}}
26+
for _, tt := range tests {
27+
t.Run(tt.name, func(t *testing.T) {
28+
UseMirror(tt.args.args)
29+
tt.verify(t, tt.args.args)
30+
})
31+
}
32+
}

pkg/shortcode.go

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package pkg
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
)
7+
8+
// ParseShortCode parses a short org/repo to a GitHub URL
9+
func ParseShortCode(args []string, output func(string) string) []string {
10+
if len(args) <= 0 {
11+
return args
12+
}
13+
if output == nil {
14+
output = defaultOutput
15+
}
16+
17+
result := []string{fmt.Sprintf("https://github.com/%s", args[0])}
18+
if len(args) > 1 {
19+
result = append(result, args[1:]...)
20+
} else {
21+
if target := output(args[0]); target != "" {
22+
result = append(result, target)
23+
}
24+
}
25+
return result
26+
}
27+
28+
func defaultOutput(arg string) string {
29+
if orgAndRepo := strings.Split(arg, "/"); len(orgAndRepo) == 2 {
30+
return orgAndRepo[1]
31+
}
32+
return ""
33+
}

pkg/shortcode_test.go

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
package pkg
2+
3+
import (
4+
"reflect"
5+
"testing"
6+
)
7+
8+
func TestParseShortCode(t *testing.T) {
9+
type args struct {
10+
args []string
11+
}
12+
tests := []struct {
13+
name string
14+
args args
15+
want []string
16+
}{{
17+
name: "org/repo format",
18+
args: args{[]string{"org/repo"}},
19+
want: []string{"https://github.com/org/repo", "repo"},
20+
}, {
21+
name: "'org/repo output' format",
22+
args: args{[]string{"org/repo", "dir"}},
23+
want: []string{"https://github.com/org/repo", "dir"},
24+
}, {
25+
name: "unexpected format",
26+
args: args{[]string{}},
27+
want: []string{},
28+
}}
29+
for _, tt := range tests {
30+
t.Run(tt.name, func(t *testing.T) {
31+
if got := ParseShortCode(tt.args.args, nil); !reflect.DeepEqual(got, tt.want) {
32+
t.Errorf("ParseShortCode() = %v, want %v", got, tt.want)
33+
}
34+
})
35+
}
36+
}

0 commit comments

Comments
 (0)