Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
a32adcf
darc vmr resolve
adamzip Oct 17, 2025
2a678f4
update swagger spec
adamzip Oct 21, 2025
5cfbba9
Update src/Microsoft.DotNet.Darc/Darc/Operations/VirtualMonoRepo/Reso…
adamzip Oct 21, 2025
626a213
Update src/Microsoft.DotNet.Darc/Darc/Operations/VirtualMonoRepo/Reso…
adamzip Oct 21, 2025
da18ecd
Update src/Microsoft.DotNet.Darc/Darc/Operations/VirtualMonoRepo/Reso…
adamzip Oct 21, 2025
9292f32
Update src/Microsoft.DotNet.Darc/Darc/Operations/VirtualMonoRepo/Reso…
adamzip Oct 21, 2025
8a75749
Apply suggestion from @adamzip
adamzip Oct 21, 2025
49c8ddd
remove unused method
adamzip Oct 21, 2025
27e28f9
Rename operation
adamzip Oct 21, 2025
d94f6f5
CR changes #1
adamzip Oct 22, 2025
eb3e958
CR changes #2
adamzip Oct 22, 2025
b85c531
CR changes 3
adamzip Oct 22, 2025
d8c59eb
remove source repo, improve sha validation
adamzip Oct 23, 2025
d91d0ce
rework exception messages
adamzip Oct 23, 2025
d5c9d10
remove changes to untouched git client file
adamzip Oct 23, 2025
2c79a81
override base SubscriptionId
adamzip Oct 23, 2025
b399554
Apply suggestions from code review
premun Oct 24, 2025
7f06d05
Register command in DI
premun Oct 24, 2025
05f2e27
Add PR operations to BAR API client
premun Oct 24, 2025
2c81aea
Fix VMR/repo cloning
premun Oct 24, 2025
75a3927
set correct vmr Uri in resolve-conflict FF
adamzip Oct 30, 2025
1605510
Fix indentation + simplify build query
adamzip Oct 31, 2025
469d23e
remove whitespace changes
adamzip Oct 31, 2025
90ea17a
Remove branch validation
adamzip Oct 31, 2025
7237026
Update src/Microsoft.DotNet.Darc/Darc/Operations/VirtualMonoRepo/Reso…
adamzip Nov 3, 2025
ba7a919
specify num of conflicted files
adamzip Nov 3, 2025
927e942
conflicted files fix
adamzip Nov 3, 2025
de6eba0
Commit changes in PCS while rebasing without conflicts
premun Nov 3, 2025
49dacaf
Integrate changes from rebase e2e tests
adamzip Nov 4, 2025
43e6774
fix merge errors
adamzip Nov 4, 2025
67d66a8
Revert "fix merge errors"
adamzip Nov 4, 2025
2242398
Revert "Integrate changes from rebase e2e tests"
adamzip Nov 4, 2025
a4c567b
merge changes from main
adamzip Nov 4, 2025
335a753
Use the new command in the rebase scenario tests
premun Nov 5, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,7 @@
using Microsoft.DotNet.Darc.Options.VirtualMonoRepo;
using Microsoft.DotNet.DarcLib;
using Microsoft.DotNet.DarcLib.Helpers;
using Microsoft.DotNet.DarcLib.Models.VirtualMonoRepo;
using Microsoft.DotNet.DarcLib.VirtualMonoRepo;
using Microsoft.DotNet.ProductConstructionService.Client.Models;
using Microsoft.Extensions.Logging;

#nullable enable
Expand All @@ -19,6 +17,7 @@ namespace Microsoft.DotNet.Darc.Operations.VirtualMonoRepo;
internal class BackflowOperation(
BackflowCommandLineOptions options,
IVmrInfo vmrInfo,
IVmrForwardFlower forwardFlower,
IVmrBackFlower backFlower,
IBackflowConflictResolver backflowConflictResolver,
IVmrCloneManager vmrCloneManager,
Expand All @@ -29,73 +28,40 @@ internal class BackflowOperation(
IProcessManager processManager,
IFileSystem fileSystem,
ILogger<BackflowOperation> logger)
: CodeFlowOperation(options, vmrInfo, vmrCloneManager, dependencyTracker, dependencyFileManager, localGitRepoFactory, barApiClient, fileSystem, logger)
: CodeFlowOperation(options, forwardFlower, backFlower, backflowConflictResolver, vmrInfo, vmrCloneManager, dependencyTracker, dependencyFileManager, localGitRepoFactory, barApiClient, fileSystem, logger)
{
private readonly BackflowCommandLineOptions _options = options;
private readonly IVmrInfo _vmrInfo = vmrInfo;
private readonly IVmrBackFlower _backFlower = backFlower;
private readonly IBackflowConflictResolver _backflowConflictResolver = backflowConflictResolver;
private readonly IProcessManager _processManager = processManager;
private readonly ILocalGitRepoFactory _localGitRepoFactory = localGitRepoFactory;

protected override async Task ExecuteInternalAsync(
string repoName,
string? targetDirectory,
IReadOnlyCollection<AdditionalRemote> additionalRemotes,
CancellationToken cancellationToken)
{

if (string.IsNullOrEmpty(targetDirectory))
{
throw new DarcException("Please specify path to a local repository to flow to");
}

_vmrInfo.VmrPath = new NativePath(_options.VmrPath ?? _processManager.FindGitRoot(Environment.CurrentDirectory));
var vmrPath = new NativePath(_options.VmrPath ?? _processManager.FindGitRoot(Environment.CurrentDirectory));
var targetRepoPath = new NativePath(_processManager.FindGitRoot(targetDirectory));

_vmrInfo.VmrPath = vmrPath;

var vmr = _localGitRepoFactory.Create(vmrPath);

var build = await GetOrCreateBuildAsync(vmr, _options.Build);

await FlowCodeLocallyAsync(
targetRepoPath,
isForwardFlow: false,
additionalRemotes,
build,
subscription: null,
cancellationToken);
}

protected override async Task<bool> FlowCodeAsync(
ILocalGitRepo productRepo,
Build build,
Codeflow currentFlow,
SourceMapping mapping,
string headBranch,
IReadOnlyList<string> excludedAssets,
CancellationToken cancellationToken)
{
LastFlows lastFlows = await _backFlower.GetLastFlowsAsync(
mapping.Name,
productRepo,
currentIsBackflow: true);

try
{
var result = await _backFlower.FlowBackAsync(
mapping.Name,
productRepo.Path,
build,
excludedAssets: excludedAssets,
headBranch,
headBranch,
enableRebase: true,
forceUpdate: true,
cancellationToken);

return result.HadUpdates;
}
finally
{
await _backflowConflictResolver.TryMergingBranchAndUpdateDependencies(
new CodeflowOptions(mapping, currentFlow, headBranch, headBranch, build, excludedAssets, true, false),
lastFlows,
productRepo,
headBranch,
headBranchExisted: true,
cancellationToken);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,9 @@ namespace Microsoft.DotNet.Darc.Operations.VirtualMonoRepo;

internal abstract class CodeFlowOperation(
ICodeFlowCommandLineOptions options,
IVmrForwardFlower forwardFlower,
IVmrBackFlower backFlower,
IBackflowConflictResolver backflowConflictResolver,
IVmrInfo vmrInfo,
IVmrCloneManager vmrCloneManager,
IVmrDependencyTracker dependencyTracker,
Expand All @@ -31,6 +34,9 @@ internal abstract class CodeFlowOperation(
: VmrOperationBase(options, logger)
{
private readonly ICodeFlowCommandLineOptions _options = options;
private readonly IVmrForwardFlower _forwardFlower = forwardFlower;
private readonly IVmrBackFlower _backFlower = backFlower;
private readonly IBackflowConflictResolver _backflowConflictResolver = backflowConflictResolver;
private readonly IVmrInfo _vmrInfo = vmrInfo;
private readonly IVmrCloneManager _vmrCloneManager = vmrCloneManager;
private readonly IVmrDependencyTracker _dependencyTracker = dependencyTracker;
Expand All @@ -44,6 +50,8 @@ protected async Task FlowCodeLocallyAsync(
NativePath repoPath,
bool isForwardFlow,
IReadOnlyCollection<AdditionalRemote> additionalRemotes,
Build build,
Subscription? subscription,
CancellationToken cancellationToken)
{
// If subscription ID is provided, fetch subscription metadata and populate options
Expand All @@ -57,7 +65,6 @@ protected async Task FlowCodeLocallyAsync(
ILocalGitRepo sourceRepo = isForwardFlow ? productRepo : vmr;
ILocalGitRepo targetRepo = isForwardFlow ? vmr : productRepo;

Build build = await GetBuildAsync(sourceRepo.Path);
string mappingName = await GetSourceMappingNameAsync(productRepo.Path);

await VerifyLocalRepositoriesAsync(productRepo);
Expand Down Expand Up @@ -94,22 +101,42 @@ protected async Task FlowCodeLocallyAsync(

try
{
bool hasChanges = await FlowCodeAsync(
productRepo,
build,
currentFlow,
mapping,
currentTargetRepoBranch,
excludedAssets,
cancellationToken);
bool hasChanges;
if (currentFlow is ForwardFlow)
{
hasChanges = await FlowForwardAsync(
productRepo,
build,
currentFlow,
mapping,
currentTargetRepoBranch,
excludedAssets,
subscription?.TargetRepository,
cancellationToken);
}
else
{
hasChanges = await FlowBackAsync(
productRepo,
build,
currentFlow,
mapping,
currentTargetRepoBranch,
excludedAssets,
cancellationToken);
}

if (!hasChanges)
{
_logger.LogInformation("No changes to flow between the VMR and {repo}.", mapping.Name);
return;
}

_logger.LogInformation("Changes staged in {repoPath}", targetRepo.Path);
if (!hasChanges)
{
_logger.LogInformation("No changes to flow between the VMR and {repo}.", mapping.Name);
return;
}

_logger.LogInformation("Changes staged in {repoPath}", targetRepo.Path);
}
}
finally
{
Expand Down Expand Up @@ -143,21 +170,10 @@ private async Task RestoreRepoToOriginalStateAsync(ILocalGitRepo repo, string or
}
}

protected abstract Task<bool> FlowCodeAsync(
ILocalGitRepo productRepo,
Build build,
Codeflow currentFlow,
SourceMapping mapping,
string headBranch,
IReadOnlyList<string> excludedAssets,
CancellationToken cancellationToken);

private async Task<Build> GetBuildAsync(NativePath sourceRepoPath)
protected async Task<Build> GetOrCreateBuildAsync(ILocalGitRepo sourceRepo, int buildId)
{
ILocalGitRepo sourceRepo = _localGitRepoFactory.Create(sourceRepoPath);

Build build;
if (_options.Build == 0)
if (buildId == 0)
{
_options.Ref = await sourceRepo.GetShaForRefAsync(_options.Ref);
build = new(-1, DateTimeOffset.Now, 0, false, false, _options.Ref, [], [], [], [])
Expand All @@ -167,7 +183,7 @@ private async Task<Build> GetBuildAsync(NativePath sourceRepoPath)
}
else
{
build = await _barApiClient.GetBuildAsync(_options.Build);
build = await _barApiClient.GetBuildAsync(buildId);

try
{
Expand All @@ -184,6 +200,92 @@ private async Task<Build> GetBuildAsync(NativePath sourceRepoPath)
return build;
}

protected async Task<bool> FlowForwardAsync(
ILocalGitRepo productRepo,
Build build,
Codeflow currentFlow,
SourceMapping mapping,
string headBranch,
IReadOnlyList<string> excludedAssets,
string? targetRepoUri,
CancellationToken cancellationToken)
{
if (targetRepoUri == null)
{
var vmr = _localGitRepoFactory.Create(_vmrInfo.VmrPath);
var remotes = await vmr.GetRemotesAsync();
targetRepoUri = remotes.First().Uri;
}

try
{
CodeFlowResult result = await _forwardFlower.FlowForwardAsync(
mapping.Name,
productRepo.Path,
build,
excludedAssets: excludedAssets,
headBranch,
headBranch,
targetRepoUri,
enableRebase: true,
forceUpdate: true,
cancellationToken);

return result.HadUpdates;
}
finally
{
// Update target branch's source manifest with the new commit
var vmr = _localGitRepoFactory.Create(_vmrInfo.VmrPath);
var sourceManifestContent = await vmr.GetFileFromGitAsync(VmrInfo.DefaultRelativeSourceManifestPath, headBranch);
var sourceManifest = SourceManifest.FromJson(sourceManifestContent!);
sourceManifest.UpdateVersion(mapping.Name, build.GetRepository(), build.Commit, build.Id);
_fileSystem.WriteToFile(_vmrInfo.SourceManifestPath, sourceManifest.ToJson());
await vmr.StageAsync([_vmrInfo.SourceManifestPath], cancellationToken);
}
}

protected async Task<bool> FlowBackAsync(
ILocalGitRepo productRepo,
Build build,
Codeflow currentFlow,
SourceMapping mapping,
string headBranch,
IReadOnlyList<string> excludedAssets,
CancellationToken cancellationToken)
{
LastFlows lastFlows = await _backFlower.GetLastFlowsAsync(
mapping.Name,
productRepo,
currentIsBackflow: true);

try
{
var result = await _backFlower.FlowBackAsync(
mapping.Name,
productRepo.Path,
build,
excludedAssets: excludedAssets,
headBranch,
headBranch,
enableRebase: true,
forceUpdate: true,
cancellationToken);

return result.HadUpdates;
}
finally
{
await _backflowConflictResolver.TryMergingBranchAndUpdateDependencies(
new CodeflowOptions(mapping, currentFlow, headBranch, headBranch, build, excludedAssets, true, false),
lastFlows,
productRepo,
headBranch,
headBranchExisted: true,
cancellationToken);
}
}

protected async Task VerifyLocalRepositoriesAsync(ILocalGitRepo repo)
{
var vmr = _localGitRepoFactory.Create(_vmrInfo.VmrPath);
Expand Down
Loading