Skip to content

Commit e97767e

Browse files
committed
migrate some commands
1 parent 6c18987 commit e97767e

17 files changed

+871
-21
lines changed

blob.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
// Copyright 2015 The Gogs Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"bytes"
9+
"io"
10+
)
11+
12+
type Blob struct {
13+
repo *Repository
14+
*TreeEntry
15+
}
16+
17+
func (b *Blob) Data() (io.Reader, error) {
18+
stdout, err := NewCommand("show", b.ID.String()).RunInDirBytes(b.repo.Path)
19+
if err != nil {
20+
return nil, err
21+
}
22+
return bytes.NewBuffer(stdout), nil
23+
}

command.go

Lines changed: 56 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"fmt"
1010
"os/exec"
1111
"strings"
12+
"time"
1213
)
1314

1415
// Command represents a command with its subcommands or arguments.
@@ -38,39 +39,79 @@ func (c *Command) AddArguments(args ...string) *Command {
3839
return c
3940
}
4041

41-
func (c *Command) run(dir string) (string, error) {
42+
const DEFAULT_TIMEOUT = 60 * time.Second
43+
44+
// RunInDirTimeout executes the command in given directory with given timeout,
45+
// and returns stdout in []byte and error (combined with stderr).
46+
func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, error) {
47+
if timeout == -1 {
48+
timeout = DEFAULT_TIMEOUT
49+
}
50+
4251
stdout := new(bytes.Buffer)
4352
stderr := new(bytes.Buffer)
4453

54+
if len(dir) == 0 {
55+
log(c.String())
56+
} else {
57+
log("%s: %v", dir, c)
58+
}
59+
4560
cmd := exec.Command(c.name, c.args...)
4661
cmd.Dir = dir
4762
cmd.Stdout = stdout
4863
cmd.Stderr = stderr
64+
if err := cmd.Start(); err != nil {
65+
return nil, concatenateError(err, stderr.String())
66+
}
4967

50-
if len(dir) == 0 {
51-
log(c.String())
52-
} else {
53-
log("%s: %v", dir, c)
68+
done := make(chan error)
69+
go func() {
70+
done <- cmd.Wait()
71+
}()
72+
73+
var err error
74+
select {
75+
case <-time.After(timeout):
76+
if cmd.Process != nil && cmd.ProcessState != nil && !cmd.ProcessState.Exited() {
77+
if err := cmd.Process.Kill(); err != nil {
78+
return nil, fmt.Errorf("fail to kill process: %v", err)
79+
}
80+
}
81+
82+
<-done
83+
return nil, ErrExecTimeout{timeout}
84+
case err = <-done:
5485
}
5586

56-
if err := cmd.Run(); err != nil {
57-
return stdout.String(), concatenateError(err, stderr.String())
87+
if err != nil {
88+
return nil, concatenateError(err, stderr.String())
5889
}
90+
5991
if stdout.Len() > 0 {
6092
log("stdout:\n%s", stdout)
6193
}
62-
63-
return stdout.String(), nil
94+
return stdout.Bytes(), nil
6495
}
6596

66-
// Run executes the command in defualt working directory
67-
// and returns stdout and error (combined with stderr).
68-
func (c *Command) Run() (string, error) {
69-
return c.run("")
97+
// RunInDir executes the command in given directory
98+
// and returns stdout in []byte and error (combined with stderr).
99+
func (c *Command) RunInDirBytes(dir string) ([]byte, error) {
100+
return c.RunInDirTimeout(-1, dir)
70101
}
71102

72103
// RunInDir executes the command in given directory
73-
// and returns stdout and error (combined with stderr).
104+
// and returns stdout in string and error (combined with stderr).
74105
func (c *Command) RunInDir(dir string) (string, error) {
75-
return c.run(dir)
106+
stdout, err := c.RunInDirTimeout(-1, dir)
107+
if err != nil {
108+
return "", err
109+
}
110+
return string(stdout), nil
111+
}
112+
113+
// Run executes the command in defualt working directory
114+
// and returns stdout in string and error (combined with stderr).
115+
func (c *Command) Run() (string, error) {
116+
return c.RunInDir("")
76117
}

commit.go

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
// Copyright 2015 The Gogs Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"fmt"
9+
"strconv"
10+
"strings"
11+
12+
"github.com/mcuadros/go-version"
13+
)
14+
15+
// Commit represents a git commit.
16+
type Commit struct {
17+
Tree
18+
ID sha1 // The ID of this commit object
19+
Author *Signature
20+
Committer *Signature
21+
CommitMessage string
22+
23+
parents []sha1 // SHA1 strings
24+
// submodules map[string]*SubModule
25+
}
26+
27+
// AddAllChanges marks local changes to be ready for commit.
28+
func AddChanges(repoPath string, all bool, files ...string) error {
29+
cmd := NewCommand("add")
30+
if all {
31+
cmd.AddArguments("--all")
32+
}
33+
_, err := cmd.AddArguments(files...).RunInDir(repoPath)
34+
return err
35+
}
36+
37+
// CommitChanges commits local changes with given message and author.
38+
func CommitChanges(repoPath, message string, author *Signature) error {
39+
cmd := NewCommand("commit", "-m", message)
40+
if author != nil {
41+
cmd.AddArguments(fmt.Sprintf("--author='%s <%s>'", author.Name, author.Email))
42+
}
43+
_, err := cmd.RunInDir(repoPath)
44+
45+
// No stderr but exit status 1 means nothing to commit.
46+
if err != nil && err.Error() == "exit status 1" {
47+
return nil
48+
}
49+
return err
50+
}
51+
52+
// CommitsCount returns number of total commits of until given revision.
53+
func CommitsCount(repoPath, revision string) (int64, error) {
54+
if version.Compare(gitVersion, "1.8.0", "<") {
55+
stdout, err := NewCommand("log", "--pretty=format:''", revision).RunInDir(repoPath)
56+
if err != nil {
57+
return 0, err
58+
}
59+
return int64(len(strings.Split(stdout, "\n"))), nil
60+
}
61+
62+
stdout, err := NewCommand("rev-list", "--count", revision).RunInDir(repoPath)
63+
if err != nil {
64+
return 0, err
65+
}
66+
return strconv.ParseInt(strings.TrimSpace(stdout), 10, 64)
67+
}

error.go

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,32 @@ package git
66

77
import (
88
"fmt"
9+
"time"
910
)
1011

11-
func concatenateError(err error, stderr string) error {
12-
if len(stderr) == 0 {
13-
return err
14-
}
15-
return fmt.Errorf("%v - %s", err, stderr)
12+
type ErrExecTimeout struct {
13+
Duration time.Duration
14+
}
15+
16+
func IsErrExecTimeout(err error) bool {
17+
_, ok := err.(ErrExecTimeout)
18+
return ok
19+
}
20+
21+
func (err ErrExecTimeout) Error() string {
22+
return fmt.Sprintf("execution is timeout [duration: %v]", err.Duration)
23+
}
24+
25+
type ErrNotExist struct {
26+
ID string
27+
RelPath string
28+
}
29+
30+
func IsErrNotExist(err error) bool {
31+
_, ok := err.(ErrNotExist)
32+
return ok
33+
}
34+
35+
func (err ErrNotExist) Error() string {
36+
return fmt.Sprintf("object does not exist [id: %s, rel_path: %s]", err.ID, err.RelPath)
1637
}

git.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,3 +49,7 @@ func Version() (string, error) {
4949
gitVersion = fields[2]
5050
return gitVersion, nil
5151
}
52+
53+
func init() {
54+
Version()
55+
}

repo.go

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,43 @@
55
package git
66

77
import (
8+
"bytes"
9+
"container/list"
10+
"errors"
811
"os"
12+
"path"
13+
"path/filepath"
914
)
1015

11-
// InitRepository initializes a new Git repository in choice of bare or not.
16+
// Repository represents a Git repository.
17+
type Repository struct {
18+
Path string
19+
20+
commitCache map[sha1]*Commit
21+
}
22+
23+
const _PRETTY_LOG_FORMAT = `--pretty=format:%H`
24+
25+
func (repo *Repository) parsePrettyFormatLogToList(logs []byte) (*list.List, error) {
26+
l := list.New()
27+
if len(logs) == 0 {
28+
return l, nil
29+
}
30+
31+
parts := bytes.Split(logs, []byte{'\n'})
32+
33+
for _, commitId := range parts {
34+
commit, err := repo.GetCommit(string(commitId))
35+
if err != nil {
36+
return nil, err
37+
}
38+
l.PushBack(commit)
39+
}
40+
41+
return l, nil
42+
}
43+
44+
// InitRepository initializes a new Git repository.
1245
func InitRepository(repoPath string, bare bool) error {
1346
os.MkdirAll(repoPath, os.ModePerm)
1447

@@ -19,3 +52,50 @@ func InitRepository(repoPath string, bare bool) error {
1952
_, err := cmd.RunInDir(repoPath)
2053
return err
2154
}
55+
56+
// OpenRepository opens the repository at the given path.
57+
func OpenRepository(repoPath string) (*Repository, error) {
58+
repoPath, err := filepath.Abs(repoPath)
59+
if err != nil {
60+
return nil, err
61+
} else if !isDir(repoPath) {
62+
return nil, errors.New("no such file or directory")
63+
}
64+
65+
return &Repository{Path: repoPath}, nil
66+
}
67+
68+
// Clone clones original repository to target path.
69+
func Clone(from, to string) error {
70+
toDir := path.Dir(to)
71+
os.MkdirAll(toDir, os.ModePerm)
72+
73+
_, err := NewCommand("clone", from, to).Run()
74+
return err
75+
}
76+
77+
// Pull pulls changes from remotes.
78+
func Pull(repoPath string, all bool) error {
79+
cmd := NewCommand("pull")
80+
if all {
81+
cmd.AddArguments("--all")
82+
}
83+
_, err := cmd.RunInDir(repoPath)
84+
return err
85+
}
86+
87+
// Push pushs local commits to given remote branch.
88+
func Push(repoPath, remote, branch string) error {
89+
_, err := NewCommand("push", remote, branch).RunInDir(repoPath)
90+
return err
91+
}
92+
93+
// Reset resets HEAD to given revision or head of branch.
94+
func Reset(repoPath string, hard bool, revision string) error {
95+
cmd := NewCommand("reset")
96+
if hard {
97+
cmd.AddArguments("--hard")
98+
}
99+
_, err := cmd.AddArguments(revision).RunInDir(repoPath)
100+
return err
101+
}

repo_branch.go

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
// Copyright 2015 The Gogs Authors. All rights reserved.
2+
// Use of this source code is governed by a MIT-style
3+
// license that can be found in the LICENSE file.
4+
5+
package git
6+
7+
import (
8+
"fmt"
9+
"strings"
10+
)
11+
12+
const BRANCH_PREFIX = "refs/heads/"
13+
14+
// Branch represents a Git branch.
15+
type Branch struct {
16+
Name string
17+
Path string
18+
}
19+
20+
// GetHEADBranch returns corresponding branch of HEAD.
21+
func (repo *Repository) GetHEADBranch() (*Branch, error) {
22+
stdout, err := NewCommand("symbolic-ref", "HEAD").RunInDir(repo.Path)
23+
if err != nil {
24+
return nil, err
25+
}
26+
stdout = strings.TrimSpace(stdout)
27+
28+
if !strings.HasPrefix(stdout, BRANCH_PREFIX) {
29+
return nil, fmt.Errorf("invalid HEAD branch: %v", stdout)
30+
}
31+
32+
return &Branch{
33+
Name: stdout[len(BRANCH_PREFIX):],
34+
Path: stdout,
35+
}, nil
36+
}
37+
38+
// IsBranchExist returns true if given branch exists in repository.
39+
func IsBranchExist(repoPath, branch string) bool {
40+
_, err := NewCommand("show-ref", "--verify", BRANCH_PREFIX+branch).RunInDir(repoPath)
41+
return err == nil
42+
}

0 commit comments

Comments
 (0)