Skip to content
This repository was archived by the owner on Sep 11, 2020. It is now read-only.

Commit c9609eb

Browse files
author
kuba--
committedJan 9, 2019
Refine Log.
Signed-off-by: kuba-- <kuba@sourced.tech>
1 parent f65d7f4 commit c9609eb

File tree

3 files changed

+168
-132
lines changed

3 files changed

+168
-132
lines changed
 

‎plumbing/object/commit_walker.go

+71-65
Original file line numberDiff line numberDiff line change
@@ -186,98 +186,104 @@ func (w *commitPostIterator) Close() {}
186186

187187
// commitAllIterator stands for commit iterator for all refs.
188188
type commitAllIterator struct {
189-
// el points to the current commit.
190-
el *list.Element
189+
// currCommit points to the current commit.
190+
currCommit *list.Element
191191
}
192192

193193
// NewCommitAllIter returns a new commit iterator for all refs.
194-
// s is a repo Storer used to get commits and references.
195-
// fn is a commit iterator function, used to iterate through ref commits in chosen order
196-
func NewCommitAllIter(s storage.Storer, fn func(*Commit) CommitIter) (CommitIter, error) {
197-
l := list.New()
198-
m := make(map[plumbing.Hash]*list.Element)
199-
200-
// ...along with the HEAD
201-
head, err := storer.ResolveReference(s, plumbing.HEAD)
194+
// repoStorer is a repo Storer used to get commits and references.
195+
// commitIterFunc is a commit iterator function, used to iterate through ref commits in chosen order
196+
func NewCommitAllIter(repoStorer storage.Storer, commitIterFunc func(*Commit) CommitIter) (CommitIter, error) {
197+
commitsPath := list.New()
198+
commitsLookup := make(map[plumbing.Hash]*list.Element)
199+
head, err := storer.ResolveReference(repoStorer, plumbing.HEAD)
202200
if err != nil {
203201
return nil, err
204202
}
205-
headCommit, err := GetCommit(s, head.Hash())
206-
if err != nil {
203+
204+
// add all references along with the HEAD
205+
if err = addReference(repoStorer, commitIterFunc, head, commitsPath, commitsLookup); err != nil {
207206
return nil, err
208207
}
209-
err = fn(headCommit).ForEach(func(c *Commit) error {
210-
el := l.PushBack(c)
211-
m[c.Hash] = el
212-
return nil
213-
})
208+
refIter, err := repoStorer.IterReferences()
214209
if err != nil {
215210
return nil, err
216211
}
217-
218-
refIter, err := s.IterReferences()
212+
defer refIter.Close()
213+
err = refIter.ForEach(
214+
func(ref *plumbing.Reference) error {
215+
return addReference(repoStorer, commitIterFunc, ref, commitsPath, commitsLookup)
216+
},
217+
)
219218
if err != nil {
220219
return nil, err
221220
}
222-
defer refIter.Close()
223-
err = refIter.ForEach(func(r *plumbing.Reference) error {
224-
if r.Hash() == head.Hash() {
225-
// we already have the HEAD
226-
return nil
227-
}
228221

229-
el, ok := m[r.Hash()]
230-
if ok {
231-
return nil
232-
}
222+
return &commitAllIterator{commitsPath.Front()}, nil
223+
}
224+
225+
func addReference(
226+
repoStorer storage.Storer,
227+
commitIterFunc func(*Commit) CommitIter,
228+
ref *plumbing.Reference,
229+
commitsPath *list.List,
230+
commitsLookup map[plumbing.Hash]*list.Element) error {
231+
232+
_, exists := commitsLookup[ref.Hash()]
233+
if exists {
234+
// we already have it - skip the reference.
235+
return nil
236+
}
233237

234-
var refCommits []*Commit
235-
c, _ := GetCommit(s, r.Hash())
238+
refCommit, _ := GetCommit(repoStorer, ref.Hash())
239+
if refCommit == nil {
236240
// if it's not a commit - skip it.
237-
if c == nil {
238-
return nil
241+
return nil
242+
}
243+
244+
var (
245+
refCommits []*Commit
246+
parent *list.Element
247+
)
248+
// collect all ref commits to add
249+
commitIter := commitIterFunc(refCommit)
250+
for c, e := commitIter.Next(); e == nil; {
251+
parent, exists = commitsLookup[c.Hash]
252+
if exists {
253+
break
239254
}
240-
cit := fn(c)
241-
for c, e := cit.Next(); e == nil; {
242-
el, ok = m[c.Hash]
243-
if ok {
244-
break
245-
}
246-
refCommits = append(refCommits, c)
247-
c, e = cit.Next()
255+
refCommits = append(refCommits, c)
256+
c, e = commitIter.Next()
257+
}
258+
commitIter.Close()
259+
260+
if parent == nil {
261+
// common parent - not found
262+
// add all commits to the path from this ref (maybe it's a HEAD and we don't have anything, yet)
263+
for _, c := range refCommits {
264+
parent = commitsPath.PushBack(c)
265+
commitsLookup[c.Hash] = parent
248266
}
249-
cit.Close()
250-
251-
if el == nil {
252-
// push back all commits from this ref.
253-
for _, c := range refCommits {
254-
el = l.PushBack(c)
255-
m[c.Hash] = el
256-
}
257-
} else {
258-
// insert ref's commits into the list
259-
for i := len(refCommits) - 1; i >= 0; i-- {
260-
c := refCommits[i]
261-
el = l.InsertBefore(c, el)
262-
m[c.Hash] = el
263-
}
267+
} else {
268+
// add ref's commits to the path in reverse order (from the latest)
269+
for i := len(refCommits) - 1; i >= 0; i-- {
270+
c := refCommits[i]
271+
// insert before found common parent
272+
parent = commitsPath.InsertBefore(c, parent)
273+
commitsLookup[c.Hash] = parent
264274
}
265-
return nil
266-
})
267-
if err != nil {
268-
return nil, err
269275
}
270276

271-
return &commitAllIterator{l.Front()}, nil
277+
return nil
272278
}
273279

274280
func (it *commitAllIterator) Next() (*Commit, error) {
275-
if it.el == nil {
281+
if it.currCommit == nil {
276282
return nil, io.EOF
277283
}
278284

279-
c := it.el.Value.(*Commit)
280-
it.el = it.el.Next()
285+
c := it.currCommit.Value.(*Commit)
286+
it.currCommit = it.currCommit.Next()
281287

282288
return c, nil
283289
}
@@ -305,5 +311,5 @@ func (it *commitAllIterator) ForEach(cb func(*Commit) error) error {
305311
}
306312

307313
func (it *commitAllIterator) Close() {
308-
it.el = nil
314+
it.currCommit = nil
309315
}

‎plumbing/object/commit_walker_file.go

+38-27
Original file line numberDiff line numberDiff line change
@@ -3,24 +3,28 @@ package object
33
import (
44
"io"
55

6+
"gopkg.in/src-d/go-git.v4/plumbing"
7+
68
"gopkg.in/src-d/go-git.v4/plumbing/storer"
79
)
810

911
type commitFileIter struct {
1012
fileName string
1113
sourceIter CommitIter
1214
currentCommit *Commit
13-
all bool
15+
checkParent bool
1416
}
1517

1618
// NewCommitFileIterFromIter returns a commit iterator which performs diffTree between
1719
// successive trees returned from the commit iterator from the argument. The purpose of this is
1820
// to find the commits that explain how the files that match the path came to be.
19-
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, all bool) CommitIter {
21+
// If checkParent is true then the function double checks if potential parent (next commit in a path)
22+
// is one of the parents in the tree (it's used by `git log --all`).
23+
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, checkParent bool) CommitIter {
2024
iterator := new(commitFileIter)
2125
iterator.sourceIter = commitIter
2226
iterator.fileName = fileName
23-
iterator.all = all
27+
iterator.checkParent = checkParent
2428
return iterator
2529
}
2630

@@ -74,36 +78,14 @@ func (c *commitFileIter) getNextFileCommit() (*Commit, error) {
7478
return nil, diffErr
7579
}
7680

77-
foundChangeForFile := false
78-
for _, change := range changes {
79-
if change.name() != c.fileName {
80-
continue
81-
}
82-
83-
// filename matches, now check if source iterator contains all commits (from all refs)
84-
if c.all {
85-
// for `git log --all` also check if the next commit comes from the same parent
86-
for _, h := range c.currentCommit.ParentHashes {
87-
if h == parentCommit.Hash {
88-
foundChangeForFile = true
89-
break
90-
}
91-
}
92-
} else {
93-
foundChangeForFile = true
94-
}
95-
96-
if foundChangeForFile {
97-
break
98-
}
99-
}
81+
found := c.hasFileChange(changes, parentCommit)
10082

10183
// Storing the current-commit in-case a change is found, and
10284
// Updating the current-commit for the next-iteration
10385
prevCommit := c.currentCommit
10486
c.currentCommit = parentCommit
10587

106-
if foundChangeForFile == true {
88+
if found {
10789
return prevCommit, nil
10890
}
10991

@@ -114,6 +96,35 @@ func (c *commitFileIter) getNextFileCommit() (*Commit, error) {
11496
}
11597
}
11698

99+
func (c *commitFileIter) hasFileChange(changes Changes, parent *Commit) bool {
100+
for _, change := range changes {
101+
if change.name() != c.fileName {
102+
continue
103+
}
104+
105+
// filename matches, now check if source iterator contains all commits (from all refs)
106+
if c.checkParent {
107+
if parent != nil && isParentHash(parent.Hash, c.currentCommit) {
108+
return true
109+
}
110+
continue
111+
}
112+
113+
return true
114+
}
115+
116+
return false
117+
}
118+
119+
func isParentHash(hash plumbing.Hash, commit *Commit) bool {
120+
for _, h := range commit.ParentHashes {
121+
if h == hash {
122+
return true
123+
}
124+
}
125+
return false
126+
}
127+
117128
func (c *commitFileIter) ForEach(cb func(*Commit) error) error {
118129
for {
119130
commit, nextErr := c.Next()

‎repository.go

+59-40
Original file line numberDiff line numberDiff line change
@@ -1027,64 +1027,83 @@ func (r *Repository) PushContext(ctx context.Context, o *PushOptions) error {
10271027

10281028
// Log returns the commit history from the given LogOptions.
10291029
func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) {
1030+
fn := commitIterFunc(o.Order)
1031+
if fn == nil {
1032+
return nil, fmt.Errorf("invalid Order=%v", o.Order)
1033+
}
1034+
10301035
var (
1031-
err error
1032-
commitIterFunc func(*object.Commit) object.CommitIter
1033-
commitIter object.CommitIter
1036+
it object.CommitIter
1037+
err error
10341038
)
1035-
switch o.Order {
1039+
if o.All {
1040+
it, err = r.logAll(fn)
1041+
} else {
1042+
it, err = r.log(o.From, fn)
1043+
}
1044+
1045+
if err != nil {
1046+
return nil, err
1047+
}
1048+
1049+
if o.FileName != nil {
1050+
// for `git log --all` also check parent (if the next commit comes from the real parent)
1051+
it = r.logWithFile(*o.FileName, it, o.All)
1052+
}
1053+
1054+
return it, nil
1055+
}
1056+
1057+
func (r *Repository) log(from plumbing.Hash, commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) {
1058+
h := from
1059+
if from == plumbing.ZeroHash {
1060+
head, err := r.Head()
1061+
if err != nil {
1062+
return nil, err
1063+
}
1064+
1065+
h = head.Hash()
1066+
}
1067+
1068+
commit, err := r.CommitObject(h)
1069+
if err != nil {
1070+
return nil, err
1071+
}
1072+
return commitIterFunc(commit), nil
1073+
}
1074+
1075+
func (r *Repository) logAll(commitIterFunc func(*object.Commit) object.CommitIter) (object.CommitIter, error) {
1076+
return object.NewCommitAllIter(r.Storer, commitIterFunc)
1077+
}
1078+
1079+
func (*Repository) logWithFile(fileName string, commitIter object.CommitIter, checkParent bool) object.CommitIter {
1080+
return object.NewCommitFileIterFromIter(fileName, commitIter, checkParent)
1081+
}
1082+
1083+
func commitIterFunc(order LogOrder) func(c *object.Commit) object.CommitIter {
1084+
switch order {
10361085
case LogOrderDefault:
1037-
commitIterFunc = func(c *object.Commit) object.CommitIter {
1086+
return func(c *object.Commit) object.CommitIter {
10381087
return object.NewCommitPreorderIter(c, nil, nil)
10391088
}
10401089
case LogOrderDFS:
1041-
commitIterFunc = func(c *object.Commit) object.CommitIter {
1090+
return func(c *object.Commit) object.CommitIter {
10421091
return object.NewCommitPreorderIter(c, nil, nil)
10431092
}
10441093
case LogOrderDFSPost:
1045-
commitIterFunc = func(c *object.Commit) object.CommitIter {
1094+
return func(c *object.Commit) object.CommitIter {
10461095
return object.NewCommitPostorderIter(c, nil)
10471096
}
10481097
case LogOrderBSF:
1049-
commitIterFunc = func(c *object.Commit) object.CommitIter {
1098+
return func(c *object.Commit) object.CommitIter {
10501099
return object.NewCommitIterBSF(c, nil, nil)
10511100
}
10521101
case LogOrderCommitterTime:
1053-
commitIterFunc = func(c *object.Commit) object.CommitIter {
1102+
return func(c *object.Commit) object.CommitIter {
10541103
return object.NewCommitIterCTime(c, nil, nil)
10551104
}
1056-
default:
1057-
return nil, fmt.Errorf("invalid Order=%v", o.Order)
10581105
}
1059-
1060-
if o.All {
1061-
commitIter, err = object.NewCommitAllIter(r.Storer, commitIterFunc)
1062-
if err != nil {
1063-
return nil, err
1064-
}
1065-
} else {
1066-
h := o.From
1067-
if o.From == plumbing.ZeroHash {
1068-
head, err := r.Head()
1069-
if err != nil {
1070-
return nil, err
1071-
}
1072-
1073-
h = head.Hash()
1074-
}
1075-
1076-
commit, err := r.CommitObject(h)
1077-
if err != nil {
1078-
return nil, err
1079-
}
1080-
commitIter = commitIterFunc(commit)
1081-
}
1082-
1083-
if o.FileName != nil {
1084-
commitIter = object.NewCommitFileIterFromIter(*o.FileName, commitIter, o.All)
1085-
}
1086-
1087-
return commitIter, nil
1106+
return nil
10881107
}
10891108

10901109
// Tags returns all the tag References in a repository.

0 commit comments

Comments
 (0)
This repository has been archived.