-
Notifications
You must be signed in to change notification settings - Fork 531
Implement git log --all #1045
Implement git log --all #1045
Changes from 1 commit
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,10 +1,12 @@ | ||
| package object | ||
|
|
||
| import ( | ||
| "container/list" | ||
| "io" | ||
|
|
||
| "gopkg.in/src-d/go-git.v4/plumbing" | ||
| "gopkg.in/src-d/go-git.v4/plumbing/storer" | ||
| "gopkg.in/src-d/go-git.v4/storage" | ||
| ) | ||
|
|
||
| type commitPreIterator struct { | ||
|
|
@@ -181,3 +183,127 @@ func (w *commitPostIterator) ForEach(cb func(*Commit) error) error { | |
| } | ||
|
|
||
| func (w *commitPostIterator) Close() {} | ||
|
|
||
| // commitAllIterator stands for commit iterator for all refs. | ||
| type commitAllIterator struct { | ||
| // el points to the current commit. | ||
| el *list.Element | ||
| } | ||
|
|
||
| // NewCommitAllIter returns a new commit iterator for all refs. | ||
| // s is a repo Storer used to get commits and references. | ||
| // fn is a commit iterator function, used to iterate through ref commits in chosen order | ||
| func NewCommitAllIter(s storage.Storer, fn func(*Commit) CommitIter) (CommitIter, error) { | ||
| l := list.New() | ||
| m := make(map[plumbing.Hash]*list.Element) | ||
|
|
||
| // ...along with the HEAD | ||
| head, err := storer.ResolveReference(s, plumbing.HEAD) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| headCommit, err := GetCommit(s, head.Hash()) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| err = fn(headCommit).ForEach(func(c *Commit) error { | ||
| el := l.PushBack(c) | ||
| m[c.Hash] = el | ||
| return nil | ||
| }) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| refIter, err := s.IterReferences() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| defer refIter.Close() | ||
| err = refIter.ForEach(func(r *plumbing.Reference) error { | ||
|
||
| if r.Hash() == head.Hash() { | ||
| // we already have the HEAD | ||
| return nil | ||
| } | ||
| c, _ := GetCommit(s, r.Hash()) | ||
| // if it's not a commit - skip it. | ||
| if c == nil { | ||
| return nil | ||
| } | ||
|
|
||
| el, ok := m[c.Hash] | ||
kuba-- marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| if ok { | ||
| return nil | ||
| } | ||
|
|
||
| var refCommits []*Commit | ||
| cit := fn(c) | ||
| for c, e := cit.Next(); e == nil; { | ||
| el, ok = m[c.Hash] | ||
| if ok { | ||
| break | ||
| } | ||
| refCommits = append(refCommits, c) | ||
jfontan marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| c, e = cit.Next() | ||
| } | ||
| cit.Close() | ||
|
|
||
| if el == nil { | ||
| // push back all commits from this ref. | ||
| for _, c := range refCommits { | ||
| el = l.PushBack(c) | ||
| m[c.Hash] = el | ||
| } | ||
| } else { | ||
| // insert ref's commits into the list | ||
| for i := len(refCommits) - 1; i >= 0; i-- { | ||
| c := refCommits[i] | ||
| el = l.InsertBefore(c, el) | ||
| m[c.Hash] = el | ||
| } | ||
| } | ||
| return nil | ||
| }) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| return &commitAllIterator{l.Front()}, nil | ||
| } | ||
|
|
||
| func (it *commitAllIterator) Next() (*Commit, error) { | ||
| if it.el == nil { | ||
| return nil, io.EOF | ||
| } | ||
|
|
||
| c := it.el.Value.(*Commit) | ||
| it.el = it.el.Next() | ||
|
|
||
| return c, nil | ||
| } | ||
|
|
||
| func (it *commitAllIterator) ForEach(cb func(*Commit) error) error { | ||
| for { | ||
| c, err := it.Next() | ||
| if err == io.EOF { | ||
| break | ||
| } | ||
| if err != nil { | ||
| return err | ||
| } | ||
|
|
||
| err = cb(c) | ||
| if err == storer.ErrStop { | ||
| break | ||
| } | ||
| if err != nil { | ||
| return err | ||
| } | ||
| } | ||
|
|
||
| return nil | ||
| } | ||
|
|
||
| func (it *commitAllIterator) Close() { | ||
| it.el = nil | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,23 +1,26 @@ | ||
| package object | ||
|
|
||
| import ( | ||
| "gopkg.in/src-d/go-git.v4/plumbing/storer" | ||
| "io" | ||
|
|
||
| "gopkg.in/src-d/go-git.v4/plumbing/storer" | ||
| ) | ||
|
|
||
| type commitFileIter struct { | ||
| fileName string | ||
| sourceIter CommitIter | ||
| currentCommit *Commit | ||
| all bool | ||
| } | ||
|
|
||
| // NewCommitFileIterFromIter returns a commit iterator which performs diffTree between | ||
| // successive trees returned from the commit iterator from the argument. The purpose of this is | ||
| // to find the commits that explain how the files that match the path came to be. | ||
| func NewCommitFileIterFromIter(fileName string, commitIter CommitIter) CommitIter { | ||
| func NewCommitFileIterFromIter(fileName string, commitIter CommitIter, all bool) CommitIter { | ||
|
||
| iterator := new(commitFileIter) | ||
| iterator.sourceIter = commitIter | ||
| iterator.fileName = fileName | ||
| iterator.all = all | ||
| return iterator | ||
| } | ||
|
|
||
|
|
@@ -73,8 +76,24 @@ func (c *commitFileIter) getNextFileCommit() (*Commit, error) { | |
|
|
||
| foundChangeForFile := false | ||
| for _, change := range changes { | ||
| if change.name() == c.fileName { | ||
| if change.name() != c.fileName { | ||
| continue | ||
| } | ||
|
|
||
| // filename matches, now check if source iterator contains all commits (from all refs) | ||
| if c.all { | ||
|
||
| // for `git log --all` also check if the next commit comes from the same parent | ||
| for _, h := range c.currentCommit.ParentHashes { | ||
| if h == parentCommit.Hash { | ||
| foundChangeForFile = true | ||
| break | ||
| } | ||
| } | ||
| } else { | ||
| foundChangeForFile = true | ||
| } | ||
|
|
||
| if foundChangeForFile { | ||
| break | ||
| } | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1027,41 +1027,64 @@ func (r *Repository) PushContext(ctx context.Context, o *PushOptions) error { | |
|
|
||
| // Log returns the commit history from the given LogOptions. | ||
| func (r *Repository) Log(o *LogOptions) (object.CommitIter, error) { | ||
| h := o.From | ||
| if o.From == plumbing.ZeroHash { | ||
| head, err := r.Head() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| h = head.Hash() | ||
| } | ||
|
|
||
| commit, err := r.CommitObject(h) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| var commitIter object.CommitIter | ||
| var ( | ||
| err error | ||
| commitIterFunc func(*object.Commit) object.CommitIter | ||
| commitIter object.CommitIter | ||
| ) | ||
| switch o.Order { | ||
| case LogOrderDefault: | ||
| commitIter = object.NewCommitPreorderIter(commit, nil, nil) | ||
| commitIterFunc = func(c *object.Commit) object.CommitIter { | ||
| return object.NewCommitPreorderIter(c, nil, nil) | ||
| } | ||
| case LogOrderDFS: | ||
| commitIter = object.NewCommitPreorderIter(commit, nil, nil) | ||
| commitIterFunc = func(c *object.Commit) object.CommitIter { | ||
| return object.NewCommitPreorderIter(c, nil, nil) | ||
| } | ||
| case LogOrderDFSPost: | ||
| commitIter = object.NewCommitPostorderIter(commit, nil) | ||
| commitIterFunc = func(c *object.Commit) object.CommitIter { | ||
| return object.NewCommitPostorderIter(c, nil) | ||
| } | ||
| case LogOrderBSF: | ||
| commitIter = object.NewCommitIterBSF(commit, nil, nil) | ||
| commitIterFunc = func(c *object.Commit) object.CommitIter { | ||
| return object.NewCommitIterBSF(c, nil, nil) | ||
| } | ||
| case LogOrderCommitterTime: | ||
| commitIter = object.NewCommitIterCTime(commit, nil, nil) | ||
| commitIterFunc = func(c *object.Commit) object.CommitIter { | ||
| return object.NewCommitIterCTime(c, nil, nil) | ||
| } | ||
| default: | ||
| return nil, fmt.Errorf("invalid Order=%v", o.Order) | ||
| } | ||
|
|
||
| if o.FileName == nil { | ||
| return commitIter, nil | ||
| if o.All { | ||
|
||
| commitIter, err = object.NewCommitAllIter(r.Storer, commitIterFunc) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| } else { | ||
| h := o.From | ||
| if o.From == plumbing.ZeroHash { | ||
| head, err := r.Head() | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
|
|
||
| h = head.Hash() | ||
| } | ||
|
|
||
| commit, err := r.CommitObject(h) | ||
| if err != nil { | ||
| return nil, err | ||
| } | ||
| commitIter = commitIterFunc(commit) | ||
| } | ||
| return object.NewCommitFileIterFromIter(*o.FileName, commitIter), nil | ||
|
|
||
| if o.FileName != nil { | ||
| commitIter = object.NewCommitFileIterFromIter(*o.FileName, commitIter, o.All) | ||
| } | ||
|
|
||
| return commitIter, nil | ||
| } | ||
|
|
||
| // Tags returns all the tag References in a repository. | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is similar to
revlist.Objectsbut for commits.This is just a note that, in the future, we might want to deprecate
revlist.Objectsin favor ofNewObjectAllIter.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe worth to do it now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but it should go in another PR (IMO)