Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/Darc.md
Original file line number Diff line number Diff line change
Expand Up @@ -3255,9 +3255,13 @@ darc vmr reset invalid-format
Diffs the VMR and the product repositories. Outputs the diff to stdout or saves it to a patch file (or multiple if patch > 1 GB), if --output-path is provided.
If input repos are not local, they'll be cloned locally and cleaned up afterwards, so the command might not be instant.

Use `--path` to limit the diff to specific paths within the repository. This option can be specified multiple times.

**Sample**
```
darc vmr diff C:\Path\VMR..https://github.com/dotnet/runtime:main
darc vmr diff C:\Path\VMR..https://github.com/dotnet/runtime:main --path src/libraries
darc vmr diff C:\Path\VMR..https://github.com/dotnet/runtime:main --path src/libraries --path src/coreclr
```

<!-- Begin Generated Content: Doc Feedback -->
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,10 @@ private async Task<int> FullVmrDiffAsync(Repo repo, Repo vmr)
(NativePath tmpRepo, NativePath tmpVmr, string mapping) = await PrepareReposAsync(repo, vmr, tmpPath);

IReadOnlyCollection<string> exclusionFilters = await GetDiffFilters(vmr.Remote, vmr.Ref, mapping);
return await GenerateDiff(tmpRepo, tmpVmr, vmr.Ref, exclusionFilters);
IReadOnlyCollection<string> allFilters = _options.Paths.Any()
? [.. _options.Paths.Select(p => p.Replace('\\', '/')), .. exclusionFilters]
: exclusionFilters;
return await GenerateDiff(tmpRepo, tmpVmr, vmr.Ref, allFilters);
Comment on lines 126 to +130
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

--path normalization differs between full diff and name-only modes. Here only backslashes are replaced, but leading '/', './', or '' aren’t trimmed like they are in FileTreeDiffAsync, which can make --path /src/foo behave differently depending on --name-only. Consider centralizing path normalization (trim leading separators/"./", replace '\' with '/', and ignore/throw on empty/whitespace entries) and using it in both modes before building allFilters.

Copilot uses AI. Check for mistakes.
}
finally
{
Expand Down Expand Up @@ -772,6 +775,16 @@ private async Task<int> FileTreeDiffAsync(Repo sourceRepo, Repo vmrRepo, bool fr
ProcessVmrOnlyFiles(filesOnlyInVmr, fileDifferences, directoriesToProcess, fromRepoDirection);
}

if (_options.Paths.Any())
{
var normalizedPaths = _options.Paths
.Select(p => "/" + p.TrimStart('/', '\\').Replace('\\', '/'))
.ToList();
fileDifferences = fileDifferences
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.

so we essentially do a full diff and filter it?
Prob doesn't really matter but in theory we could not search the files that are not on the provided paths too

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.

Right. Yeah, that would make it run much faster. @copilot can you make this change?

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.

rip

.Where(kv => normalizedPaths.Any(p => kv.Key.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
Comment on lines +783 to +784
Copy link

Copilot AI Apr 1, 2026

Choose a reason for hiding this comment

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

The name-only --path filter uses StartsWith, which can over-match sibling paths (e.g., --path src/lib would also include src/libraries). If the intention is to treat --path as a directory/file boundary (like git pathspecs), consider matching either exact path or prefix followed by '/' (and normalize any trailing slashes). Also consider whether OrdinalIgnoreCase is desired on case-sensitive platforms; using an OS-dependent comparison would better mirror git behavior.

Suggested change
fileDifferences = fileDifferences
.Where(kv => normalizedPaths.Any(p => kv.Key.StartsWith(p, StringComparison.OrdinalIgnoreCase)))
var pathComparison = OperatingSystem.IsWindows()
? StringComparison.OrdinalIgnoreCase
: StringComparison.Ordinal;
bool PathMatchesFilter(string filePath, string filterPath)
{
// Both filePath and filterPath are expected to be normalized to use '/' and start with '/'
if (filePath.Equals(filterPath, pathComparison))
{
return true;
}
if (!filterPath.EndsWith("/", StringComparison.Ordinal))
{
filterPath += "/";
}
return filePath.StartsWith(filterPath, pathComparison);
}
fileDifferences = fileDifferences
.Where(kv => normalizedPaths.Any(p => PathMatchesFilter(kv.Key, p)))

Copilot uses AI. Check for mistakes.
.ToDictionary(kv => kv.Key, kv => kv.Value);
}

foreach (var difference in fileDifferences.Values.OrderBy(v => v))
{
Console.WriteLine(difference);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

using System.Collections.Generic;
using CommandLine;
using Microsoft.DotNet.Darc.Operations.VirtualMonoRepo;

Expand Down Expand Up @@ -28,4 +29,9 @@ internal class VmrDiffOptions : VmrCommandLineOptions<VmrDiffOperation>
"Only list names of differing files and directories. " +
"Listed differences are prefixed with +, - or * for addition, removal or differing content.")]
public bool NameOnly { get; set; }

[Option("path", Required = false, HelpText =
"Paths to include in the diff. When specified, only differences in these paths will be shown. " +
"Can be specified multiple times to include multiple paths.")]
public IEnumerable<string> Paths { get; set; } = [];
}
Loading