|
-
-
-
-
- package git
-
- import (
- "github.com/emirpasic/gods/trees/binaryheap"
- "gopkg.in/src-d/go-git.v4/plumbing"
- "gopkg.in/src-d/go-git.v4/plumbing/object"
- cgobject "gopkg.in/src-d/go-git.v4/plumbing/object/commitgraph"
- )
-
-
- func (tes Entries) GetCommitsInfo(commit *Commit, treePath string, cache LastCommitCache) ([][]interface{}, *Commit, error) {
- entryPaths := make([]string, len(tes)+1)
-
- entryPaths[0] = ""
- for i, entry := range tes {
- entryPaths[i+1] = entry.Name()
- }
-
- commitNodeIndex, commitGraphFile := commit.repo.CommitNodeIndex()
- if commitGraphFile != nil {
- defer commitGraphFile.Close()
- }
-
- c, err := commitNodeIndex.Get(commit.ID)
- if err != nil {
- return nil, nil, err
- }
-
- revs, err := getLastCommitForPaths(c, treePath, entryPaths)
- if err != nil {
- return nil, nil, err
- }
-
- commit.repo.gogitStorage.Close()
-
- commitsInfo := make([][]interface{}, len(tes))
- for i, entry := range tes {
- if rev, ok := revs[entry.Name()]; ok {
- entryCommit := convertCommit(rev)
- if entry.IsSubModule() {
- subModuleURL := ""
- var fullPath string
- if len(treePath) > 0 {
- fullPath = treePath + "/" + entry.Name()
- } else {
- fullPath = entry.Name()
- }
- if subModule, err := commit.GetSubModule(fullPath); err != nil {
- return nil, nil, err
- } else if subModule != nil {
- subModuleURL = subModule.URL
- }
- subModuleFile := NewSubModuleFile(entryCommit, subModuleURL, entry.ID.String())
- commitsInfo[i] = []interface{}{entry, subModuleFile}
- } else {
- commitsInfo[i] = []interface{}{entry, entryCommit}
- }
- } else {
- commitsInfo[i] = []interface{}{entry, nil}
- }
- }
-
-
-
-
- var treeCommit *Commit
- if treePath == "" {
- treeCommit = commit
- } else if rev, ok := revs[""]; ok {
- treeCommit = convertCommit(rev)
- treeCommit.repo = commit.repo
- }
- return commitsInfo, treeCommit, nil
- }
-
- type commitAndPaths struct {
- commit cgobject.CommitNode
-
- paths []string
-
- hashes map[string]plumbing.Hash
- }
-
- func getCommitTree(c cgobject.CommitNode, treePath string) (*object.Tree, error) {
- tree, err := c.Tree()
- if err != nil {
- return nil, err
- }
-
-
- if treePath != "" {
- tree, err = tree.Tree(treePath)
- if err != nil {
- return nil, err
- }
- }
-
- return tree, nil
- }
-
- func getFileHashes(c cgobject.CommitNode, treePath string, paths []string) (map[string]plumbing.Hash, error) {
- tree, err := getCommitTree(c, treePath)
- if err == object.ErrDirectoryNotFound {
-
- return make(map[string]plumbing.Hash), nil
- }
- if err != nil {
- return nil, err
- }
-
- hashes := make(map[string]plumbing.Hash)
- for _, path := range paths {
- if path != "" {
- entry, err := tree.FindEntry(path)
- if err == nil {
- hashes[path] = entry.Hash
- }
- } else {
- hashes[path] = tree.Hash
- }
- }
-
- return hashes, nil
- }
-
- func getLastCommitForPaths(c cgobject.CommitNode, treePath string, paths []string) (map[string]*object.Commit, error) {
-
- heap := binaryheap.NewWith(func(a, b interface{}) int {
- if a.(*commitAndPaths).commit.CommitTime().Before(b.(*commitAndPaths).commit.CommitTime()) {
- return 1
- }
- return -1
- })
-
- resultNodes := make(map[string]cgobject.CommitNode)
- initialHashes, err := getFileHashes(c, treePath, paths)
- if err != nil {
- return nil, err
- }
-
-
- heap.Push(&commitAndPaths{c, paths, initialHashes})
-
- for {
- cIn, ok := heap.Pop()
- if !ok {
- break
- }
- current := cIn.(*commitAndPaths)
-
-
- numParents := current.commit.NumParents()
- var parents []cgobject.CommitNode
- for i := 0; i < numParents; i++ {
- parent, err := current.commit.ParentNode(i)
- if err != nil {
- break
- }
- parents = append(parents, parent)
- }
-
-
- pathUnchanged := make([]bool, len(current.paths))
- parentHashes := make([]map[string]plumbing.Hash, len(parents))
- for j, parent := range parents {
- parentHashes[j], err = getFileHashes(parent, treePath, current.paths)
- if err != nil {
- break
- }
-
- for i, path := range current.paths {
- if parentHashes[j][path] == current.hashes[path] {
- pathUnchanged[i] = true
- }
- }
- }
-
- var remainingPaths []string
- for i, path := range current.paths {
-
-
- if resultNodes[path] == nil {
- if pathUnchanged[i] {
-
-
- remainingPaths = append(remainingPaths, path)
- } else {
-
-
-
-
-
-
-
-
- resultNodes[path] = current.commit
- }
- }
- }
-
- if len(remainingPaths) > 0 {
-
-
- for j, parent := range parents {
-
-
- remainingPathsForParent := make([]string, 0, len(remainingPaths))
- newRemainingPaths := make([]string, 0, len(remainingPaths))
- for _, path := range remainingPaths {
- if parentHashes[j][path] == current.hashes[path] {
- remainingPathsForParent = append(remainingPathsForParent, path)
- } else {
- newRemainingPaths = append(newRemainingPaths, path)
- }
- }
-
- if remainingPathsForParent != nil {
- heap.Push(&commitAndPaths{parent, remainingPathsForParent, parentHashes[j]})
- }
-
- if len(newRemainingPaths) == 0 {
- break
- } else {
- remainingPaths = newRemainingPaths
- }
- }
- }
- }
-
-
- result := make(map[string]*object.Commit)
- for path, commitNode := range resultNodes {
- var err error
- result[path], err = commitNode.Commit()
- if err != nil {
- return nil, err
- }
- }
-
- return result, nil
- }
|