Skip to content

Commit 4dd55c0

Browse files
authored
Properly reset PCS branches during codeflow (#4348)
1 parent c64a725 commit 4dd55c0

File tree

17 files changed

+162
-51
lines changed

17 files changed

+162
-51
lines changed

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/CloneManager.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ protected async Task<ILocalGitRepo> PrepareCloneInternalAsync(
5656
IReadOnlyCollection<string> remoteUris,
5757
IReadOnlyCollection<string> requestedRefs,
5858
string checkoutRef,
59-
CancellationToken cancellationToken)
59+
bool resetToRemote = false,
60+
CancellationToken cancellationToken = default)
6061
{
6162
if (remoteUris.Count == 0)
6263
{
@@ -104,6 +105,19 @@ protected async Task<ILocalGitRepo> PrepareCloneInternalAsync(
104105

105106
var repo = _localGitRepoFactory.Create(path);
106107
await repo.CheckoutAsync(checkoutRef);
108+
109+
if (resetToRemote)
110+
{
111+
// get the upstream branch for the currently checked out branch
112+
var result = await _localGitRepo.RunGitCommandAsync(path, ["for-each-ref", "--format=%(upstream:short)", $"refs/heads/{checkoutRef}"]);
113+
result.ThrowIfFailed("Couldn't get upstream branch for the current branch");
114+
var upstream = result.StandardOutput.Trim();
115+
116+
// reset the branch to the remote one
117+
result = await _localGitRepo.RunGitCommandAsync(path, ["reset", "--hard", upstream]);
118+
result.ThrowIfFailed($"Couldn't reset to remote ref {upstream}");
119+
}
120+
107121
return repo;
108122
}
109123

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/IVmrUpdater.cs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,5 +42,6 @@ Task<bool> UpdateRepository(
4242
bool reapplyVmrPatches,
4343
bool lookUpBuilds,
4444
CancellationToken cancellationToken,
45-
bool amendReapplyCommit = false);
45+
bool amendReapplyCommit = false,
46+
bool resetToRemoteWhenCloningRepo = false);
4647
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/PcsVmrBackFlower.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -110,6 +110,7 @@ await _vmrCloneManager.PrepareVmrAsync(
110110
[build.GetRepository()],
111111
[build.Commit],
112112
build.Commit,
113+
ShouldResetVmr,
113114
cancellationToken);
114115

115116
// Prepare repo
@@ -130,6 +131,7 @@ await _vmrCloneManager.PrepareVmrAsync(
130131
remotes,
131132
[subscription.TargetBranch, targetBranch],
132133
targetBranch,
134+
ShouldResetClones,
133135
cancellationToken);
134136
targetBranchExisted = true;
135137
}
@@ -140,11 +142,15 @@ await _vmrCloneManager.PrepareVmrAsync(
140142
mapping,
141143
remotes,
142144
subscription.TargetBranch,
145+
ShouldResetClones,
143146
cancellationToken);
144147
await targetRepo.CreateBranchAsync(targetBranch);
145148
targetBranchExisted = false;
146149
}
147150

148151
return (targetBranchExisted, mapping, targetRepo);
149152
}
153+
154+
// During backflow, we're targeting a specific repo branch, so we should make sure we reset local branch to the remote one
155+
protected bool ShouldResetClones => true;
150156
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/PcsVmrForwardFlower.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ public async Task<bool> FlowForwardAsync(
8585
mapping,
8686
remotes,
8787
build.Commit,
88+
ShouldResetClones,
8889
cancellationToken);
8990

9091
return await FlowForwardAsync(
@@ -98,4 +99,7 @@ public async Task<bool> FlowForwardAsync(
9899
discardPatches: true,
99100
cancellationToken);
100101
}
102+
103+
// During forward flow, we're targeting a specific remote VMR branch, so we should make sure our local branch is reset to it
104+
protected override bool ShouldResetVmr => true;
101105
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/RepositoryCloneManager.cs

Lines changed: 18 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,13 @@ public interface IRepositoryCloneManager
2323
/// </summary>
2424
/// <param name="repoUri">Remote to fetch from</param>
2525
/// <param name="checkoutRef">Ref to check out at the end</param>
26+
/// <param name="resetToRemote">Whether to reset the branch to the remote one</param>
2627
/// <returns>Path to the clone</returns>
2728
Task<ILocalGitRepo> PrepareCloneAsync(
2829
string repoUri,
2930
string checkoutRef,
30-
CancellationToken cancellationToken);
31+
bool resetToRemote = false,
32+
CancellationToken cancellationToken = default);
3133

3234
/// <summary>
3335
/// Clones a repo in a target directory, fetches from given remotes and checks out a given ref.
@@ -37,12 +39,14 @@ Task<ILocalGitRepo> PrepareCloneAsync(
3739
/// <param name="mapping">VMR repo mapping to associate the clone with</param>
3840
/// <param name="remotes">Remotes to fetch from</param>
3941
/// <param name="checkoutRef">Ref to check out at the end</param>
42+
/// <param name="resetToRemote">Whether to reset the branch to the remote one</param>
4043
/// <returns>Path to the clone</returns>
4144
Task<ILocalGitRepo> PrepareCloneAsync(
4245
SourceMapping mapping,
4346
IReadOnlyCollection<string> remotes,
4447
string checkoutRef,
45-
CancellationToken cancellationToken);
48+
bool resetToRemote = false,
49+
CancellationToken cancellationToken = default);
4650

4751
/// <summary>
4852
/// Prepares a clone of a repository by fetching from given remotes one-by-one until all requested commits are available.
@@ -52,13 +56,15 @@ Task<ILocalGitRepo> PrepareCloneAsync(
5256
/// <param name="remoteUris">Remotes to fetch one by one</param>
5357
/// <param name="requestedRefs">List of refs that need to be available</param>
5458
/// <param name="checkoutRef">Ref to check out at the end</param>
59+
/// <param name="resetToRemote">Whether to reset the branch to the remote one</param>
5560
/// <returns>Path to the clone</returns>
5661
Task<ILocalGitRepo> PrepareCloneAsync(
5762
SourceMapping mapping,
5863
IReadOnlyCollection<string> remoteUris,
5964
IReadOnlyCollection<string> requestedRefs,
6065
string checkoutRef,
61-
CancellationToken cancellationToken);
66+
bool resetToRemote = false,
67+
CancellationToken cancellationToken = default);
6268
}
6369

6470
/// <summary>
@@ -88,46 +94,34 @@ public async Task<ILocalGitRepo> PrepareCloneAsync(
8894
SourceMapping mapping,
8995
IReadOnlyCollection<string> remoteUris,
9096
string checkoutRef,
91-
CancellationToken cancellationToken)
97+
bool resetToRemote = false,
98+
CancellationToken cancellationToken = default)
9299
{
93100
if (remoteUris.Count == 0)
94101
{
95102
throw new ArgumentException("No remote URIs provided to clone");
96103
}
97104

98-
NativePath path = null!;
99-
bool cleanup = true;
100-
foreach (string remoteUri in remoteUris)
101-
{
102-
// Path should be returned the same for all invocations
103-
// We checkout a default ref
104-
path = await PrepareCloneInternal(remoteUri, mapping.Name, cleanup, cancellationToken);
105-
cleanup = false;
106-
}
107-
108-
var repo = _localGitRepoFactory.Create(path);
109-
await repo.CheckoutAsync(checkoutRef);
110-
return repo;
105+
return await PrepareCloneInternalAsync(mapping.Name, remoteUris, [checkoutRef], checkoutRef, resetToRemote, cancellationToken);
111106
}
112107

113108
public async Task<ILocalGitRepo> PrepareCloneAsync(
114109
string repoUri,
115110
string checkoutRef,
116-
CancellationToken cancellationToken)
111+
bool resetToRemote = false,
112+
CancellationToken cancellationToken = default)
117113
{
118114
// We store clones in directories named as a hash of the repo URI
119115
var cloneDir = StringUtils.GetXxHash64(repoUri);
120-
var path = await PrepareCloneInternal(repoUri, cloneDir, performCleanup: true, cancellationToken);
121-
var repo = _localGitRepoFactory.Create(path);
122-
await repo.CheckoutAsync(checkoutRef);
123-
return repo;
116+
return await PrepareCloneInternalAsync(cloneDir, [repoUri], [checkoutRef], checkoutRef, resetToRemote, cancellationToken);
124117
}
125118

126119
public Task<ILocalGitRepo> PrepareCloneAsync(
127120
SourceMapping mapping,
128121
IReadOnlyCollection<string> remoteUris,
129122
IReadOnlyCollection<string> requestedRefs,
130123
string checkoutRef,
131-
CancellationToken cancellationToken)
132-
=> PrepareCloneInternalAsync(mapping.Name, remoteUris, requestedRefs, checkoutRef, cancellationToken);
124+
bool resetToRemote = false,
125+
CancellationToken cancellationToken = default)
126+
=> PrepareCloneInternalAsync(mapping.Name, remoteUris, requestedRefs, checkoutRef, resetToRemote, cancellationToken);
133127
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/VmrBackflower.cs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -572,7 +572,7 @@ protected override IEnumerable<UnixPath> GetAllowedConflicts(IEnumerable<UnixPat
572572
string targetBranch,
573573
CancellationToken cancellationToken)
574574
{
575-
await _vmrCloneManager.PrepareVmrAsync([build.GetRepository()], [build.Commit], build.Commit, cancellationToken);
575+
await _vmrCloneManager.PrepareVmrAsync([build.GetRepository()], [build.Commit], build.Commit, ShouldResetVmr, cancellationToken);
576576

577577
SourceMapping mapping = _dependencyTracker.GetMapping(mappingName);
578578
ISourceComponent repoInfo = _sourceManifest.GetRepoVersion(mappingName);
@@ -595,6 +595,7 @@ await _repositoryCloneManager.PrepareCloneAsync(
595595
remotes,
596596
[baseBranch, targetBranch],
597597
targetBranch,
598+
ShouldResetVmr,
598599
cancellationToken);
599600
targetBranchExisted = true;
600601
}
@@ -731,4 +732,6 @@ private async Task<bool> UpdateDependenciesAndToolset(
731732

732733
protected override NativePath GetEngCommonPath(NativePath sourceRepo) => sourceRepo / VmrInfo.ArcadeRepoDir / Constants.CommonScriptFilesPath;
733734
protected override bool TargetRepoIsVmr() => false;
735+
// During backflow, we're flowing a specific VMR commit that the build was built from, so we should just check it out
736+
protected virtual bool ShouldResetVmr => false;
734737
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/VmrCloneManager.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,14 @@ public interface IVmrCloneManager
2121
/// <param name="remoteUris">Remotes to fetch from one by one</param>
2222
/// <param name="requestedRefs">List of refs that need to be available</param>
2323
/// <param name="checkoutRef">Ref to check out at the end</param>
24+
/// <param name="resetToRemote">Whether to reset to the remote ref after fetching</param>
2425
/// <returns>Path to the clone</returns>
2526
Task<ILocalGitRepo> PrepareVmrAsync(
2627
IReadOnlyCollection<string> remoteUris,
2728
IReadOnlyCollection<string> requestedRefs,
2829
string checkoutRef,
29-
CancellationToken cancellationToken);
30+
bool resetToRemote = false,
31+
CancellationToken cancellationToken = default);
3032
}
3133

3234
public class VmrCloneManager : CloneManager, IVmrCloneManager
@@ -54,7 +56,8 @@ public async Task<ILocalGitRepo> PrepareVmrAsync(
5456
IReadOnlyCollection<string> remoteUris,
5557
IReadOnlyCollection<string> requestedRefs,
5658
string checkoutRef,
57-
CancellationToken cancellationToken)
59+
bool resetToRemote = false,
60+
CancellationToken cancellationToken = default)
5861
{
5962
// This makes sure we keep different VMRs separate
6063
// We expect to have up to 3:
@@ -69,6 +72,7 @@ public async Task<ILocalGitRepo> PrepareVmrAsync(
6972
remoteUris,
7073
requestedRefs,
7174
checkoutRef,
75+
resetToRemote,
7276
cancellationToken);
7377

7478
_vmrInfo.VmrPath = vmr.Path;

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/VmrForwardFlower.cs

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,6 +217,7 @@ await _vmrCloneManager.PrepareVmrAsync(
217217
[vmrUri],
218218
[baseBranch, targetBranch],
219219
targetBranch,
220+
ShouldResetVmr,
220221
cancellationToken);
221222
branchExisted = true;
222223
}
@@ -228,6 +229,7 @@ await _vmrCloneManager.PrepareVmrAsync(
228229
[vmrUri],
229230
[baseBranch],
230231
baseBranch,
232+
ShouldResetVmr,
231233
cancellationToken);
232234

233235
await vmr.CheckoutAsync(baseBranch);
@@ -282,6 +284,7 @@ protected override async Task<bool> SameDirectionFlowAsync(
282284
reapplyVmrPatches: true,
283285
lookUpBuilds: true,
284286
amendReapplyCommit: true,
287+
resetToRemoteWhenCloningRepo: ShouldResetClones,
285288
cancellationToken: cancellationToken);
286289
}
287290
catch (PatchApplicationFailedException e)
@@ -302,7 +305,12 @@ protected override async Task<bool> SameDirectionFlowAsync(
302305
_vmrInfo.SourceManifestPath,
303306
line => line.Contains(lastFlow.SourceSha),
304307
lastFlow.TargetSha);
305-
var vmr = await _vmrCloneManager.PrepareVmrAsync([_vmrInfo.VmrUri], [previousFlowTargetSha], previousFlowTargetSha, cancellationToken);
308+
var vmr = await _vmrCloneManager.PrepareVmrAsync(
309+
[_vmrInfo.VmrUri],
310+
[previousFlowTargetSha],
311+
previousFlowTargetSha,
312+
ShouldResetVmr,
313+
cancellationToken);
306314
await vmr.CreateBranchAsync(targetBranch, overwriteExistingBranch: true);
307315

308316
// Reconstruct the previous flow's branch
@@ -338,6 +346,7 @@ await FlowCodeAsync(
338346
reapplyVmrPatches: true,
339347
lookUpBuilds: true,
340348
amendReapplyCommit: true,
349+
resetToRemoteWhenCloningRepo: ShouldResetClones,
341350
cancellationToken: cancellationToken);
342351
}
343352

@@ -414,6 +423,7 @@ .. submodules.Select(s => s.Path).Distinct().Select(VmrPatchHandler.GetExclusion
414423
reapplyVmrPatches: true,
415424
lookUpBuilds: true,
416425
amendReapplyCommit: true,
426+
resetToRemoteWhenCloningRepo: ShouldResetClones,
417427
cancellationToken: cancellationToken);
418428
}
419429

@@ -491,4 +501,8 @@ private async Task TryResolvingSourceManifestConflict(ILocalGitRepo vmr, string
491501

492502
protected override NativePath GetEngCommonPath(NativePath sourceRepo) => sourceRepo / Constants.CommonScriptFilesPath;
493503
protected override bool TargetRepoIsVmr() => true;
504+
// When flowing local repos, we should never reset branches to the remote ones, we might lose some changes devs wanted
505+
protected virtual bool ShouldResetVmr => false;
506+
// In forward flow, we're flowing a specific commit, so we should just check it out, no need to sync local branch to remote
507+
protected virtual bool ShouldResetClones => false;
494508
}

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/VmrInitializer.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,7 @@ private async Task InitializeRepository(
200200
remotes,
201201
new[] { update.TargetRevision },
202202
update.TargetRevision,
203+
resetToRemote: false,
203204
cancellationToken);
204205

205206
cancellationToken.ThrowIfCancellationRequested();

src/Microsoft.DotNet.Darc/DarcLib/VirtualMonoRepo/VmrPatchHandler.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -561,7 +561,7 @@ private async Task<List<VmrIngestionPatch>> GetPatchesForSubmoduleChange(
561561
CancellationToken cancellationToken)
562562
{
563563
var checkoutCommit = change.Before == Constants.EmptyGitObject ? change.After : change.Before;
564-
var clonePath = await _cloneManager.PrepareCloneAsync(change.Url, checkoutCommit, cancellationToken);
564+
var clonePath = await _cloneManager.PrepareCloneAsync(change.Url, checkoutCommit, resetToRemote: false, cancellationToken);
565565

566566
// We are only interested in filters specific to submodule's path
567567
ImmutableArray<string> GetSubmoduleFilters(IReadOnlyCollection<string> filters)

0 commit comments

Comments
 (0)