Skip to content

Commit 96dff62

Browse files
authored
v1: overhaul (#42)
* git: add unit tests * ci: fix install step * blob: update tests * command: improve docstring and method name * akk: revert wrong changes * command: add unit tests * Delete deprecated * all: rename sha1 to SHA1 * commit: improve methods name * ci: require Go 1.9 * misc: move things around * blob: rename methods * misc: improve docstring * repo: improve methods * Add helper functions * repo_commit: overhaul * sha1: fix copylocks * diff: overhaul * repo: overhaul * misc: move things around * repo_tag: overhaul * repo_tree: overhaul * signature: overhaul * submodule: overhaul * Finishing up! Ready to be alpha
1 parent ab71f42 commit 96dff62

39 files changed

+2590
-2094
lines changed

.travis.yml

+2-6
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,12 @@
11
sudo: false
22
language: go
33
go:
4-
- 1.7.x
5-
- 1.8.x
64
- 1.9.x
75
- 1.10.x
86
- 1.11.x
97
- 1.12.x
108
- 1.13.x
119

12-
install: go get -v ./...
13-
script:
14-
- go get golang.org/x/tools/cmd/cover
15-
- go get github.com/smartystreets/goconvey
10+
install: go get -v -t ./...
11+
script:
1612
- go test -v -cover -race

Makefile

+1-1
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ test:
77
go test -v -cover -race
88

99
bench:
10-
go test -v -cover -race -test.bench=. -test.benchmem
10+
go test -v -cover -test.bench=. -test.benchmem
1111

1212
coverage:
1313
go test -coverprofile=c.out && go tool cover -html=c.out && rm c.out

README.md

+5-3
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,13 @@
44

55
Package git-module is a Go module for Git access through shell commands.
66

7-
## Limitations
7+
⚠️ v1 is currently untable, please use [the latest tagged version 0.8.3](https://github.com/gogs/git-module/releases/tag/v0.8.3) ⚠️
88

9-
- Go version must be at least **1.7**.
9+
## Requirements
10+
11+
- Go version must be at least **1.9**.
1012
- Git version must be no less than **1.8.3**.
11-
- For Windows users, try use as new a version as possible.
13+
- For Windows users, try to use the latest version of both.
1214

1315
## License
1416

blob.go

+8-7
Original file line numberDiff line numberDiff line change
@@ -9,27 +9,28 @@ import (
99
"io"
1010
)
1111

12-
// Blob represents a Git object.
12+
// Blob is a blob object.
1313
type Blob struct {
1414
repo *Repository
1515
*TreeEntry
1616
}
1717

18-
// Data gets content of blob all at once and wrap it as io.Reader.
18+
// Bytes reads and returns the content of the blob all at once in bytes.
1919
// This can be very slow and memory consuming for huge content.
20-
func (b *Blob) Data() (io.Reader, error) {
20+
func (b *Blob) Bytes() ([]byte, error) {
2121
stdout := new(bytes.Buffer)
2222
stderr := new(bytes.Buffer)
2323

2424
// Preallocate memory to save ~50% memory usage on big files.
2525
stdout.Grow(int(b.Size() + 2048))
2626

27-
if err := b.DataPipeline(stdout, stderr); err != nil {
27+
if err := b.Pipeline(stdout, stderr); err != nil {
2828
return nil, concatenateError(err, stderr.String())
2929
}
30-
return stdout, nil
30+
return stdout.Bytes(), nil
3131
}
3232

33-
func (b *Blob) DataPipeline(stdout, stderr io.Writer) error {
34-
return NewCommand("show", b.ID.String()).RunInDirPipeline(b.repo.Path, stdout, stderr)
33+
// Pipeline reads the content of the blob and pipes stdout and stderr to supplied io.Writer.
34+
func (b *Blob) Pipeline(stdout, stderr io.Writer) error {
35+
return NewCommand("show", b.id.String()).RunInDirPipeline(stdout, stderr, b.repo.path)
3536
}

blob_test.go

+22-44
Original file line numberDiff line numberDiff line change
@@ -6,25 +6,13 @@ package git
66

77
import (
88
"bytes"
9-
"io/ioutil"
109
"testing"
1110

12-
. "github.com/smartystreets/goconvey/convey"
11+
"github.com/stretchr/testify/assert"
1312
)
1413

15-
var testBlob = &Blob{
16-
repo: &Repository{},
17-
TreeEntry: &TreeEntry{
18-
ID: MustIDFromString("176d8dfe018c850d01851b05fb8a430096247353"),
19-
ptree: &Tree{
20-
repo: &Repository{},
21-
},
22-
},
23-
}
24-
25-
func Test_Blob_Data(t *testing.T) {
26-
Convey("Get blob data", t, func() {
27-
_output := `Copyright (c) 2015 All Gogs Contributors
14+
func TestBlob(t *testing.T) {
15+
expOutput := `Copyright (c) 2015 All Gogs Contributors
2816
2917
Permission is hereby granted, free of charge, to any person obtaining a copy
3018
of this software and associated documentation files (the "Software"), to deal
@@ -44,36 +32,26 @@ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
4432
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
4533
THE SOFTWARE.`
4634

47-
Convey("Get data all at once", func() {
48-
r, err := testBlob.Data()
49-
So(err, ShouldBeNil)
50-
So(r, ShouldNotBeNil)
51-
52-
data, err := ioutil.ReadAll(r)
53-
So(err, ShouldBeNil)
54-
So(string(data), ShouldEqual, _output)
55-
})
35+
blob := &Blob{
36+
repo: &Repository{},
37+
TreeEntry: &TreeEntry{
38+
id: MustIDFromString("176d8dfe018c850d01851b05fb8a430096247353"), // Blob ID of "LICENSE" file
39+
parent: &Tree{
40+
repo: &Repository{},
41+
},
42+
},
43+
}
5644

57-
Convey("Get blob data with pipeline", func() {
58-
stdout := new(bytes.Buffer)
59-
err := testBlob.DataPipeline(stdout, nil)
60-
So(err, ShouldBeNil)
61-
So(stdout.String(), ShouldEqual, _output)
62-
})
45+
t.Run("get data all at once", func(t *testing.T) {
46+
p, err := blob.Bytes()
47+
assert.Nil(t, err)
48+
assert.Equal(t, expOutput, string(p))
6349
})
64-
}
65-
66-
func Benchmark_Blob_Data(b *testing.B) {
67-
for i := 0; i < b.N; i++ {
68-
r, _ := testBlob.Data()
69-
ioutil.ReadAll(r)
70-
}
71-
}
7250

73-
func Benchmark_Blob_DataPipeline(b *testing.B) {
74-
stdout := new(bytes.Buffer)
75-
for i := 0; i < b.N; i++ {
76-
stdout.Reset()
77-
testBlob.DataPipeline(stdout, nil)
78-
}
51+
t.Run("get data with pipeline", func(t *testing.T) {
52+
stdout := new(bytes.Buffer)
53+
err := blob.Pipeline(stdout, nil)
54+
assert.Nil(t, err)
55+
assert.Equal(t, expOutput, stdout.String())
56+
})
7957
}

command.go

+51-56
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ package git
66

77
import (
88
"bytes"
9+
"context"
910
"fmt"
1011
"io"
1112
"os"
@@ -14,56 +15,62 @@ import (
1415
"time"
1516
)
1617

17-
// Command represents a command with its subcommands or arguments.
18+
// Command contains the name, arguments and environment variables of a command.
1819
type Command struct {
1920
name string
2021
args []string
2122
envs []string
2223
}
2324

25+
// String returns the string representation of the command.
2426
func (c *Command) String() string {
2527
if len(c.args) == 0 {
2628
return c.name
2729
}
2830
return fmt.Sprintf("%s %s", c.name, strings.Join(c.args, " "))
2931
}
3032

31-
// NewCommand creates and returns a new Git Command based on given command and arguments.
33+
// NewCommand creates and returns a new Command with given arguments for "git".
3234
func NewCommand(args ...string) *Command {
3335
return &Command{
3436
name: "git",
3537
args: args,
3638
}
3739
}
3840

39-
// AddArguments adds new argument(s) to the command.
40-
func (c *Command) AddArguments(args ...string) *Command {
41+
// AddArgs appends given arguments to the command.
42+
func (c *Command) AddArgs(args ...string) *Command {
4143
c.args = append(c.args, args...)
4244
return c
4345
}
4446

45-
// AddEnvs adds new environment variables to the command.
47+
// AddEnvs appends given environment variables to the command.
4648
func (c *Command) AddEnvs(envs ...string) *Command {
4749
c.envs = append(c.envs, envs...)
4850
return c
4951
}
5052

51-
const DEFAULT_TIMEOUT = 60 * time.Second
53+
// DefaultTimeout is the default timeout duration for all commands.
54+
const DefaultTimeout = time.Minute
5255

53-
// RunInDirTimeoutPipeline executes the command in given directory with given timeout,
54-
// it pipes stdout and stderr to given io.Writer.
55-
func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, stdout, stderr io.Writer) error {
56-
if timeout == -1 {
57-
timeout = DEFAULT_TIMEOUT
56+
// RunInDirPipelineWithTimeout executes the command in given directory and timeout duration.
57+
// It pipes stdout and stderr to supplied io.Writer. DefaultTimeout will be used if the timeout
58+
// duration is less than time.Nanosecond (i.e. less than or equal to 0).
59+
func (c *Command) RunInDirPipelineWithTimeout(timeout time.Duration, stdout, stderr io.Writer, dir string) error {
60+
if timeout < time.Nanosecond {
61+
timeout = DefaultTimeout
5862
}
5963

6064
if len(dir) == 0 {
61-
log(c.String())
65+
log("[timeout: %v] %s", timeout, c)
6266
} else {
63-
log("%s: %v", dir, c)
67+
log("[timeout: %v] %s: %s", timeout, dir, c)
6468
}
6569

66-
cmd := exec.Command(c.name, c.args...)
70+
ctx, cancel := context.WithTimeout(context.Background(), timeout)
71+
defer cancel()
72+
73+
cmd := exec.CommandContext(ctx, c.name, c.args...)
6774
if c.envs != nil {
6875
cmd.Env = append(os.Environ(), c.envs...)
6976
}
@@ -74,34 +81,38 @@ func (c *Command) RunInDirTimeoutPipeline(timeout time.Duration, dir string, std
7481
return err
7582
}
7683

77-
done := make(chan error)
84+
result := make(chan error)
7885
go func() {
79-
done <- cmd.Wait()
86+
result <- cmd.Wait()
8087
}()
8188

82-
var err error
8389
select {
84-
case <-time.After(timeout):
90+
case <-ctx.Done():
8591
if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() {
8692
if err := cmd.Process.Kill(); err != nil {
87-
return fmt.Errorf("fail to kill process: %v", err)
93+
return fmt.Errorf("kill process: %v", err)
8894
}
8995
}
9096

91-
<-done
97+
<-result
9298
return ErrExecTimeout{timeout}
93-
case err = <-done:
99+
case err := <-result:
100+
return err
94101
}
102+
}
95103

96-
return err
104+
// RunInDirPipeline executes the command in given directory and default timeout duration.
105+
// It pipes stdout and stderr to supplied io.Writer.
106+
func (c *Command) RunInDirPipeline(stdout, stderr io.Writer, dir string) error {
107+
return c.RunInDirPipelineWithTimeout(DefaultTimeout, stdout, stderr, dir)
97108
}
98109

99-
// RunInDirTimeout executes the command in given directory with given timeout,
100-
// and returns stdout in []byte and error (combined with stderr).
101-
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
110+
// RunInDirWithTimeout executes the command in given directory and timeout duration.
111+
// It returns stdout in []byte and error (combined with stderr).
112+
func (c *Command) RunInDirWithTimeout(timeout time.Duration, dir string) ([]byte, error) {
102113
stdout := new(bytes.Buffer)
103114
stderr := new(bytes.Buffer)
104-
if err := c.RunInDirTimeoutPipeline(timeout, dir, stdout, stderr); err != nil {
115+
if err := c.RunInDirPipelineWithTimeout(timeout, stdout, stderr, dir); err != nil {
105116
return nil, concatenateError(err, stderr.String())
106117
}
107118

@@ -111,40 +122,24 @@ func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, er
111122
return stdout.Bytes(), nil
112123
}
113124

114-
// RunInDirPipeline executes the command in given directory,
115-
// it pipes stdout and stderr to given io.Writer.
116-
func (c *Command) RunInDirPipeline(dir string, stdout, stderr io.Writer) error {
117-
return c.RunInDirTimeoutPipeline(-1, dir, stdout, stderr)
118-
}
119-
120-
// RunInDirBytes executes the command in given directory
121-
// and returns stdout in []byte and error (combined with stderr).
122-
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
123-
return c.RunInDirTimeout(-1, dir)
124-
}
125-
126-
// RunInDir executes the command in given directory
127-
// and returns stdout in string and error (combined with stderr).
128-
func (c *Command) RunInDir(dir string) (string, error) {
129-
stdout, err := c.RunInDirTimeout(-1, dir)
130-
if err != nil {
131-
return "", err
132-
}
133-
return string(stdout), nil
125+
// RunInDir executes the command in given directory and default timeout duration.
126+
// It returns stdout and error (combined with stderr).
127+
func (c *Command) RunInDir(dir string) ([]byte, error) {
128+
return c.RunInDirWithTimeout(DefaultTimeout, dir)
134129
}
135130

136-
// RunTimeout executes the command in defualt working directory with given timeout,
137-
// and returns stdout in string and error (combined with stderr).
138-
func (c *Command) RunTimeout(timeout time.Duration) (string, error) {
139-
stdout, err := c.RunInDirTimeout(timeout, "")
131+
// RunWithTimeout executes the command in working directory and given timeout duration.
132+
// It returns stdout in string and error (combined with stderr).
133+
func (c *Command) RunWithTimeout(timeout time.Duration) ([]byte, error) {
134+
stdout, err := c.RunInDirWithTimeout(timeout, "")
140135
if err != nil {
141-
return "", err
136+
return nil, err
142137
}
143-
return string(stdout), nil
138+
return stdout, nil
144139
}
145140

146-
// Run executes the command in defualt working directory
147-
// and returns stdout in string and error (combined with stderr).
148-
func (c *Command) Run() (string, error) {
149-
return c.RunTimeout(-1)
141+
// Run executes the command in working directory and default timeout duration.
142+
// It returns stdout in string and error (combined with stderr).
143+
func (c *Command) Run() ([]byte, error) {
144+
return c.RunWithTimeout(DefaultTimeout)
150145
}

0 commit comments

Comments
 (0)