+30
-37
blame/blame.go
blame.go
+30
-37
blame/blame.go
blame.go
···
6
6
//
7
7
// This package also provides a pretty print function to output the
8
8
// results of a blame in a similar format to the git-blame command.
9
-
package blame
9
+
package git
10
10
11
11
import (
12
12
"bytes"
···
16
16
"strings"
17
17
"unicode/utf8"
18
18
19
-
"gopkg.in/src-d/go-git.v2"
20
19
"gopkg.in/src-d/go-git.v2/core"
21
20
"gopkg.in/src-d/go-git.v2/diff"
22
-
"gopkg.in/src-d/go-git.v2/revlist"
23
21
)
24
22
25
-
// Blame returns the last commit that modified each line of a file in
26
-
// a repository.
23
+
type Blame struct {
24
+
Path string
25
+
Rev core.Hash
26
+
Lines []*line
27
+
}
28
+
29
+
// Blame returns the last commit that modified each line of a file in a
30
+
// repository.
27
31
//
28
32
// The file to blame is identified by the input arguments: repo, commit and path.
29
33
// The output is a slice of commits, one for each line in the file.
···
66
70
// 1. Add memoization between revlist and assign.
67
71
//
68
72
// 2. It is using much more memory than needed, see the TODOs below.
69
-
70
-
type Blame struct {
71
-
Repo string
72
-
Path string
73
-
Rev string
74
-
Lines []*line
75
-
}
76
-
77
-
func New(repo *git.Repository, path string, commit *git.Commit) (*Blame, error) {
78
-
// init the internal blame struct
73
+
func (c *Commit) Blame(path string) (*Blame, error) {
79
74
b := new(blame)
80
-
b.repo = repo
81
-
b.fRev = commit
75
+
b.fRev = c
82
76
b.path = path
83
77
84
78
// get all the file revisions
···
104
98
}
105
99
106
100
return &Blame{
107
-
Repo: repo.URL,
108
101
Path: path,
109
-
Rev: commit.Hash.String(),
102
+
Rev: c.Hash,
110
103
Lines: lines,
111
104
}, nil
112
105
}
···
123
116
}
124
117
}
125
118
126
-
func newLines(contents []string, commits []*git.Commit) ([]*line, error) {
119
+
func newLines(contents []string, commits []*Commit) ([]*line, error) {
127
120
if len(contents) != len(commits) {
128
121
return nil, errors.New("contents and commits have different length")
129
122
}
···
138
131
// this struct is internally used by the blame function to hold its
139
132
// inputs, outputs and state.
140
133
type blame struct {
141
-
repo *git.Repository // the repo holding the history of the file to blame
142
-
path string // the path of the file to blame
143
-
fRev *git.Commit // the commit of the final revision of the file to blame
144
-
revs revlist.Revs // the chain of revisions affecting the the file to blame
145
-
data []string // the contents of the file across all its revisions
146
-
graph [][]*git.Commit // the graph of the lines in the file across all the revisions TODO: not all commits are needed, only the current rev and the prev
134
+
path string // the path of the file to blame
135
+
fRev *Commit // the commit of the final revision of the file to blame
136
+
revs []*Commit // the chain of revisions affecting the the file to blame
137
+
data []string // the contents of the file across all its revisions
138
+
graph [][]*Commit // the graph of the lines in the file across all the revisions TODO: not all commits are needed, only the current rev and the prev
147
139
}
148
140
149
141
// calculte the history of a file "path", starting from commit "from", sorted by commit date.
150
142
func (b *blame) fillRevs() error {
151
143
var err error
152
-
b.revs, err = revlist.NewRevs(b.repo, b.fRev, b.path)
144
+
145
+
b.revs, err = b.fRev.References(b.path)
153
146
if err != nil {
154
147
return err
155
148
}
···
158
151
159
152
// build graph of a file from its revision history
160
153
func (b *blame) fillGraphAndData() error {
161
-
b.graph = make([][]*git.Commit, len(b.revs))
154
+
b.graph = make([][]*Commit, len(b.revs))
162
155
b.data = make([]string, len(b.revs)) // file contents in all the revisions
163
156
// for every revision of the file, starting with the first
164
157
// one...
···
169
162
return nil
170
163
}
171
164
b.data[i] = file.Contents()
172
-
nLines := git.CountLines(b.data[i])
165
+
nLines := countLines(b.data[i])
173
166
// create a node for each line
174
-
b.graph[i] = make([]*git.Commit, nLines)
167
+
b.graph[i] = make([]*Commit, nLines)
175
168
// assign a commit to each node
176
169
// if this is the first revision, then the node is assigned to
177
170
// this first commit.
178
171
if i == 0 {
179
172
for j := 0; j < nLines; j++ {
180
-
b.graph[i][j] = (*git.Commit)(b.revs[i])
173
+
b.graph[i][j] = (*Commit)(b.revs[i])
181
174
}
182
175
} else {
183
176
// if this is not the first commit, then assign to the old
···
191
184
192
185
// sliceGraph returns a slice of commits (one per line) for a particular
193
186
// revision of a file (0=first revision).
194
-
func (b *blame) sliceGraph(i int) []*git.Commit {
187
+
func (b *blame) sliceGraph(i int) []*Commit {
195
188
fVs := b.graph[i]
196
-
result := make([]*git.Commit, 0, len(fVs))
189
+
result := make([]*Commit, 0, len(fVs))
197
190
for _, v := range fVs {
198
-
c := git.Commit(*v)
191
+
c := Commit(*v)
199
192
result = append(result, &c)
200
193
}
201
194
return result
···
209
202
sl := -1 // source line
210
203
dl := -1 // destination line
211
204
for h := range hunks {
212
-
hLines := git.CountLines(hunks[h].Text)
205
+
hLines := countLines(hunks[h].Text)
213
206
for hl := 0; hl < hLines; hl++ {
214
207
switch {
215
208
case hunks[h].Type == 0:
···
218
211
b.graph[c][dl] = b.graph[p][sl]
219
212
case hunks[h].Type == 1:
220
213
dl++
221
-
b.graph[c][dl] = (*git.Commit)(b.revs[c])
214
+
b.graph[c][dl] = (*Commit)(b.revs[c])
222
215
case hunks[h].Type == -1:
223
216
sl++
224
217
default:
···
255
248
}
256
249
257
250
// utility function to pretty print the author.
258
-
func prettyPrintAuthor(c *git.Commit) string {
251
+
func prettyPrintAuthor(c *Commit) string {
259
252
return fmt.Sprintf("%s %s", c.Author.Name, c.Author.When.Format("2006-01-02"))
260
253
}
261
254
blame/blame2humantest.bash
utils/blame2humantest.bash
blame/blame2humantest.bash
utils/blame2humantest.bash
+14
-38
blame/blame_test.go
blame_test.go
+14
-38
blame/blame_test.go
blame_test.go
···
1
-
package blame
1
+
package git
2
2
3
3
import (
4
-
"bytes"
5
4
"os"
6
-
"testing"
7
5
8
-
"gopkg.in/src-d/go-git.v2"
9
6
"gopkg.in/src-d/go-git.v2/core"
10
7
"gopkg.in/src-d/go-git.v2/formats/packfile"
11
8
12
9
. "gopkg.in/check.v1"
13
10
)
14
11
15
-
func Test(t *testing.T) { TestingT(t) }
16
-
17
-
type SuiteCommon struct {
18
-
repos map[string]*git.Repository
12
+
type BlameCommon struct {
13
+
repos map[string]*Repository
19
14
}
20
15
21
-
var _ = Suite(&SuiteCommon{})
22
-
23
-
var fixtureRepos = [...]struct {
24
-
url string
25
-
packfile string
26
-
}{
27
-
{"https://github.com/tyba/git-fixture.git", "../formats/packfile/fixtures/git-fixture.ofs-delta"},
28
-
{"https://github.com/spinnaker/spinnaker.git", "../formats/packfile/fixtures/spinnaker-spinnaker.pack"},
29
-
}
16
+
var _ = Suite(&BlameCommon{})
30
17
31
18
// create the repositories of the fixtures
32
-
func (s *SuiteCommon) SetUpSuite(c *C) {
33
-
s.repos = make(map[string]*git.Repository, 0)
19
+
func (s *BlameCommon) SetUpSuite(c *C) {
20
+
s.repos = make(map[string]*Repository, 0)
34
21
for _, fixRepo := range fixtureRepos {
35
-
repo := git.NewPlainRepository()
22
+
repo := NewPlainRepository()
36
23
repo.URL = fixRepo.url
37
24
38
25
d, err := os.Open(fixRepo.packfile)
···
60
47
blames []string // the commits blamed for each line
61
48
}
62
49
63
-
func (s *SuiteCommon) mockBlame(t blameTest, c *C) (blame *Blame) {
50
+
func (s *BlameCommon) mockBlame(t blameTest, c *C) (blame *Blame) {
64
51
repo, ok := s.repos[t.repo]
65
52
c.Assert(ok, Equals, true)
66
53
···
85
72
}
86
73
87
74
return &Blame{
88
-
Repo: t.repo,
89
75
Path: t.path,
90
-
Rev: t.rev,
76
+
Rev: core.NewHash(t.rev),
91
77
Lines: blamedLines,
92
78
}
93
79
}
94
80
95
81
// run a blame on all the suite's tests
96
-
func (s *SuiteCommon) TestBlame(c *C) {
97
-
for i, t := range blameTests {
82
+
func (s *BlameCommon) TestBlame(c *C) {
83
+
for _, t := range blameTests {
98
84
expected := s.mockBlame(t, c)
99
85
100
86
repo, ok := s.repos[t.repo]
···
103
89
commit, err := repo.Commit(core.NewHash(t.rev))
104
90
c.Assert(err, IsNil)
105
91
106
-
obtained, err := New(repo, t.path, commit)
107
-
c.Assert(err, IsNil, Commentf("subtest %d", i))
108
-
109
-
c.Assert(obtained, DeepEquals, expected, Commentf("subtest %d: %s",
110
-
i, sideBySide(obtained, expected)))
92
+
obtained, err := commit.Blame(t.path)
93
+
c.Assert(err, IsNil)
94
+
c.Assert(obtained, DeepEquals, expected)
111
95
}
112
-
}
113
-
114
-
func sideBySide(output, expected *Blame) string {
115
-
var buf bytes.Buffer
116
-
buf.WriteString(output.Repo)
117
-
buf.WriteString(" ")
118
-
buf.WriteString(expected.Repo)
119
-
return buf.String()
120
96
}
121
97
122
98
// utility function to avoid writing so many repeated commits
-16
clients/ssh/git_upload_pack_test.go
-16
clients/ssh/git_upload_pack_test.go
···
25
25
)
26
26
27
27
func (s *SuiteRemote) TestConnect(c *C) {
28
-
fmt.Println("TestConnect")
29
28
r := NewGitUploadPackService()
30
29
c.Assert(r.Connect(fixRepo), Equals, ErrAuthRequired)
31
30
}
···
59
58
}
60
59
61
60
func (s *SuiteRemote) TestConnectWithPublicKeysCallback(c *C) {
62
-
fmt.Println("TestConnectWithPublicKeysCallback")
63
61
agent, err := newSSHAgentConn()
64
62
c.Assert(err, IsNil)
65
63
defer func() { c.Assert(agent.close(), IsNil) }()
···
72
70
}
73
71
74
72
func (s *SuiteRemote) TestConnectBadVcs(c *C) {
75
-
fmt.Println("TestConnectBadVcs")
76
73
r := NewGitUploadPackService()
77
74
c.Assert(r.ConnectWithAuth(fixRepoBadVcs, nil), ErrorMatches, fmt.Sprintf(".*%s.*", fixRepoBadVcs))
78
75
}
79
76
80
77
func (s *SuiteRemote) TestConnectNonGit(c *C) {
81
-
fmt.Println("TestConnectNonGit")
82
78
r := NewGitUploadPackService()
83
79
c.Assert(r.ConnectWithAuth(fixRepoNonGit, nil), Equals, ErrUnsupportedVCS)
84
80
}
85
81
86
82
func (s *SuiteRemote) TestConnectNonGithub(c *C) {
87
-
fmt.Println("TestConnectNonGit")
88
83
r := NewGitUploadPackService()
89
84
c.Assert(r.ConnectWithAuth(fixGitRepoNonGithub, nil), Equals, ErrUnsupportedRepo)
90
85
}
···
97
92
func (*mockAuth) String() string { return "" }
98
93
99
94
func (s *SuiteRemote) TestConnectWithAuthWrongType(c *C) {
100
-
fmt.Println("TestConnectWithAuthWrongType")
101
95
r := NewGitUploadPackService()
102
96
c.Assert(r.ConnectWithAuth(fixRepo, &mockAuth{}), Equals, ErrInvalidAuthMethod)
103
97
c.Assert(r.connected, Equals, false)
104
98
}
105
99
106
100
func (s *SuiteRemote) TestAlreadyConnected(c *C) {
107
-
fmt.Println("TestAlreadyConnected")
108
101
agent, err := newSSHAgentConn()
109
102
c.Assert(err, IsNil)
110
103
defer func() { c.Assert(agent.close(), IsNil) }()
···
117
110
}
118
111
119
112
func (s *SuiteRemote) TestDisconnect(c *C) {
120
-
fmt.Println("TestDisconnect")
121
113
agent, err := newSSHAgentConn()
122
114
c.Assert(err, IsNil)
123
115
defer func() { c.Assert(agent.close(), IsNil) }()
···
129
121
}
130
122
131
123
func (s *SuiteRemote) TestDisconnectedWhenNonConnected(c *C) {
132
-
fmt.Println("TestDisconnectedWhenNonConnected")
133
124
r := NewGitUploadPackService()
134
125
c.Assert(r.Disconnect(), Equals, ErrNotConnected)
135
126
}
136
127
137
128
func (s *SuiteRemote) TestAlreadyDisconnected(c *C) {
138
-
fmt.Println("TestAlreadyDisconnected")
139
129
agent, err := newSSHAgentConn()
140
130
c.Assert(err, IsNil)
141
131
defer func() { c.Assert(agent.close(), IsNil) }()
···
148
138
}
149
139
150
140
func (s *SuiteRemote) TestServeralConnections(c *C) {
151
-
fmt.Println("TestServeralConnections")
152
141
agent, err := newSSHAgentConn()
153
142
c.Assert(err, IsNil)
154
143
defer func() { c.Assert(agent.close(), IsNil) }()
···
169
158
}
170
159
171
160
func (s *SuiteRemote) TestInfoNotConnected(c *C) {
172
-
fmt.Println("TestInfoNotConnected")
173
161
r := NewGitUploadPackService()
174
162
_, err := r.Info()
175
163
c.Assert(err, Equals, ErrNotConnected)
176
164
}
177
165
178
166
func (s *SuiteRemote) TestDefaultBranch(c *C) {
179
-
fmt.Println("TestDefaultBranch")
180
167
agent, err := newSSHAgentConn()
181
168
c.Assert(err, IsNil)
182
169
defer func() { c.Assert(agent.close(), IsNil) }()
···
191
178
}
192
179
193
180
func (s *SuiteRemote) TestCapabilities(c *C) {
194
-
fmt.Println("TestCapabilities")
195
181
agent, err := newSSHAgentConn()
196
182
c.Assert(err, IsNil)
197
183
defer func() { c.Assert(agent.close(), IsNil) }()
···
206
192
}
207
193
208
194
func (s *SuiteRemote) TestFetchNotConnected(c *C) {
209
-
fmt.Println("TestFetchNotConnected")
210
195
r := NewGitUploadPackService()
211
196
pr := &common.GitUploadPackRequest{}
212
197
pr.Want(core.NewHash("6ecf0ef2c2dffb796033e5a02219af86ec6584e5"))
···
215
200
}
216
201
217
202
func (s *SuiteRemote) TestFetch(c *C) {
218
-
fmt.Println("TestFetch")
219
203
agent, err := newSSHAgentConn()
220
204
c.Assert(err, IsNil)
221
205
defer func() { c.Assert(agent.close(), IsNil) }()
+23
commit.go
+23
commit.go
···
6
6
"errors"
7
7
"fmt"
8
8
"io"
9
+
"sort"
9
10
10
11
"gopkg.in/src-d/go-git.v2/core"
11
12
)
···
159
160
defer func() { i.IsClosed = true }()
160
161
close(i.ch)
161
162
}
163
+
164
+
type commitSorterer struct {
165
+
l []*Commit
166
+
}
167
+
168
+
func (s commitSorterer) Len() int {
169
+
return len(s.l)
170
+
}
171
+
172
+
func (s commitSorterer) Less(i, j int) bool {
173
+
return s.l[i].Committer.When.Before(s.l[j].Committer.When)
174
+
}
175
+
176
+
func (s commitSorterer) Swap(i, j int) {
177
+
s.l[i], s.l[j] = s.l[j], s.l[i]
178
+
}
179
+
180
+
// SortCommits sort a commit list by commit date, from older to newer.
181
+
func SortCommits(l []*Commit) {
182
+
s := &commitSorterer{l}
183
+
sort.Sort(s)
184
+
}
+7
-5
common.go
+7
-5
common.go
···
2
2
3
3
import "strings"
4
4
5
-
// CountLines returns the number of lines in a string à la git, this is
5
+
// countLines returns the number of lines in a string à la git, this is
6
6
// The newline character is assumed to be '\n'. The empty string
7
7
// contains 0 lines. If the last line of the string doesn't end with a
8
8
// newline, it will still be considered a line.
9
-
func CountLines(s string) int {
9
+
func countLines(s string) int {
10
10
if s == "" {
11
11
return 0
12
12
}
13
-
nEol := strings.Count(s, "\n")
13
+
14
+
nEOL := strings.Count(s, "\n")
14
15
if strings.HasSuffix(s, "\n") {
15
-
return nEol
16
+
return nEOL
16
17
}
17
-
return nEol + 1
18
+
19
+
return nEOL + 1
18
20
}
+10
-1
common_test.go
+10
-1
common_test.go
···
44
44
return r, nil
45
45
}
46
46
47
+
var fixtureRepos = [...]struct {
48
+
url string
49
+
packfile string
50
+
}{
51
+
{"https://github.com/tyba/git-fixture.git", "formats/packfile/fixtures/git-fixture.ofs-delta"},
52
+
{"https://github.com/jamesob/desk.git", "formats/packfile/fixtures/jamesob-desk.pack"},
53
+
{"https://github.com/spinnaker/spinnaker.git", "formats/packfile/fixtures/spinnaker-spinnaker.pack"},
54
+
}
55
+
47
56
type SuiteCommon struct{}
48
57
49
58
var _ = Suite(&SuiteCommon{})
···
65
74
66
75
func (s *SuiteCommon) TestCountLines(c *C) {
67
76
for i, t := range countLinesTests {
68
-
o := CountLines(t.i)
77
+
o := countLines(t.i)
69
78
c.Assert(o, Equals, t.e, Commentf("subtest %d, input=%q", i, t.i))
70
79
}
71
80
}
-2
objects_test.go
-2
objects_test.go
+31
-63
revlist/revlist.go
references.go
+31
-63
revlist/revlist.go
references.go
···
15
15
//
16
16
// Another way to get the revision history for a file is:
17
17
// git log --follow -p -- file
18
-
package revlist
18
+
package git
19
19
20
20
import (
21
-
"bytes"
22
21
"io"
23
-
"sort"
24
22
25
-
"gopkg.in/src-d/go-git.v2"
26
23
"gopkg.in/src-d/go-git.v2/core"
27
24
"gopkg.in/src-d/go-git.v2/diff"
28
25
29
26
"github.com/sergi/go-diff/diffmatchpatch"
30
27
)
31
28
32
-
// A Revs is a list of revisions for a file (basically a list of commits).
33
-
// It implements sort.Interface using the commit time.
34
-
type Revs []*git.Commit
35
-
36
-
func (l Revs) Len() int {
37
-
return len(l)
38
-
}
39
-
40
-
// sorts from older to newer commit.
41
-
func (l Revs) Less(i, j int) bool {
42
-
return l[i].Committer.When.Before(l[j].Committer.When)
43
-
}
44
-
45
-
func (l Revs) Swap(i, j int) {
46
-
l[i], l[j] = l[j], l[i]
47
-
}
48
-
49
-
// for debugging
50
-
func (l Revs) GoString() string {
51
-
var buf bytes.Buffer
52
-
for _, c := range l {
53
-
buf.WriteString(c.Hash.String()[:8])
54
-
buf.WriteString("\n")
55
-
}
56
-
return buf.String()
57
-
}
58
-
59
-
// NewRevs returns a Revs pointer for the
60
-
// file at "path", from commit "commit".
61
-
// The commits are sorted in commit order.
62
-
// It stops searching a branch for a file upon reaching the commit
63
-
// were the file was created.
64
-
// Moves and copies are not currently supported.
65
-
// Cherry-picks are not detected unless there are no commits between
66
-
// them and therefore can appear repeated in the list.
67
-
// (see git path-id for hints on how to fix this).
68
-
func NewRevs(repo *git.Repository, commit *git.Commit, path string) (Revs, error) {
69
-
result := make(Revs, 0)
29
+
// References returns a References for the file at "path", the commits are
30
+
// sorted in commit order. It stops searching a branch for a file upon reaching
31
+
// the commit were the file was created.
32
+
//
33
+
// Caveats:
34
+
// - Moves and copies are not currently supported.
35
+
// - Cherry-picks are not detected unless there are no commits between them and
36
+
// therefore can appear repeated in the list.
37
+
// (see git path-id for hints on how to fix this).
38
+
func (c *Commit) References(path string) ([]*Commit, error) {
39
+
result := make([]*Commit, 0)
70
40
seen := make(map[core.Hash]struct{}, 0)
71
-
err := walkGraph(&result, &seen, repo, commit, path)
72
-
if err != nil {
41
+
if err := walkGraph(&result, &seen, c.r, c, path); err != nil {
73
42
return nil, err
74
43
}
75
-
sort.Sort(result)
76
-
result, err = removeComp(path, result, equivalent) // for merges of identical cherry-picks
77
-
if err != nil {
78
-
return nil, err
79
-
}
80
-
return result, nil
44
+
45
+
SortCommits(result)
46
+
47
+
// for merges of identical cherry-picks
48
+
return removeComp(path, result, equivalent)
81
49
}
82
50
83
51
// Recursive traversal of the commit graph, generating a linear history
84
52
// of the path.
85
-
func walkGraph(result *Revs, seen *map[core.Hash]struct{}, repo *git.Repository, current *git.Commit, path string) error {
53
+
func walkGraph(result *[]*Commit, seen *map[core.Hash]struct{}, repo *Repository, current *Commit, path string) error {
86
54
// check and update seen
87
55
if _, ok := (*seen)[current.Hash]; ok {
88
56
return nil
···
132
100
133
101
// TODO: benchmark this making git.Commit.parent public instead of using
134
102
// an iterator
135
-
func parentsContainingPath(path string, c *git.Commit) []*git.Commit {
136
-
var result []*git.Commit
103
+
func parentsContainingPath(path string, c *Commit) []*Commit {
104
+
var result []*Commit
137
105
iter := c.Parents()
138
106
for {
139
107
parent, err := iter.Next()
···
151
119
152
120
// Returns an slice of the commits in "cs" that has the file "path", but with different
153
121
// contents than what can be found in "c".
154
-
func differentContents(path string, c *git.Commit, cs []*git.Commit) ([]*git.Commit, error) {
155
-
result := make([]*git.Commit, 0, len(cs))
122
+
func differentContents(path string, c *Commit, cs []*Commit) ([]*Commit, error) {
123
+
result := make([]*Commit, 0, len(cs))
156
124
h, found := blobHash(path, c)
157
125
if !found {
158
-
return nil, git.ErrFileNotFound
126
+
return nil, ErrFileNotFound
159
127
}
160
128
for _, cx := range cs {
161
129
if hx, found := blobHash(path, cx); found && h != hx {
···
166
134
}
167
135
168
136
// blobHash returns the hash of a path in a commit
169
-
func blobHash(path string, commit *git.Commit) (hash core.Hash, found bool) {
137
+
func blobHash(path string, commit *Commit) (hash core.Hash, found bool) {
170
138
file, err := commit.File(path)
171
139
if err != nil {
172
140
var empty core.Hash
···
175
143
return file.Hash, true
176
144
}
177
145
178
-
type contentsComparatorFn func(path string, a, b *git.Commit) (bool, error)
146
+
type contentsComparatorFn func(path string, a, b *Commit) (bool, error)
179
147
180
148
// Returns a new slice of commits, with duplicates removed. Expects a
181
149
// sorted commit list. Duplication is defined according to "comp". It
182
150
// will always keep the first commit of a series of duplicated commits.
183
-
func removeComp(path string, cs []*git.Commit, comp contentsComparatorFn) ([]*git.Commit, error) {
184
-
result := make([]*git.Commit, 0, len(cs))
151
+
func removeComp(path string, cs []*Commit, comp contentsComparatorFn) ([]*Commit, error) {
152
+
result := make([]*Commit, 0, len(cs))
185
153
if len(cs) == 0 {
186
154
return result, nil
187
155
}
···
199
167
}
200
168
201
169
// Equivalent commits are commits whose patch is the same.
202
-
func equivalent(path string, a, b *git.Commit) (bool, error) {
170
+
func equivalent(path string, a, b *Commit) (bool, error) {
203
171
numParentsA := a.NumParents()
204
172
numParentsB := b.NumParents()
205
173
···
221
189
return sameDiffs(diffsA, diffsB), nil
222
190
}
223
191
224
-
func patch(c *git.Commit, path string) ([]diffmatchpatch.Diff, error) {
192
+
func patch(c *Commit, path string) ([]diffmatchpatch.Diff, error) {
225
193
// get contents of the file in the commit
226
194
file, err := c.File(path)
227
195
if err != nil {
···
265
233
}
266
234
switch a.Type {
267
235
case 0:
268
-
return git.CountLines(a.Text) == git.CountLines(b.Text)
236
+
return countLines(a.Text) == countLines(b.Text)
269
237
case 1, -1:
270
238
return a.Text == b.Text
271
239
default:
revlist/revlist2humantest.bash
utils/revlist2humantest.bash
revlist/revlist2humantest.bash
utils/revlist2humantest.bash
+17
-34
revlist/revlist_test.go
references_test.go
+17
-34
revlist/revlist_test.go
references_test.go
···
1
-
package revlist
1
+
package git
2
2
3
3
import (
4
4
"bytes"
5
5
"fmt"
6
6
"os"
7
-
"testing"
8
7
9
-
"gopkg.in/src-d/go-git.v2"
10
8
"gopkg.in/src-d/go-git.v2/core"
11
9
"gopkg.in/src-d/go-git.v2/formats/packfile"
12
10
13
11
. "gopkg.in/check.v1"
14
12
)
15
13
16
-
func Test(t *testing.T) { TestingT(t) }
17
-
18
-
type SuiteCommon struct {
19
-
repos map[string]*git.Repository
14
+
type ReferencesSuite struct {
15
+
repos map[string]*Repository
20
16
}
21
17
22
-
var _ = Suite(&SuiteCommon{})
23
-
24
-
var fixtureRepos = [...]struct {
25
-
url string
26
-
packfile string
27
-
}{
28
-
{"https://github.com/tyba/git-fixture.git", "../formats/packfile/fixtures/git-fixture.ofs-delta"},
29
-
{"https://github.com/jamesob/desk.git", "../formats/packfile/fixtures/jamesob-desk.pack"},
30
-
{"https://github.com/spinnaker/spinnaker.git", "../formats/packfile/fixtures/spinnaker-spinnaker.pack"},
31
-
}
18
+
var _ = Suite(&ReferencesSuite{})
32
19
33
20
// create the repositories of the fixtures
34
-
func (s *SuiteCommon) SetUpSuite(c *C) {
35
-
s.repos = make(map[string]*git.Repository, 0)
21
+
func (s *ReferencesSuite) SetUpSuite(c *C) {
22
+
s.repos = make(map[string]*Repository, 0)
36
23
for _, fixRepo := range fixtureRepos {
37
-
s.repos[fixRepo.url] = git.NewPlainRepository()
24
+
s.repos[fixRepo.url] = NewPlainRepository()
38
25
39
26
d, err := os.Open(fixRepo.packfile)
40
27
defer d.Close()
···
48
35
}
49
36
}
50
37
51
-
var revListTests = [...]struct {
38
+
var referencesTests = [...]struct {
52
39
// input data to revlist
53
40
repo string
54
41
commit string
···
318
305
*/
319
306
}
320
307
321
-
func (s *SuiteCommon) TestRevList(c *C) {
322
-
for _, t := range revListTests {
308
+
func (s *ReferencesSuite) TestRevList(c *C) {
309
+
for _, t := range referencesTests {
323
310
repo, ok := s.repos[t.repo]
324
311
c.Assert(ok, Equals, true)
325
312
326
313
commit, err := repo.Commit(core.NewHash(t.commit))
327
314
c.Assert(err, IsNil)
328
315
329
-
revs, err := NewRevs(repo, commit, t.path)
330
-
c.Assert(err, IsNil, Commentf("\nrepo=%s, commit=%s, path=%s\n",
331
-
t.repo, t.commit, t.path))
316
+
revs, err := commit.References(t.path)
317
+
c.Assert(err, IsNil)
318
+
c.Assert(len(revs), Equals, len(t.revs))
332
319
333
-
c.Assert(len(revs), Equals, len(t.revs), Commentf("\nrepo=%s, commit=%s, path=%s\n EXPECTED (len %d)\n%s\n OBTAINED (len %d)\n%s\n",
334
-
t.repo, t.commit, t.path, len(t.revs), t.revs, len(revs), revs.GoString()))
335
320
for i := range revs {
336
321
if revs[i].Hash.String() != t.revs[i] {
337
322
commit, err := repo.Commit(core.NewHash(t.revs[i]))
···
346
331
}
347
332
}
348
333
}
349
-
fmt.Printf("OK repo=%s, commit=%s, path=%s\n",
350
-
t.repo, t.commit, t.path)
351
334
}
352
335
}
353
336
354
337
// same length is assumed
355
-
func compareSideBySide(a []string, b []*git.Commit) string {
338
+
func compareSideBySide(a []string, b []*Commit) string {
356
339
var buf bytes.Buffer
357
340
buf.WriteString("\t EXPECTED OBTAINED ")
358
341
var sep string
···
379
362
}
380
363
381
364
// should detect cherry picks
382
-
func (s *SuiteCommon) TestEquivalent(c *C) {
365
+
func (s *ReferencesSuite) TestEquivalent(c *C) {
383
366
for _, t := range cherryPicks {
384
367
cs := s.commits(c, t[0], t[2], t[3])
385
368
equiv, err := equivalent(t[1], cs[0], cs[1])
···
389
372
}
390
373
391
374
// returns the commits from a slice of hashes
392
-
func (s *SuiteCommon) commits(cc *C, repo string, hs ...string) []*git.Commit {
375
+
func (s *ReferencesSuite) commits(cc *C, repo string, hs ...string) []*Commit {
393
376
r, ok := s.repos[repo]
394
377
cc.Assert(ok, Equals, true)
395
-
result := make([]*git.Commit, 0, len(hs))
378
+
result := make([]*Commit, 0, len(hs))
396
379
for _, h := range hs {
397
380
c, err := r.Commit(core.NewHash(h))
398
381
cc.Assert(err, IsNil)