Skip to content

Instantly share code, notes, and snippets.

@filipnavara
Last active November 16, 2018 00:06
Show Gist options
  • Save filipnavara/8e6fdf980130d6ca120bfda4c25481e9 to your computer and use it in GitHub Desktop.
Save filipnavara/8e6fdf980130d6ca120bfda4c25481e9 to your computer and use it in GitHub Desktop.
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