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

Commit 3180dff

Browse files
author
kuba--
committed
Implement git log --all
Signed-off-by: kuba-- <[email protected]>
1 parent 791aea3 commit 3180dff

File tree

5 files changed

+322
-33
lines changed

5 files changed

+322
-33
lines changed

options.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,11 @@ type LogOptions struct {
335335
// Show only those commits in which the specified file was inserted/updated.
336336
// It is equivalent to running `git log -- <file-name>`.
337337
FileName *string
338+
339+
// Pretend as if all the refs in refs/, along with HEAD, are listed on the command line as <commit>.
340+
// It is equivalent to running `git log --all`.
341+
// If set on true, the From option will be ignored.
342+
All bool
338343
}
339344

340345
var (

plumbing/object/commit_walker.go

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package object
22

33
import (
4+
"container/list"
45
"io"
56

67
"gopkg.in/src-d/go-git.v4/plumbing"
78
"gopkg.in/src-d/go-git.v4/plumbing/storer"
9+
"gopkg.in/src-d/go-git.v4/storage"
810
)
911

1012
type commitPreIterator struct {
@@ -181,3 +183,127 @@ func (w *commitPostIterator) ForEach(cb func(*Commit) error) error {
181183
}
182184

183185
func (w *commitPostIterator) Close() {}
186+
187+
// commitAllIterator stands for commit iterator for all refs.
188+
type commitAllIterator struct {
189+
// el points to the current commit.
190+
el *list.Element
191+
}
192+
193+
// 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)
202+
if err != nil {
203+
return nil, err
204+
}
205+
headCommit, err := GetCommit(s, head.Hash())
206+
if err != nil {
207+
return nil, err
208+
}
209+
err = fn(headCommit).ForEach(func(c *Commit) error {
210+
el := l.PushBack(c)
211+
m[c.Hash] = el
212+
return nil
213+
})
214+
if err != nil {
215+
return nil, err
216+
}
217+
218+
refIter, err := s.IterReferences()
219+
if err != nil {
220+
return nil, err
221+
}
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+
}
228+
c, _ := GetCommit(s, r.Hash())
229+
// if it's not a commit - skip it.
230+
if c == nil {
231+
return nil
232+
}
233+
234+
el, ok := m[c.Hash]
235+
if ok {
236+
return nil
237+
}
238+
239+
var refCommits []*Commit
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()
248+
}
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+
}
264+
}
265+
return nil
266+
})
267+
if err != nil {
268+
return nil, err
269+
}
270+
271+
return &commitAllIterator{l.Front()}, nil
272+
}
273+
274+
func (it *commitAllIterator) Next() (*Commit, error) {
275+
if it.el == nil {
276+
return nil, io.EOF
277+
}
278+
279+
c := it.el.Value.(*Commit)
280+
it.el = it.el.Next()
281+
282+
return c, nil
283+
}
284+
285+
func (it *commitAllIterator) ForEach(cb func(*Commit) error) error {
286+
for {
287+
c, err := it.Next()
288+
if err == io.EOF {
289+
break
290+
}
291+
if err != nil {
292+
return err
293+
}
294+
295+
err = cb(c)
296+
if err == storer.ErrStop {
297+
break
298+
}
299+
if err != nil {
300+
return err
301+
}
302+
}
303+
304+
return nil
305+
}
306+
307+
func (it *commitAllIterator) Close() {
308+
it.el = nil
309+
}

plumbing/object/commit_walker_file.go

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,26 @@
11
package object
22

33
import (
4-
"gopkg.in/src-d/go-git.v4/plumbing/storer"
54
"io"
5+
6+
"gopkg.in/src-d/go-git.v4/plumbing/storer"
67
)
78

89
type commitFileIter struct {
910
fileName string
1011
sourceIter CommitIter
1112
currentCommit *Commit
13+
all bool
1214
}
1315

1416
// NewCommitFileIterFromIter returns a commit iterator which performs diffTree between
1517
// successive trees returned from the commit iterator from the argument. The purpose of this is
1618
// to find the commits that explain how the files that match the path came to be.
17-
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter) CommitIter {
19+
func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, all bool) CommitIter {
1820
iterator := new(commitFileIter)
1921
iterator.sourceIter = commitIter
2022
iterator.fileName = fileName
23+
iterator.all = all
2124
return iterator
2225
}
2326

@@ -73,8 +76,24 @@ func (c *commitFileIter) getNextFileCommit() (*Commit, error) {
7376

7477
foundChangeForFile := false
7578
for _, change := range changes {
76-
if change.name() == c.fileName {
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 {
7793
foundChangeForFile = true
94+
}
95+
96+
if foundChangeForFile {
7897
break
7998
}
8099
}

repository.go

Lines changed: 47 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1027,41 +1027,64 @@ 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-
h := o.From
1031-
if o.From == plumbing.ZeroHash {
1032-
head, err := r.Head()
1033-
if err != nil {
1034-
return nil, err
1035-
}
1036-
1037-
h = head.Hash()
1038-
}
1039-
1040-
commit, err := r.CommitObject(h)
1041-
if err != nil {
1042-
return nil, err
1043-
}
1044-
1045-
var commitIter object.CommitIter
1030+
var (
1031+
err error
1032+
commitIterFunc func(*object.Commit) object.CommitIter
1033+
commitIter object.CommitIter
1034+
)
10461035
switch o.Order {
10471036
case LogOrderDefault:
1048-
commitIter = object.NewCommitPreorderIter(commit, nil, nil)
1037+
commitIterFunc = func(c *object.Commit) object.CommitIter {
1038+
return object.NewCommitPreorderIter(c, nil, nil)
1039+
}
10491040
case LogOrderDFS:
1050-
commitIter = object.NewCommitPreorderIter(commit, nil, nil)
1041+
commitIterFunc = func(c *object.Commit) object.CommitIter {
1042+
return object.NewCommitPreorderIter(c, nil, nil)
1043+
}
10511044
case LogOrderDFSPost:
1052-
commitIter = object.NewCommitPostorderIter(commit, nil)
1045+
commitIterFunc = func(c *object.Commit) object.CommitIter {
1046+
return object.NewCommitPostorderIter(c, nil)
1047+
}
10531048
case LogOrderBSF:
1054-
commitIter = object.NewCommitIterBSF(commit, nil, nil)
1049+
commitIterFunc = func(c *object.Commit) object.CommitIter {
1050+
return object.NewCommitIterBSF(c, nil, nil)
1051+
}
10551052
case LogOrderCommitterTime:
1056-
commitIter = object.NewCommitIterCTime(commit, nil, nil)
1053+
commitIterFunc = func(c *object.Commit) object.CommitIter {
1054+
return object.NewCommitIterCTime(c, nil, nil)
1055+
}
10571056
default:
10581057
return nil, fmt.Errorf("invalid Order=%v", o.Order)
10591058
}
10601059

1061-
if o.FileName == nil {
1062-
return commitIter, nil
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)
10631081
}
1064-
return object.NewCommitFileIterFromIter(*o.FileName, commitIter), nil
1082+
1083+
if o.FileName != nil {
1084+
commitIter = object.NewCommitFileIterFromIter(*o.FileName, commitIter, o.All)
1085+
}
1086+
1087+
return commitIter, nil
10651088
}
10661089

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

0 commit comments

Comments
 (0)