feat(worker): build/test the merge preview, not just the post-merge merge
preview_merge could only see file-overlap cleanliness via git merge-tree, never whether the result compiles -- the two costliest findings of the 2026-08-06 batch run were both merge-tree-clean but build-broken. When a list has a verify command configured, a clean preview is now additionally materialized (via a commit-tree + detached scratch worktree, outside the real repo, always cleaned up) and built/tested there, without ever touching the real working tree. preview_merge always attempts a verify run when a command is configured; preview_merge_set only does when its new runVerify parameter is set (default off), so a set preview never starts N builds unasked. The post-merge verify gate is unchanged.
This commit is contained in:
@@ -61,4 +61,43 @@ public class GitServicePreviewMergeTests : IDisposable
|
||||
Assert.Equal(headBefore, GitRepoFixture.RunGit(repo.RepoDir, "rev-parse", "HEAD").Trim());
|
||||
Assert.False(await git.IsMidMergeAsync(repo.RepoDir));
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task PreviewMergeAsync_Clean_ReturnsTreeOidMaterializableIntoDetachedWorktree()
|
||||
{
|
||||
if (!GitRepoFixture.IsGitAvailable()) return;
|
||||
var repo = NewRepo();
|
||||
var git = new GitService();
|
||||
var baseBranch = await git.GetCurrentBranchAsync(repo.RepoDir);
|
||||
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "checkout", "-b", "feature");
|
||||
File.WriteAllText(Path.Combine(repo.RepoDir, "newfile.txt"), "x\n");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "add", "-A");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "commit", "-m", "feat");
|
||||
GitRepoFixture.RunGit(repo.RepoDir, "checkout", baseBranch);
|
||||
|
||||
var preview = await git.PreviewMergeAsync(repo.RepoDir, baseBranch, "feature", CancellationToken.None);
|
||||
Assert.True(preview.Clean);
|
||||
Assert.False(string.IsNullOrWhiteSpace(preview.TreeOid));
|
||||
|
||||
// The tree a clean preview would produce can be wrapped in a commit and checked out into
|
||||
// a detached, branchless scratch worktree -- the technique the verify-in-preview feature
|
||||
// relies on -- without ever touching the real working tree.
|
||||
var parentSha = await git.RevParseAsync(repo.RepoDir, baseBranch, CancellationToken.None);
|
||||
var commitSha = await git.CommitTreeAsync(repo.RepoDir, preview.TreeOid!, parentSha, "scratch", CancellationToken.None);
|
||||
|
||||
var scratchPath = Path.Combine(Path.GetTempPath(), $"scratch_{Guid.NewGuid():N}");
|
||||
await git.WorktreeAddDetachedAsync(repo.RepoDir, scratchPath, commitSha, CancellationToken.None);
|
||||
try
|
||||
{
|
||||
Assert.True(File.Exists(Path.Combine(scratchPath, "newfile.txt")));
|
||||
}
|
||||
finally
|
||||
{
|
||||
await git.WorktreeRemoveAsync(repo.RepoDir, scratchPath, force: true, CancellationToken.None);
|
||||
}
|
||||
|
||||
Assert.False(Directory.Exists(scratchPath));
|
||||
Assert.False(await git.HasChangesAsync(repo.RepoDir, CancellationToken.None));
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user