-
Notifications
You must be signed in to change notification settings - Fork 2
fix(server): prevent stale diff in practice review pipeline #981
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 4 commits
d1692a6
f5f8bbc
d44e55a
4eb41cb
108b8b9
923c86e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -93,6 +93,9 @@ public class PullRequestReviewHandler implements JobTypeHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final PracticeDetectionDeliveryService deliveryService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final FeedbackDeliveryService feedbackService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Nullable | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final de.tum.in.www1.hephaestus.gitprovider.common.gitlab.GitLabTokenService gitLabTokenService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PullRequestReviewHandler( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ObjectMapper objectMapper, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| GitRepositoryManager gitRepositoryManager, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -102,7 +105,10 @@ public class PullRequestReviewHandler implements JobTypeHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ContributorHistoryProvider contributorHistoryProvider, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PracticeDetectionResultParser resultParser, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| PracticeDetectionDeliveryService deliveryService, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FeedbackDeliveryService feedbackService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| FeedbackDeliveryService feedbackService, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @org.springframework.beans.factory.annotation.Autowired( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| required = false | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) @Nullable de.tum.in.www1.hephaestus.gitprovider.common.gitlab.GitLabTokenService gitLabTokenService | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.objectMapper = objectMapper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.gitRepositoryManager = gitRepositoryManager; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -113,6 +119,7 @@ public class PullRequestReviewHandler implements JobTypeHandler { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.resultParser = resultParser; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.deliveryService = deliveryService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.feedbackService = feedbackService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| this.gitLabTokenService = gitLabTokenService; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Override | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -167,8 +174,8 @@ public Map<String, byte[]> prepareInputFiles(AgentJob job) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<String, byte[]> files = new HashMap<>(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // PR review requires a pre-prepared local checkout. The sandbox never clones or fetches | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // repositories on demand; it only mounts an existing host-side checkout. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // PR review requires a local checkout (cloned by push webhook). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // The diff step will fetch to ensure refs are current before computing. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ensureRepositoryAvailable(repositoryId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Load PR entity once — shared by metadata, comments, and contributor history | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -180,7 +187,7 @@ public Map<String, byte[]> prepareInputFiles(AgentJob job) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Pre-compute diff: source branches may be deleted after merge, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // so we compute the diff from the merge commit graph using SHAs. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| computeAndStoreDiff(files, repositoryId, metadata); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| computeAndStoreDiff(files, repositoryId, metadata, job); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Build a structured per-file diff summary optimized for single-pass AI consumption. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // This is structural transformation (splitting on "diff --git" boundaries), not judgment. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -500,11 +507,10 @@ public void deliver(AgentJob job) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ------------------------------------------------------------------------- | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Require a pre-prepared local repository checkout for bind-mounting. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Require a local repository checkout for bind-mounting. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p>This review flow never clones or fetches repositories on demand. Repository preparation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * must happen ahead of time via the normal sync/bootstrap path so sandbox runs stay offline and | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * deterministic with respect to repo contents. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * <p>The repository must have been cloned by a prior push webhook. The diff computation | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * step ({@link #fetchBeforeDiff}) may fetch to update refs before computing the diff. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void ensureRepositoryAvailable(long repositoryId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!gitRepositoryManager.isEnabled()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -519,6 +525,63 @@ private void ensureRepositoryAvailable(long repositoryId) { | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Fetch latest refs from the remote before computing the diff. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Uses GitLabTokenService for GitLab workspaces to get an authenticated fetch. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * Falls back gracefully on failure — the diff computation will still attempt | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * with whatever refs are locally available. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void fetchBeforeDiff(long repositoryId, AgentJob job) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| var workspace = job.getWorkspace(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (workspace == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("No workspace on job, skipping pre-diff fetch: jobId={}", job.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String serverUrl = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String token = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long scopeId = workspace.getId(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (gitLabTokenService != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| serverUrl = gitLabTokenService.resolveServerUrl(scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| token = gitLabTokenService.getAccessToken(scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("GitLab token not available for fetch: scopeId={}, reason={}", scopeId, e.getMessage()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // For GitHub workspaces or when GitLab token is unavailable, the repo may already | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // be current (fetched by push webhook). The headSha validation in resolveDiffRange | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // will catch staleness even without a fetch here. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (serverUrl == null || token == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("No token available for pre-diff fetch, relying on existing clone: repoId={}", repositoryId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| JsonNode metadata = job.getMetadata(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String repoFullName = | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| metadata != null && metadata.has("repository_full_name") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? metadata.get("repository_full_name").asText() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (repoFullName == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("No repository_full_name in metadata, skipping pre-diff fetch"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String cloneUrl = serverUrl + "/" + repoFullName + ".git"; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| gitRepositoryManager.ensureRepository(repositoryId, cloneUrl, token); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.debug("Fetched latest refs before diff computation: repoId={}, scopeId={}", repositoryId, scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } catch (Exception e) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| log.warn( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| "Pre-diff fetch failed (will proceed with existing clone): repoId={}, error={}", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| repositoryId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| e.getMessage() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Build and store pull request metadata and review comments as context JSON files. */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void storeMetadataAndComments( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Map<String, byte[]> files, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -596,7 +659,7 @@ private void storeContributorHistory(Map<String, byte[]> files, PullRequest pull | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * between merge^1 (target before merge) and merge^2 (MR tip). Falls back to | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| * target_branch..head_sha if branch still exists. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void computeAndStoreDiff(Map<String, byte[]> files, long repositoryId, JsonNode metadata) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private void computeAndStoreDiff(Map<String, byte[]> files, long repositoryId, JsonNode metadata, AgentJob job) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String headSha = metadata.has("commit_sha") ? metadata.get("commit_sha").asText() : null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String targetBranch = requireText(metadata, "target_branch"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String sourceBranch = requireText(metadata, "source_branch"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -605,6 +668,12 @@ private void computeAndStoreDiff(Map<String, byte[]> files, long repositoryId, J | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Path repoPath = gitRepositoryManager.getRepositoryPath(repositoryId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Fetch latest refs from remote before computing the diff. | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Without this, the local clone may have stale branch refs from an earlier push, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // causing the diff to be computed against the wrong commit (see: stale-diff incident). | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| fetchBeforeDiff(repositoryId, job); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| try { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String[] range = resolveDiffRange(repoPath, targetBranch, sourceBranch, headSha); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (range == null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -622,11 +691,24 @@ private void computeAndStoreDiff(Map<String, byte[]> files, long repositoryId, J | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (diffStat != null) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| files.put(".context/diff_stat.txt", diffStat.getBytes(StandardCharsets.UTF_8)); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Log diff quality metrics for stale-diff incident detection | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int addedLines = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int removedLines = 0; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| for (String line : diff.split("\n", -1)) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (line.startsWith("+") && !line.startsWith("+++")) addedLines++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| else if (line.startsWith("-") && !line.startsWith("---")) removedLines++; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+699
to
+700
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (line.startsWith("+") && !line.startsWith("+++")) addedLines++; | |
| else if (line.startsWith("-") && !line.startsWith("---")) removedLines++; | |
| boolean isAddedFileHeader = line.startsWith("+++ ") || line.startsWith("+++\t"); | |
| boolean isRemovedFileHeader = line.startsWith("--- ") || line.startsWith("---\t"); | |
| if (line.startsWith("+") && !isAddedFileHeader) addedLines++; | |
| else if (line.startsWith("-") && !isRemovedFileHeader) removedLines++; |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
diff.split("\n", -1) builds a full array using regex splitting, which can add significant CPU/memory overhead for large diffs (this method already holds the entire diff string). Since this is only for metrics, consider iterating over the string with an index / BufferedReader(new StringReader(diff)) to count line prefixes without allocating an extra array.
| for (String line : diff.split("\n", -1)) { | |
| if (line.startsWith("+") && !line.startsWith("+++")) addedLines++; | |
| else if (line.startsWith("-") && !line.startsWith("---")) removedLines++; | |
| int lineStart = 0; | |
| while (lineStart <= diff.length()) { | |
| int lineEnd = diff.indexOf('\n', lineStart); | |
| if (lineEnd == -1) { | |
| lineEnd = diff.length(); | |
| } | |
| if (lineStart < lineEnd) { | |
| char firstChar = diff.charAt(lineStart); | |
| if (firstChar == '+') { | |
| if (lineEnd - lineStart < 3 || diff.charAt(lineStart + 1) != '+' || diff.charAt(lineStart + 2) != '+') { | |
| addedLines++; | |
| } | |
| } else if (firstChar == '-') { | |
| if (lineEnd - lineStart < 3 || diff.charAt(lineStart + 1) != '-' || diff.charAt(lineStart + 2) != '-') { | |
| removedLines++; | |
| } | |
| } | |
| } | |
| if (lineEnd == diff.length()) { | |
| break; | |
| } | |
| lineStart = lineEnd + 1; |
Copilot
AI
Apr 10, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
resolveDiffRange is called from multiple places (diff precompute, diff scope filtering, diff-hunk validation). Only computeAndStoreDiff now does a fetch, so other call sites can still operate on stale origin/* refs or even miss the headSha object if the checkout wasn't recently fetched (e.g., deliver runs in a different process or long after job preparation). Consider moving the ref update into a shared helper (or into resolveDiffRange itself) so all diff computations consistently work against up-to-date refs.
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -91,7 +91,8 @@ void setUp() { | |
| contributorHistoryProvider, | ||
| resultParser, | ||
| deliveryService, | ||
| feedbackService | ||
| feedbackService, | ||
| null | ||
|
Comment on lines
+94
to
+95
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add a test for the non-null Line 95 hardwires As per coding guidelines, "Focus on risk: cover critical flows and edge cases first when writing tests". 🤖 Prompt for AI Agents |
||
| ); | ||
| } | ||
|
|
||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
git fetch originis executed via the system git CLI, but the local clones are created/fetched via JGit with aCredentialsProvider(the token is not persisted into.git/config). For private repos, the CLI fetch is likely to fail due to missing credentials, which would leave refs stale and defeat the purpose of this change. Consider fetching throughGitRepositoryManager/JGit (or otherwise ensuring non-interactive credentials are available for the CLI fetch) and treat a fetch failure as a hard fallback to SHA-only strategies that don't rely on updated remote refs.