11

How do I get a list of commits which contain a particular file, ie the equivalent of git log path for LibGit2Sharp.

Has it not been implemented or is there a way that I'm missing?

toofarsideways
  • 3,956
  • 2
  • 31
  • 51
  • 2
    There's a **[feature request](https://github.com/libgit2/libgit2sharp/issues/89)** in the LibGit2Sharp issue tracker specifically dealing with this topic. – nulltoken Oct 30 '12 at 07:49

4 Answers4

6

I was working on getting the same functionality into my application with LibGit2Sharp.

I wrote the code below which will list all of the commits that contain the file. The GitCommit class isn't included, but it is just a collection of properties.

My intention was to have the code only list commits where the file had changed, similar to a SVN log, but I haven't written that part yet.

Please note that the code hasn't been optimized, it was merely my initial attempt, but I hope it will be useful.

/// <summary>
/// Loads the history for a file
/// </summary>
/// <param name="filePath">Path to file</param>
/// <returns>List of version history</returns>
public List<IVersionHistory> LoadHistory(string filePath)
{
    LibGit2Sharp.Repository repo = new Repository(this.pathToRepo);

    string path = filePath.Replace(this.pathToRepo.Replace(System.IO.Path.DirectorySeparatorChar + ".git", string.Empty), string.Empty).Substring(1);
    List<IVersionHistory> list = new List<IVersionHistory>();

    foreach (Commit commit in repo.Head.Commits)
    {
        if (this.TreeContainsFile(commit.Tree, path) && list.Count(x => x.Date == commit.Author.When) == 0)
        {
            list.Add(new GitCommit() { Author = commit.Author.Name, Date = commit.Author.When, Message = commit.MessageShort} as IVersionHistory);
        }
    }

    return list;
}

/// <summary>
/// Checks a GIT tree to see if a file exists
/// </summary>
/// <param name="tree">The GIT tree</param>
/// <param name="filename">The file name</param>
/// <returns>true if file exists</returns>
private bool TreeContainsFile(Tree tree, string filename)
{
    if (tree.Any(x => x.Path == filename))
    {
        return true;
    }
    else
    {
        foreach (Tree branch in tree.Where(x => x.Type == GitObjectType.Tree).Select(x => x.Target as Tree))
        {
            if (this.TreeContainsFile(branch, filename))
            {
                return true;
            }
        }
    }

    return false;
}
dmck
  • 7,801
  • 7
  • 43
  • 79
  • Just out of curiosity, what is an `IVersionHistory`? – toofarsideways Oct 29 '12 at 16:21
  • @toofarsideways my application works with SVN and Git repos, so I have a interface for file version history logs. – dmck Oct 29 '12 at 16:23
  • I'd suggest you to rather use diff based approach which should be faster. You can peek at a proposal leveraging this approach **[here](https://github.com/libgit2/libgit2sharp/issues/89#issuecomment-6983251)** – nulltoken Oct 30 '12 at 09:23
  • I've noticed that earlier, however, I'm not sure where `repo.History` which appears in `libgit2sharp/Follow test/Program.cs` appears in... I suppose I'd better pull it down and grep it then, thanks! – toofarsideways Nov 02 '12 at 18:03
  • @nulltoken Right, found it and trying it out, unfortunately it doesn't seem to generate the commit list for the file. – toofarsideways Nov 02 '12 at 18:51
  • A bug? Ah. Maybe could you add a comment to the code in order to ping the author? – nulltoken Nov 03 '12 at 18:33
  • @nulltoken That's the odd thing, he has written a test case, that I suspect is fully functioning, I'll have a crack tracing this thing to see if I can isolate it... – toofarsideways Nov 05 '12 at 13:01
  • @nulltoken Ok, I've figured out my error, I'm running it against an older commit, however the new version uses a version of `Diff.Compare` that doesn't exist on the NuGet 0.95 package version. Cue fiddling with the compiled version and trying to work out how to fix the `Proxy.cs` file complaining about an `EntryPointNotFoundException` as apparently `git_index_get_bypath` isn't implemented in the git.dll that ships with that version. (I'm using `vNext 1f3757bd`) – toofarsideways Nov 05 '12 at 16:44
  • 1
    The dll has to to match the managed code. I'd suggest starting from the vNext branch. Also, maybe should we take this discussion out of SO? :-) – nulltoken Nov 05 '12 at 19:11
5

LibGit2Sharp comes from the C library libgit2... which didn't include git log in the first place ;)

Yet, LibGit2Sharp has its own git log function:
Its page on git log involves Filters, but a Filter doesn't seem to filter by path (as detailed in "How to exclude stashes while querying refs?").
So it doesn't seem to be implemented at the moment.

Community
  • 1
  • 1
VonC
  • 1,262,500
  • 529
  • 4,410
  • 5,250
4

Each time when tree/blob has been changed it gets new id hash. All you need is to compare with parent commit tree/blob item hash:

var commits = repository.Commits
   .Where(c => c.Parents.Count() == 1 && c.Tree["file"] != null &&
      (c.Parents.FirstOrDefault().Tree["file"] == null ||
         c.Tree["file"].Target.Id !=
         c.Parents.FirstOrDefault().Tree["file"].Target.Id));
Ian Mercer
  • 38,490
  • 8
  • 97
  • 133
igoryok.zp
  • 63
  • 6
1

Very similar to dmck's answer but more up to date

private bool TreeContainsFile(Tree tree, string filePath)
{
    //filePath is the relative path of your file to the root directory
    if (tree.Any(x => x.Path == filePath))
    {
        return true;
    }

    return tree.Where(x => x.GetType() == typeof (TreeEntry))
        .Select(x => x.Target)
        .OfType<Tree>()
        .Any(branch => TreeContainsFile(branch, filePath));
}
nemo
  • 1,675
  • 10
  • 16