Skip to content

Commit ebd9fb2

Browse files
committed
speed up for getting a lot commits
1 parent 3835dd7 commit ebd9fb2

File tree

8 files changed

+155
-42
lines changed

8 files changed

+155
-42
lines changed

command.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (c *Command) RunInDirTimeout(timeout time.Duration, dir string) ([]byte, er
9595
}
9696

9797
if stdout.Len() > 0 {
98-
log("stdout:\n%s", stdout)
98+
log("stdout:\n%s", stdout.Bytes()[:1024])
9999
}
100100
return stdout.Bytes(), nil
101101
}

commit.go

Lines changed: 20 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ type Commit struct {
2323
Committer *Signature
2424
CommitMessage string
2525

26-
parents []sha1 // SHA1 strings
27-
submodules map[string]*SubModule
26+
parents []sha1 // SHA1 strings
27+
submoduleCache *objectCache
2828
}
2929

3030
// Message returns the commit message. Same as retrieving CommitMessage directly.
@@ -180,17 +180,9 @@ func (c *Commit) SearchCommits(keyword string) (*list.List, error) {
180180
return c.repo.searchCommits(c.ID, keyword)
181181
}
182182

183-
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
184-
modules, err := c.GetSubModules()
185-
if err != nil {
186-
return nil, err
187-
}
188-
return modules[entryname], nil
189-
}
190-
191-
func (c *Commit) GetSubModules() (map[string]*SubModule, error) {
192-
if c.submodules != nil {
193-
return c.submodules, nil
183+
func (c *Commit) GetSubModules() (*objectCache, error) {
184+
if c.submoduleCache != nil {
185+
return c.submoduleCache, nil
194186
}
195187

196188
entry, err := c.GetTreeEntryByPath(".gitmodules")
@@ -203,7 +195,6 @@ func (c *Commit) GetSubModules() (map[string]*SubModule, error) {
203195
}
204196

205197
scanner := bufio.NewScanner(rd)
206-
c.submodules = make(map[string]*SubModule)
207198
var ismodule bool
208199
var path string
209200
for scanner.Scan() {
@@ -217,11 +208,24 @@ func (c *Commit) GetSubModules() (map[string]*SubModule, error) {
217208
if k == "path" {
218209
path = strings.TrimSpace(fields[1])
219210
} else if k == "url" {
220-
c.submodules[path] = &SubModule{path, strings.TrimSpace(fields[1])}
211+
c.submoduleCache.Set(path, &SubModule{path, strings.TrimSpace(fields[1])})
221212
ismodule = false
222213
}
223214
}
224215
}
225216

226-
return c.submodules, nil
217+
return c.submoduleCache, nil
218+
}
219+
220+
func (c *Commit) GetSubModule(entryname string) (*SubModule, error) {
221+
modules, err := c.GetSubModules()
222+
if err != nil {
223+
return nil, err
224+
}
225+
226+
module, has := modules.Get(entryname)
227+
if has {
228+
return module.(*SubModule), nil
229+
}
230+
return nil, nil
227231
}

git.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import (
1010
"time"
1111
)
1212

13-
const _VERSION = "0.1.1"
13+
const _VERSION = "0.2.0"
1414

1515
func Version() string {
1616
return _VERSION

repo.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import (
1818
type Repository struct {
1919
Path string
2020

21-
commitCache map[sha1]*Commit
22-
tagCache map[sha1]*Tag
21+
commitCache *objectCache
22+
tagCache *objectCache
2323
}
2424

2525
const _PRETTY_LOG_FORMAT = `--pretty=format:%H`
@@ -64,7 +64,11 @@ func OpenRepository(repoPath string) (*Repository, error) {
6464
return nil, errors.New("no such file or directory")
6565
}
6666

67-
return &Repository{Path: repoPath}, nil
67+
return &Repository{
68+
Path: repoPath,
69+
commitCache: newObjectCache(),
70+
tagCache: newObjectCache(),
71+
}, nil
6872
}
6973

7074
type CloneRepoOptions struct {

repo_commit.go

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@ func (repo *Repository) GetTagCommitID(name string) (string, error) {
3636
// \n\n separate headers from message
3737
func parseCommitData(data []byte) (*Commit, error) {
3838
commit := new(Commit)
39+
commit.submoduleCache = newObjectCache()
3940
commit.parents = make([]sha1, 0, 1)
4041
// we now have the contents of the commit object. Let's investigate...
4142
nextline := 0
@@ -86,13 +87,10 @@ l:
8687
}
8788

8889
func (repo *Repository) getCommit(id sha1) (*Commit, error) {
89-
if repo.commitCache != nil {
90+
c, ok := repo.commitCache.Get(id.String())
91+
if ok {
9092
log("Hit cache: %s", id)
91-
if c, ok := repo.commitCache[id]; ok {
92-
return c, nil
93-
}
94-
} else {
95-
repo.commitCache = make(map[sha1]*Commit, 10)
93+
return c.(*Commit), nil
9694
}
9795

9896
data, err := NewCommand("cat-file", "-p", id.String()).RunInDirBytes(repo.Path)
@@ -107,7 +105,7 @@ func (repo *Repository) getCommit(id sha1) (*Commit, error) {
107105
commit.repo = repo
108106
commit.ID = id
109107

110-
repo.commitCache[id] = commit
108+
repo.commitCache.Set(id.String(), commit)
111109
return commit, nil
112110
}
113111

repo_tag.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -27,12 +27,10 @@ func (repo *Repository) CreateTag(name, revision string) error {
2727
}
2828

2929
func (repo *Repository) getTag(id sha1) (*Tag, error) {
30-
if repo.tagCache != nil {
31-
if t, ok := repo.tagCache[id]; ok {
32-
return t, nil
33-
}
34-
} else {
35-
repo.tagCache = make(map[sha1]*Tag, 10)
30+
t, ok := repo.tagCache.Get(id.String())
31+
if ok {
32+
log("Hit cache: %s", id)
33+
return t.(*Tag), nil
3634
}
3735

3836
// Get tag type
@@ -50,7 +48,8 @@ func (repo *Repository) getTag(id sha1) (*Tag, error) {
5048
Type: string(OBJECT_COMMIT),
5149
repo: repo,
5250
}
53-
repo.tagCache[id] = tag
51+
52+
repo.tagCache.Set(id.String(), tag)
5453
return tag, nil
5554
}
5655

@@ -68,7 +67,7 @@ func (repo *Repository) getTag(id sha1) (*Tag, error) {
6867
tag.ID = id
6968
tag.repo = repo
7069

71-
repo.tagCache[id] = tag
70+
repo.tagCache.Set(id.String(), tag)
7271
return tag, nil
7372
}
7473

tree_entry.go

Lines changed: 86 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,9 @@
55
package git
66

77
import (
8+
"fmt"
9+
"path"
10+
"path/filepath"
811
"sort"
912
"strconv"
1013
"strings"
@@ -84,10 +87,10 @@ var sorter = []func(t1, t2 *TreeEntry) bool{
8487
},
8588
}
8689

87-
func (bs Entries) Len() int { return len(bs) }
88-
func (bs Entries) Swap(i, j int) { bs[i], bs[j] = bs[j], bs[i] }
89-
func (bs Entries) Less(i, j int) bool {
90-
t1, t2 := bs[i], bs[j]
90+
func (tes Entries) Len() int { return len(tes) }
91+
func (tes Entries) Swap(i, j int) { tes[i], tes[j] = tes[j], tes[i] }
92+
func (tes Entries) Less(i, j int) bool {
93+
t1, t2 := tes[i], tes[j]
9194
var k int
9295
for k = 0; k < len(sorter)-1; k++ {
9396
sort := sorter[k]
@@ -101,6 +104,83 @@ func (bs Entries) Less(i, j int) bool {
101104
return sorter[k](t1, t2)
102105
}
103106

104-
func (bs Entries) Sort() {
105-
sort.Sort(bs)
107+
func (tes Entries) Sort() {
108+
sort.Sort(tes)
109+
}
110+
111+
type commitInfo struct {
112+
id string
113+
infos []interface{}
114+
err error
115+
}
116+
117+
// GetCommitsInfo takes advantages of concurrey to speed up getting information
118+
// of all commits that are corresponding to these entries.
119+
// TODO: limit max goroutines at same time
120+
func (tes Entries) GetCommitsInfo(commit *Commit, treePath string) ([][]interface{}, error) {
121+
if len(tes) == 0 {
122+
return nil, nil
123+
}
124+
125+
revChan := make(chan commitInfo, 10)
126+
127+
infoMap := make(map[string][]interface{}, len(tes))
128+
for i := range tes {
129+
if tes[i].Type != OBJECT_COMMIT {
130+
go func(i int) {
131+
cinfo := commitInfo{id: tes[i].ID.String()}
132+
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
133+
if err != nil {
134+
cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
135+
} else {
136+
cinfo.infos = []interface{}{tes[i], c}
137+
}
138+
revChan <- cinfo
139+
}(i)
140+
continue
141+
}
142+
143+
// Handle submodule
144+
go func(i int) {
145+
cinfo := commitInfo{id: tes[i].ID.String()}
146+
sm, err := commit.GetSubModule(path.Join(treePath, tes[i].Name()))
147+
if err != nil {
148+
cinfo.err = fmt.Errorf("GetSubModule (%s/%s): %v", treePath, tes[i].Name(), err)
149+
revChan <- cinfo
150+
return
151+
}
152+
153+
smUrl := ""
154+
if sm != nil {
155+
smUrl = sm.Url
156+
}
157+
158+
c, err := commit.GetCommitByPath(filepath.Join(treePath, tes[i].Name()))
159+
if err != nil {
160+
cinfo.err = fmt.Errorf("GetCommitByPath (%s/%s): %v", treePath, tes[i].Name(), err)
161+
} else {
162+
cinfo.infos = []interface{}{tes[i], NewSubModuleFile(c, smUrl, tes[i].ID.String())}
163+
}
164+
revChan <- cinfo
165+
}(i)
166+
}
167+
168+
i := 0
169+
for info := range revChan {
170+
if info.err != nil {
171+
return nil, info.err
172+
}
173+
174+
infoMap[info.id] = info.infos
175+
i++
176+
if i == len(tes) {
177+
break
178+
}
179+
}
180+
181+
commitsInfo := make([][]interface{}, len(tes))
182+
for i := 0; i < len(tes); i++ {
183+
commitsInfo[i] = infoMap[tes[i].ID.String()]
184+
}
185+
return commitsInfo, nil
106186
}

utlis.go

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,36 @@ import (
99
"os"
1010
"path/filepath"
1111
"strings"
12+
"sync"
1213
)
1314

15+
// objectCache provides thread-safe cache opeations.
16+
type objectCache struct {
17+
lock sync.RWMutex
18+
cache map[string]interface{}
19+
}
20+
21+
func newObjectCache() *objectCache {
22+
return &objectCache{
23+
cache: make(map[string]interface{}, 10),
24+
}
25+
}
26+
27+
func (oc *objectCache) Set(id string, obj interface{}) {
28+
oc.lock.Lock()
29+
defer oc.lock.Unlock()
30+
31+
oc.cache[id] = obj
32+
}
33+
34+
func (oc *objectCache) Get(id string) (interface{}, bool) {
35+
oc.lock.RLock()
36+
defer oc.lock.RUnlock()
37+
38+
obj, has := oc.cache[id]
39+
return obj, has
40+
}
41+
1442
// isDir returns true if given path is a directory,
1543
// or returns false when it's a file or does not exist.
1644
func isDir(dir string) bool {

0 commit comments

Comments
 (0)