Skip to content

RoslynTools: handle snap commits - #85843

Open
jjonescz wants to merge 5 commits into
dotnet:mainfrom
jjonescz:pr-list
Open

jjonescz wants to merge 5 commits into
dotnet:mainfrom
jjonescz:pr-list

Conversation

@jjonescz

@jjonescz jjonescz commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

For example, the PR list in insertion https://dev.azure.com/devdiv/DevDiv/_git/VS/pullrequest/787957 contains commits from release/insiders branch even though main branch was snapped into it. This PR should fix that and display only the following:

View Complete Diff of Changes

Copilot AI balanced review requested due to automatic review settings September 30, 2026 12:35
@jjonescz
jjonescz requested a review from a team as a code owner September 30, 2026 12:35
@azure-pipelines

Copy link
Copy Markdown
Azure Pipelines:
Successfully started running 2 pipeline(s).
There may be pipelines that require an authorized user to comment /azp run to run.

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Azure DevOps commit details are fetched with unbounded concurrency, risking throttling for large ranges.

Review effort: Balanced
Findings: 1 High severity

Open (1)
What changed in this PR

Updates RoslynTools changelog generation to exclude branch history replaced by snap merges.

Changes:

  • Adds tree-aware commit-history filtering.
  • Supports paginated GitHub and Azure DevOps comparisons.
  • Adds regression tests across local Git and both providers.
File Description
Tool/​PRFinder/​PRFinder.cs Applies filtering and cancellation.
Tool/​PRFinder/​GitCommit.cs Records parent commits.
Tool/​PRFinder/​CommitHistoryFilter.cs Implements tree-based filtering.
Tool/​Insertion/​RoslynInsertionTool.VisualStudioTeamServices.cs Adds provider pagination and filtering.
Tool/​Insertion/​RoslynInsertionTool.cs Propagates cancellation.
Tool/​Commands/​PRFinderCommand.cs Passes command cancellation.
Test/​PRFinder/​PRFinderTests.cs Tests local repository behavior.
Test/​PRFinder/​CommitHistoryFilterTests.cs Tests graph-filtering scenarios.
Test/​Insertion/​InsertionChangelogTests.cs Tests GitHub changelogs.
Test/​Insertion/​AzureInsertionChangelogTests.cs Tests Azure DevOps changelogs.

Copilot AI balanced review requested due to automatic review settings September 30, 2026 13:51

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

Azure filtering should reuse fetched tree IDs to avoid potentially hundreds of sequential REST requests.

Review effort: Balanced
Findings: None

Resolved since last review (1)
Previously missed (1)

In code that hasn't changed since last review

Medium severity Avoid redundant commit fetches causing PR creation delays

src/​Tools/​dotnet-roslyn-tools/​Tool/​Insertion/​RoslynInsertionTool.VisualStudioTeamServices.cs:614

The filter now performs a sequential GetItemAsync REST call for every unique merge commit and parent, even though the full GitCommit objects fetched above already include TreeId. A large insertion range with many merged PRs can therefore add hundreds of serialized Azure DevOps requests and significantly delay or time out PR creation. Cache each detail's c.TreeId while constructing commits, and use that cache here; only fetch a tree for a parent outside the returned range.

Copilot AI balanced review requested due to automatic review settings September 30, 2026 14:29

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🔵 Needs a closer look

The provider-sensitive graph changes look sound, but the latest CI run is still pending.

Review effort: Balanced
Findings: None

@jasonmalinowski jasonmalinowski left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See if Copilot can do a simplification pass on this -- if it cleans up a lot of unnecessary tests that make the rest easier to follow.

Comment on lines +139 to +216
[Fact]
public async Task DetailFailurePreventsLaterBatches()
{
var commits = Enumerable.Range(1, 33).Reverse()
.Select(i => Commit($"commit-{i}", $"Update (#{i})", i == 1 ? "base" : $"commit-{i - 1}"))
.ToArray();
var releaseDetails = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var failure = new IOException("Commit details unavailable.");
using var client = new Client(commits, new Dictionary<string, string>())
{
BeforeCommitDetailsAsync = (id, token) => id == "commit-33"
? Task.FromException(failure)
: releaseDetails.Task.WaitAsync(token),
};

var getChanges = RoslynInsertionTool.GetChangesBetweenBuildsFromAzDOAsync(client, "project", "repo", RepoUrl, "base", "commit-33");
Assert.Equal(16, client.CommitDetailRequests.Count);
Assert.False(getChanges.IsCompleted);
releaseDetails.SetResult();
var actualFailure = await Assert.ThrowsAsync<IOException>(() => getChanges);

Assert.Same(failure, actualFailure);
Assert.Equal(commits.Take(16).Select(commit => commit.CommitId), client.CommitDetailRequests);
Assert.Empty(client.TreeLookups);
}

[Fact]
public async Task CancellationDuringDetailsPreventsLaterBatches()
{
var commits = Enumerable.Range(1, 33).Reverse()
.Select(i => Commit($"commit-{i}", $"Update (#{i})", i == 1 ? "base" : $"commit-{i - 1}"))
.ToArray();
using var cancellation = new CancellationTokenSource();
var releaseDetails = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
using var client = new Client(commits, new Dictionary<string, string>())
{
BeforeCommitDetailsAsync = (_, token) =>
{
Assert.Equal(cancellation.Token, token);
return releaseDetails.Task.WaitAsync(token);
},
};

var getChanges = RoslynInsertionTool.GetChangesBetweenBuildsFromAzDOAsync(client, "project", "repo", RepoUrl, "base", "commit-33", cancellation.Token);
Assert.Equal(16, client.CommitDetailRequests.Count);
cancellation.Cancel();
await Assert.ThrowsAnyAsync<OperationCanceledException>(() => getChanges);

Assert.Equal(commits.Take(16).Select(commit => commit.CommitId), client.CommitDetailRequests);
Assert.Empty(client.TreeLookups);
}

[Fact]
public async Task CancellationBetweenBatchesPreventsFurtherRequests()
{
var commits = Enumerable.Range(1, 33).Reverse()
.Select(i => Commit($"commit-{i}", $"Update (#{i})", i == 1 ? "base" : $"commit-{i - 1}"))
.ToArray();
using var cancellation = new CancellationTokenSource();
using var client = new Client(commits, new Dictionary<string, string>())
{
BeforeCommitDetailsAsync = (id, _) =>
{
if (id == "commit-18")
{
cancellation.Cancel();
}

return Task.CompletedTask;
},
};

await Assert.ThrowsAnyAsync<OperationCanceledException>(() =>
RoslynInsertionTool.GetChangesBetweenBuildsFromAzDOAsync(client, "project", "repo", RepoUrl, "base", "commit-33", cancellation.Token));

Assert.Equal(commits.Take(16).Select(commit => commit.CommitId), client.CommitDetailRequests);
Assert.Empty(client.TreeLookups);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we reduce the tests in this file to the core behavior changes? I've noticed this a lot with Copilot recently that it likes to write a lot of (really verbose) tests to assert that cancellation is respected and exceptions flow. But these tests aren't really useful, and also mandates all sorts of funky test hooks.

Comment on lines +65 to +109
[Fact]
public async Task FetchesAllCommitPagesBeforeFiltering()
{
var commits = Enumerable.Range(1, 1001).Reverse()
.Select(i => Commit($"commit-{i}", $"Update (#{i})", i == 1 ? "base" : $"commit-{i - 1}"))
.ToArray();
var releaseDetails = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously);
var pendingDetails = 0;
using var client = new Client(commits, new Dictionary<string, string>())
{
BeforeCommitDetailsAsync = async (_, token) =>
{
var pending = Interlocked.Increment(ref pendingDetails);
try
{
Assert.InRange(pending, 1, 16);
await releaseDetails.Task.WaitAsync(token);
await Task.Yield();
}
finally
{
Interlocked.Decrement(ref pendingDetails);
}
},
};

var getChanges = RoslynInsertionTool.GetChangesBetweenBuildsFromAzDOAsync(client, "project", "repo", RepoUrl, "base", "commit-1001");
try
{
Assert.Equal(16, client.CommitDetailRequests.Count);
Assert.Equal(16, Volatile.Read(ref pendingDetails));
}
finally
{
releaseDetails.SetResult();
}

var (changes, _) = await getChanges;

Assert.Equal(commits.Select(commit => commit.CommitId), changes.Select(commit => commit.CommitId));
Assert.Equal(commits.Select(commit => commit.CommitId), client.CommitDetailRequests);
Assert.Equal(0, pendingDetails);
Assert.Equal([0, 1000], client.PageOffsets);
Assert.Empty(client.TreeLookups);
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test seems to be baking in unecessary implemetation details.

Copilot AI balanced review requested due to automatic review settings October 1, 2026 11:42

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟢 Approval recommended

The implementation matches the snap topology and has comprehensive provider and graph regression coverage.

Review effort: Balanced
Findings: None

This branch has not been deployed

No deployments
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

Successfully merging this pull request may close these issues.

4 participants