How to easily get all Refs for a given Commit? - libgit2sharp

Is there an easy way to find all References (e.g. Tags) for a given Commit?
For example:
using( Repository repo = new Repository( path_to_repo ) )
{
foreach( Commit commit in repo.Commits )
{
List<Reference> assignedRefs = commit.Refs; // <- how to do this?
}
}

The code below retrieves all the references that can reach the selected commit.
using( Repository repo = new Repository( path_to_repo ) )
{
foreach( Commit commit in repo.Commits )
{
IEnumerable<Reference> refs =
repo.Refs.ReachableFrom(new[] { commit });
}
}
If you want to only retrieve Tags, the method exposes an overload to help you with this.
using( Repository repo = new Repository( path_to_repo ) )
{
var allTags = repo.Refs.Where(r => r.IsTag()).ToList();
foreach( Commit commit in repo.Commits )
{
IEnumerable<Reference> refs =
repo.Refs.ReachableFrom(allTags, new[] { commit });
}
}
Note: This code will retrieve all refs that either directly point to the chosen commit or point to an ancestor of this commit. In this sense, it behaves similarly to the git rev-list command.

As #user1130329 pointed out, going through all refs multiple times can be very expensive. Just as an alternative, this is what I have done here for a similar case:
logs = _repo.Commits
.QueryBy(filter)
.Select(c => new LogEntry
{
Id = c.Id.Sha,
// other fields
})
.ToList();
var logsDictionary = logs.ToDictionary(d => d.Id);
_repo.Refs
.Where(r => r.IsTag || r.IsLocalBranch)
.ForEach(r =>
{
if (r.IsTag) logsDictionary.GetValueOrDefault(r.TargetIdentifier)?.Tags.Add(r.CanonicalName);
if (r.IsLocalBranch) logsDictionary.GetValueOrDefault(r.TargetIdentifier)?.Heads.Add(r.CanonicalName);
});

Related

How can I get the last commit for a folder using LibGit2Sharp?

I've got a large number of projects all in a single repository. I want to determine the volatility of all of these projects, i.e., when there was last a commit that affected each project. I've got a list of all of the project paths, and I'm trying to find the last commit for each one. My code looks like this:
public CommitInfo GetLastCommit(string path)
{
// resolve any \..\ and pathing weirdness
path = Path.GetDirectoryName(Path.GetFullPath(path));
var relativePath = path.Substring(BaseRepoPath.Length + 1).Replace("\\", "/");
if (!CommitCache.TryGetValue(relativePath, out CommitInfo result))
{
var options = new RepositoryOptions()
{
WorkingDirectoryPath = BaseRepoPath
};
using (var repo = new Repository(BaseRepoPath, options))
{
var filter = new CommitFilter()
{
IncludeReachableFrom = BranchName
};
var commit = repo.Commits.QueryBy(relativePath, filter).First().Commit;
result = new CommitInfo
{
When = commit.Author.When.DateTime,
Who = commit.Author.Name,
Message = commit.Message,
Files = commit.Tree.Select(x => x.Name).ToList()
};
repo.Dispose();
}
CommitCache.Add(relativePath, result);
}
return result;
}
This works, but the line where the commit is actually retrieved:
var commit = repo.Commits.QueryBy(relativePath, filter).First().Commit;
Can take up to eight minutes to complete. As far as I can tell, there's nothing especially complex about those folders...a sample of them reveals maybe twenty commits. I suspect I'm doing something wrong like loading the entire repo graph when I need something more specific, but I haven't been able to figure out a better way.
Thoughts?
Your requirement is producing following git command through lib2gitsharp package.
$ git log -1 -C "relativePath"
You can limit the size of commits with the help of Take(numberOfCommits) extension in lib2gitsharp. Please have a try with putting Take(1) before your First() like following;
var commit = repo.Commits.QueryBy(relativePath, filter).Take(1).First().Commit;
Hope this helps.

How to get the Commits against a VSTS WorkItem that uses Git as source control

I have a VSTS project that uses Git for source control. The project has multiple repositories (11 to be precise) each with multiple branches.
Given a VSTS Id I am trying to get a list of all the Commits that are associated with that Id.
I am currently coding as follows
VSTSHelper helper = new VSTSHelper(); // my helper for establishing a connection
ProjectHttpClient projectClient = helper.Connection.GetClient<ProjectHttpClient>();
GitHttpClient gitClient = helper.Connection.GetClient<GitHttpClient>();
Microsoft.TeamFoundation.Core.WebApi.TeamProject project = projectClient.GetProject(helper.Project).Result;
List<GitRepository> gitRepositoryList = gitClient.GetRepositoriesAsync(project.Id).Result;
GitQueryCommitsCriteria criteria = new GitQueryCommitsCriteria()
{
IncludeWorkItems = true
};
foreach (GitRepository repo in gitRepositoryList)
{
List<GitBranchStats> branchStatsList = gitClient.GetBranchesAsync(repo.Id).Result;
foreach (GitBranchStats branchStats in branchStatsList)
{
criteria.ItemVersion = new GitVersionDescriptor() { Version = branchStats.Name, VersionType = GitVersionType.Branch };
List<GitCommitRef> commits = gitClient.GetCommitsAsync(repo.Id, criteria).Result;
if (commits.Count > 0)
{
List<GitCommitRef> commitsWithWorkItems = commits.Where(pc => pc.WorkItems.Count > 0).ToList();
if (commitsWithWorkItems.Count > 0)
{
// WorkItemIds is a list of int
List<GitCommitRef> workItemCommits = projectCommitsWithWorkItems.Where(
pc => pc.WorkItems.Any(x => this.WorkItemIds.Contains(Convert.ToInt32(x.Id)))).ToList();
// get the changes associated with the commit here
if (workItemCommits.Count > 0)
{
foreach (GitCommitRef wiCommit in workItemCommits)
{
GitCommitChanges wiChanges = gitClient.GetChangesAsync(wiCommit.CommitId, repo.Id).Result;
}
}
}
}
}
}
The work item id that is passed to my code (e.g Id = 810) has code that was originally committed against another Work Item Id (e.g. 675) in a different branch and then moved to the given Id. The code was then edited and then committed against the new Id (810)
My code above only ever finds the original commit against item 675 - and all the code changes are shown against this Id - including those that I was expecting to see against 810. Nothing is ever returned for Id 810.
Despite lots of googling I find myself struggling big time and I presume that I am misunderstanding some big time!
Any help or pointers in the right direction would be greatly appreciated.
You can use below code to get all the commits which link to a given work item:
As the REST API GET a work item with Fully expanded, all the links information exist under relations object. Then you can get the commits by filtering the url which contains vstfs:///Git/Commit.
int id = workitemID;
var token = "PAT";
String Uri = "https://account.visualstudio.com/DefaultCollection";
var Connection1 = new VssConnection(new Uri(Uri), new VssBasicCredential(string.Empty, token));
WorkItemTrackingHttpClient workItemTrackingClient = Connection1.GetClient<WorkItemTrackingHttpClient>();
WorkItem workitem = workItemTrackingClient.GetWorkItemAsync(id, expand: WorkItemExpand.All).Result;
Console.WriteLine("Related commits contain: ");
foreach (var relation in workitem.Relations)
{
if (relation.Url.Contains("vstfs:///Git/Commit"))
{
Console.WriteLine("commit: {0}", relation.Url);
}
}
To Get the commit sha-1 value (commit ID), it's located in the last 40 characters of the relation.Url which separate with repository ID by %2f. So the url format actually is vstfs:///Git/Commit/{projectID}%2f{repositoryID}%2f{commitID}.
Such as for a relation.Url as below:
vstfs:///Git/Commit/f7855e29-6f8d-429d-8c9b-41fd4d7e70a4%2fad3acf8e-b269-48e5-81bc-354251856b51%2fb802c91e68f321676fe31eca9dda048e7032ea11"
The commit sha-1 value is b802c91e68f321676fe31eca9dda048e7032ea11.
The repository ID is ad3acf8e-b269-48e5-81bc-354251856b51.
The project ID is f7855e29-6f8d-429d-8c9b-41fd4d7e70a4.

libgit2sharp get commits between and including

I'm using filter with QueryBy to get the commits that I'm interested in.
// Set the filter
CommitFilter filter = null;
filter = new CommitFilter()
{
IncludeReachableFrom = "85494c32921c136cc3381fc14a3a20e08012c514",
ExcludeReachableFrom = "63c8cb9ed585c0f3b79b6e2efc067e254910f875"
};
// Get the commits
repo.Commits.QueryBy(filter)
The QueryBy will not return the 63c8cb9ed585c0f3b79b6e2efc067e254910f875 commit, it will return the commits between the two.
Is there a way to have the 63c8cb9ed585c0f3b79b6e2efc067e254910f875 commit included as well ?
Maybe there another way available that I can use to get to the previous 63c8cb9ed585c0f3b79b6e2efc067e254910f875 from the commit that came after 63c8cb9ed585c0f3b79b6e2efc067e254910f875 and is returned by the query ?
You can use parents of the Commit as list of commit pointers for your ExcludeReachableFrom filter.
Example:
var filter = new CommitFilter
{
IncludeReachableFrom = "824201fcb8d4fa79b0aafa7c5aea86643cdd118a",
ExcludeReachableFrom = repo.Lookup<Commit>("bcd85da0e287a3b404d12f8b666888962f692076").Parents
};
var commits = repo.Commits.QueryBy(filter);
foreach (var commit in commits)
{
Console.WriteLine($"{commit.Sha}");
}
Output:
824201fcb8d4fa79b0aafa7c5aea86643cdd118a
934fa3892acb2a48f296b7afc66b07125fb6db91
da995a21dc3fd038173695776fc1a3f4ff64f6ab
a9ee8086e5647141087c90909cd847a5fa5f294e
6313ca4b41dfef4d6b779f34f7b4807917c31188
bcd85da0e287a3b404d12f8b666888962f692076

Retrieve firstparentonly on master using libgit2sharp

I want to retrieve commits from master first parent only using libgit2sharp, the equivalent command line statement is
git log --first-parent master --oneline
I know of the CommitFilter that I can use with QueryBy, like this
repo.Commits.QueryBy(new CommitFilter() { FirstParentOnly = true })
I'm getting commits on the branch that I'm currently on if I'm not on master. Can I limit the commit results to master, even when I'm on a different branch ?
I believe you are looking for the IncludeReachableFrom in the CommitFilter.
You might want to also use ExcludeReachableFrom at the same time to remove the commits from the branch that you are currently on...
using (var repo = new Repository("/Users/sushi/code/redux/playscript"))
{
var commitLog = repo.Commits.QueryBy(new CommitFilter() { FirstParentOnly = true, IncludeReachableFrom = "master" });
foreach (var commit in commitLog)
{
Console.WriteLine($"{commit.Sha}");
Console.ReadKey();
}
}

History log in sub directory

I'm trying to figure out how to get the commit log for a sub directory. According to this thread, it should work fine in libgit2sharp.
For testing I'm using a small repo, https://github.com/ornatwork/nugetpackages.git that has 8 total entries for the whole repository.
I cloned it locally in root c:/nugetpackages, from that folder I can do.
git log -- devbox
on the command line and I will get two commit entries for the /devbox sub directory as expected.
Sample Xunit test code to do the same using libgit2sharp
[Fact]
public void testSub()
{
// Extract the git commit history
using (var repo = new Repository(#"C:\nugetpackages"))
{
Trace.WriteLine("repo count=" + repo.Commits.Count());
// absolute path
IEnumerable<LogEntry> history = repo.Commits.QueryBy(#"C:\nugetpackages\devbox");
Trace.WriteLine("subdir count=" + history.Count());
}
}
I'm expecting count of 8 and 2, but this is what I get.
repo count=8
subdir count=0
What am I missing ?
Use a relative path from the repository's base diretory:
using (var repo = new Repository(#"/Users/sushi/code/sushi/Xamarin.Forms.Renderer.Tests"))
{
D.WriteLine("repo count=" + repo.Commits.Count());
IEnumerable<LogEntry> history = repo.Commits.QueryBy(#"AlarmSO");
D.WriteLine("subdir count=" + history.Count());
}
ref: FileHistoryFixture.cs
Update:
Follow up, is there a way to combine subdir with a filter, for example. CommitFilter filter = new CommitFilter(); filter.FirstParentOnly = true;
Not sure if this is what you are looking for... if not please in a new question, thanks.
using (var repo = new Repository(#"/Users/sushi/code/sushi/RealmJson"))
{
var subDir = "media";
var commits = repo.Commits.QueryBy(new CommitFilter { FirstParentOnly = true }).Where(
(Commit c) => c.Tree.Any(
(TreeEntry te) => te.Path.StartsWith(subDir, StringComparison.Ordinal)));
foreach (var commit in commits)
{
D.WriteLine($"{commit.Sha} : {commit.MessageShort}");
}
}

Resources