Thanks to visit codestin.com
Credit goes to github.com

Skip to content

How to get the latest commit that changed a particular file? #729

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
neel1996 opened this issue Jan 18, 2021 · 2 comments
Closed

How to get the latest commit that changed a particular file? #729

neel1996 opened this issue Jan 18, 2021 · 2 comments

Comments

@neel1996
Copy link

Hi, I am looking for a way to get the latest commit that affected a particular file / directory. It is something similar to the following git command

git log -1 FILE_NAME

I have tried my own way by iterating through all the commits in the repo and comparing it with its immediate ancestor to find the files that were modified in a commit. The sample code is as follows,

head, _ := r.Head()
commit, _ := r.LookupCommit(head.Target())
for commit != nil {
  numParents := commit.ParentCount()
  if numParents > 0 {
    parentCommit := commit.Parent(0)
    parentTree, _ := parentCommit.Tree()
    commitTree, _ := commit.Tree()

    if parentTree != nil && commitTree != nil {
      diff, _ := r.DiffTreeToTree(parentTree, commitTree, nil)
      if diff != nil {
        numDeltas, _ := diff.NumDeltas()
        if numDeltas > 0 {
          entry := commitEntry{
          commitMessage: commit.Message(),
        }
        for d := 0; d < numDeltas; d++ {
          delta, _ := diff.Delta(d)
          fileEntry := fileDiffStruct{diffPath: delta.NewFile.Path}
          entry.fileEntries = append(entry.fileEntries, fileEntry)
        }
        commitEntries = append(commitEntries, entry)
      }
    }
   }
  }
  commit = commit.Parent(0)
}

I have a function which compares the target file name with the entries stored in the commitEntries and this logic works as expected for me. The issue arises when the repo size is large.

I tested it with my own repo with around 360 commits and 1100 files, and I get the result in matter of milliseconds. When I tested the same with a larger repo with 20,000 commits and over 5000 objects in the tree, the function takes 3 minutes to return the results.

Is there a built-in API to achieve the expected result or is there any way to optimize my logic to get the expected results faster even for large repos ? Any feedback would be appreciated.

@lhchavez
Copy link
Contributor

there isn't a built-in API to achieve this currently :( I opened a bug against libgit2 so that we could create one, with a pathway towards making it fast: libgit2/libgit2#5758

one thing that you could potentially do is instead of diffing the trees (since it does a lot of additional work since it has to diff every single file), is to try to grab the git.Oid of the file that you're interested in with git.Tree.EntryByPath() in both commits and compare that. if it's the same, it hasn't been modified. this is probably going to be the way the built-in API that i mention above is going to be implemented internally if I had to guess.

@neel1996
Copy link
Author

Thanks so much @lhchavez. Diffing trees was the issue and replacing that with the OId comparing logic worked like a chard. I tried with the same repo with 20k commits and got back the expected results in 2 seconds.

For any one looking for a way to implement a similar solution, below is the code

// allCommits holds all the commits from the repo collected by iterating through all the parents of the HEAD commit
// The same can be achieved using RevWalker (repo.Walk) as well
//
// The approach works for both files and directories

for _, commit := range allCommits {
  if commit != nil {
    numParents := commit.ParentCount()
      if numParents > 0 {
      parent := commit.Parent(0)
      commitTree, _ := commit.Tree()
      parentTree, _ := parent.Tree()
      
      if commitTree != nil && parentTree != nil {
	cId, _ := commitTree.EntryByPath(*file)
	pId, _ := parentTree.EntryByPath(*file)

	if cId != nil && pId != nil {
		if cId.Id.String() != pId.Id.String() {
			fileEntry = *file
			commitMsg = commit.Message()
			break
		}
	}
      }
    }
  }
}

neel1996 added a commit to neel1996/gitconvex-server that referenced this issue Jan 18, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants