From 571f0bf2a4589abbd8a70e2278a0f0e35ab75d68 Mon Sep 17 00:00:00 2001 From: amadzarak Date: Sun, 26 Apr 2026 13:14:06 -0400 Subject: [PATCH] OPC # 0006: OPC Git Trunk-Based management --- ControlPlane.Api/Services/PromotionService.cs | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/ControlPlane.Api/Services/PromotionService.cs b/ControlPlane.Api/Services/PromotionService.cs index aa00bbb..812acee 100644 --- a/ControlPlane.Api/Services/PromotionService.cs +++ b/ControlPlane.Api/Services/PromotionService.cs @@ -90,8 +90,10 @@ public class PromotionService(IConfiguration config, ILogger l { ct.ThrowIfCancellationRequested(); - var branchName = Ladder[i]; - var branch = repo.Branches[branchName]; + var branchName = Ladder[i]; + // Always read from the remote tracking ref so the status reflects what is on origin, + // not the server's potentially-stale local branch pointer. + var branch = repo.Branches[$"origin/{branchName}"]; if (branch?.Tip is null) { @@ -110,7 +112,7 @@ public class PromotionService(IConfiguration config, ILogger l if (i + 1 < Ladder.Length) { - var nextBranch = repo.Branches[Ladder[i + 1]]; + var nextBranch = repo.Branches[$"origin/{Ladder[i + 1]}"]; if (nextBranch?.Tip is not null) { var div = repo.ObjectDatabase.CalculateHistoryDivergence(tip, nextBranch.Tip); @@ -220,9 +222,12 @@ public class PromotionService(IConfiguration config, ILogger l var refSpecs = remote.FetchRefSpecs.Select(r => r.Specification).ToList(); repo.Network.Fetch(remote.Name, refSpecs, MakeFetchOptions()); - // 2. Resolve local branches - var fromBranch = repo.Branches[from] - ?? throw new InvalidOperationException($"Branch '{from}' not found."); + // 2. Resolve branches — always read from origin/ so we reflect what is actually on the remote, + // never the server's potentially-stale local branch pointers. + var fromBranch = repo.Branches[$"origin/{from}"] + ?? throw new InvalidOperationException($"Remote branch 'origin/{from}' not found."); + // `to` is read locally because we need to mutate its ref and push — it is immediately + // fast-forwarded to origin/{to} in the next step so it is never stale when used. var toBranch = repo.Branches[to] ?? throw new InvalidOperationException($"Branch '{to}' not found."); @@ -566,12 +571,13 @@ public class PromotionService(IConfiguration config, ILogger l var branchName = Ladder[i]; var srcName = i > 0 ? Ladder[i - 1] : null; // predecessor branch (e.g. develop for staging) - var branch = repo.Branches[branchName]; + // Always read from origin/ tracking refs — never local branch pointers. + var branch = repo.Branches[$"origin/{branchName}"]; // ── Branch missing ────────────────────────────────────────────── if (branch?.Tip is null) { - var srcTip = srcName is not null ? repo.Branches[srcName]?.Tip?.Sha : null; + var srcTip = srcName is not null ? repo.Branches[$"origin/{srcName}"]?.Tip?.Sha : null; checks.Add(new BranchConformanceCheck( branchName, srcName, ConformanceViolation.Missing, @@ -592,7 +598,7 @@ public class PromotionService(IConfiguration config, ILogger l continue; } - var srcBranch = repo.Branches[srcName]; + var srcBranch = repo.Branches[$"origin/{srcName}"]; if (srcBranch?.Tip is null) { // Source branch is itself missing — skip, it will be reported separately.