-
-
Save roscopecoltran/651caf92e2a0d0f88a379c3864693cab to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package main | |
import ( | |
"fmt" | |
"os" | |
"time" | |
"gopkg.in/src-d/go-git.v4" | |
"gopkg.in/src-d/go-git.v4/plumbing" | |
"gopkg.in/src-d/go-git.v4/plumbing/object" | |
"gopkg.in/src-d/go-git.v4/plumbing/storer" | |
"gopkg.in/src-d/go-git.v4/storage/filesystem" | |
"gopkg.in/src-d/go-billy.v4/osfs" | |
//"github.com/pkg/profile" | |
) | |
func CheckIfError(err error) { | |
if err == nil { | |
return | |
} | |
fmt.Printf("\x1b[31;1m%s\x1b[0m\n", fmt.Sprintf("error: %s", err)) | |
os.Exit(1) | |
} | |
func main() { | |
//defer profile.Start().Stop() | |
start := time.Now() | |
fs := osfs.New(os.Args[1]); | |
s, err := filesystem.NewStorageWithOptions(fs, filesystem.Options{KeepDescriptors: true}) | |
CheckIfError(err) | |
r, err := git.Open(s, fs) | |
CheckIfError(err) | |
head, err := r.Head() | |
CheckIfError(err) | |
commit, err := r.CommitObject(head.Hash()) | |
CheckIfError(err) | |
tree, err := commit.Tree() | |
CheckIfError(err) | |
var paths []string | |
for _, entry := range tree.Entries { | |
paths = append(paths, entry.Name) | |
} | |
revs, err := getLastCommitForPaths(commit, paths) | |
CheckIfError(err) | |
for i, rev := range revs { | |
fmt.Println(paths[i]); | |
fmt.Println(rev); | |
} | |
s.Close(); | |
elapsed := time.Since(start) | |
fmt.Printf("Time: %s", elapsed) | |
} | |
func getLastCommitForPaths(c *object.Commit, paths []string) ([]*object.Commit, error) { | |
cIter := object.NewCommitIterCTime(c, nil, nil) | |
result := make([]*object.Commit, len(paths)) | |
remainingResults := len(paths) | |
cTree, err := c.Tree(); | |
if err != nil { | |
return nil, err | |
} | |
currentEntryHashes := make([]plumbing.Hash, len(paths)); | |
for i, path := range paths { | |
cEntry, err := cTree.FindEntry(path) | |
if err != nil { | |
return nil, err | |
} | |
currentEntryHashes[i] = cEntry.Hash; | |
} | |
cIter.ForEach(func(current *object.Commit) error { | |
newEntryHashes := make([]plumbing.Hash, len(paths)); | |
err := current.Parents().ForEach(func(parent *object.Commit) error { | |
parentTree, err := parent.Tree() | |
if err != nil { | |
return err; | |
} | |
for i, path := range paths { | |
// skip path if we already found it | |
if currentEntryHashes[i] != plumbing.ZeroHash { | |
// find parents that contain the path | |
if parentEntry, err := parentTree.FindEntry(path); err == nil { | |
// if the hash for the path differs in the parent then the current commit changed it | |
if parentEntry.Hash == currentEntryHashes[i] { | |
newEntryHashes[i] = currentEntryHashes[i]; | |
} else { | |
// mark for saving the result below | |
newEntryHashes[i] = plumbing.ZeroHash; | |
// stop any further processing for this file | |
currentEntryHashes[i] = plumbing.ZeroHash; | |
} | |
} | |
} | |
} | |
return nil; | |
}); | |
if err != nil { | |
return err | |
} | |
// if a file didn't exist in any parent commit then it must have been created in the | |
// current one. also we mark changed files in the loop above as not present in the | |
// parent to simplify processing | |
for i, newEntryHash := range newEntryHashes { | |
if newEntryHash == plumbing.ZeroHash && result[i] == nil { | |
result[i] = current | |
remainingResults-- | |
} | |
} | |
if remainingResults == 0 { | |
return storer.ErrStop | |
} | |
currentEntryHashes = newEntryHashes | |
return nil | |
}); | |
return result, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment