Skip to content

fix(ci): resolve merge_commit_sha^1 as diff base for merged PRs - #2893

Closed
Erik Osterman (Cloud Posse) (osterman) wants to merge 1 commit into
mainfrom
osterman/fix-merged-pr-diff-base
Closed

fix(ci): resolve merge_commit_sha^1 as diff base for merged PRs#2893
Erik Osterman (Cloud Posse) (osterman) wants to merge 1 commit into
mainfrom
osterman/fix-merged-pr-diff-base

Conversation

@osterman

@osterman Erik Osterman (Cloud Posse) (osterman) commented Aug 6, 2026

Copy link
Copy Markdown
Member

what

  • resolvePRBase (pkg/ci/providers/github/base.go) now resolves merge_commit_sha^1 as a preferred first step for closed/merged pull_request events, before falling into the existing merge-base(HEAD, origin/<target>)HEAD~1event.pull_request.base.sha → ref fallback chain.
  • The existing merge-base tier now treats a result equal to the checked-out HEAD as a failed resolution for closed PRs (instead of accepting it as a valid base), as a secondary guard against the same failure class when merge_commit_sha is unavailable.
  • Any failure in the new step (field missing, fetch failure, no parent) falls through unchanged to the pre-existing fallback chain — no behavior change for open/synchronize PR events, push events, or merge_group events.
  • Added docs/fixes/2026-08-06-github-merged-pr-diff-base.md recording the fix per repo convention.

why

  • atmos describe affected --upload in GitHub Actions with ci.enabled: true auto-resolves the diff base for merged PRs. If the workflow's checkout isn't pinned to pull_request.head.sha, HEAD can end up on or past the target branch's post-merge tip, causing the existing merge-base tier to resolve a degenerate, self-referential base.
  • This has caused two confirmed customer incidents, including one with 510 false-positive "affected" components after a single merge, traced through months of smaller occurrences. In that incident, the resolved --base exactly equaled the PR's merge_commit_sha — the fingerprint of this exact degeneracy.
  • merge_commit_sha^1 (the merge commit's first parent) is derived from the merge commit GitHub itself created when the PR was merged, so it's correct regardless of what the workflow happened to check out.

references

  • N/A — reported directly, no tracking issue per this task's request.

Summary by CodeRabbit

  • Bug Fixes

    • Improved base commit detection for merged GitHub pull requests.
    • Correctly resolves the merge commit’s first parent when available.
    • Avoids using invalid merge-base results that match the current commit.
    • Preserves existing fallback behavior when merge information is unavailable.
    • Ensures merge-specific handling does not affect opened or synchronized pull requests.
  • Documentation

    • Added documentation describing the GitHub merged pull request base-resolution fix and validation results.

When a GitHub Actions checkout for a merged PR isn't pinned to
pull_request.head.sha, HEAD can end up on or past the target branch's
post-merge tip. resolvePRBase's merge-base tier could then resolve a
degenerate, self-referential base (confirmed in production: the resolved
--base exactly equaled merge_commit_sha), causing every commit that landed
on the target branch between the PR branch cut and the merge to be
misreported as "affected" -- up to 510 false positives in one incident.

Add a preferred first resolution step for closed/merged PRs that resolves
merge_commit_sha^1 directly from the merge commit GitHub created, which is
correct regardless of checkout state. Any failure falls through unchanged
to the existing 4-tier chain. Also harden the existing merge-base tier to
treat a result equal to the checked-out HEAD as a failed resolution for
closed PRs, instead of accepting it as a false success.
@atmos-pro

atmos-pro Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Tip

Atmos Pro  

No affected stacks workflow was detected for this pull request.
If this is expected, no action is needed.
Learn More. Ask AI.

@osterman Erik Osterman (Cloud Posse) (osterman) added the patch A minor, backward compatible change label Aug 6, 2026
@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

GitHub merged pull requests now resolve their base from the merge commit’s first parent, with fetch-and-fallback behavior. Closed-PR merge-base results equal to HEAD are skipped. Regression tests and fix documentation were added.

Changes

GitHub merged PR base resolution

Layer / File(s) Summary
Merged PR resolution implementation
pkg/ci/providers/github/base.go
The resolver checks merged pull requests first, resolves the merge commit’s first parent locally or after one fetch, rejects self-referential merge-base results, and preserves existing fallbacks.
Regression coverage
pkg/ci/providers/github/base_test.go
Git-based tests cover merge-parent resolution, missing merge_commit_sha, fallback behavior, and non-closed actions.
Fix documentation
docs/fixes/2026-08-06-github-merged-pr-diff-base.md
The fix record describes the defect, resolution logic, regression tests, and validation results.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: aknysh

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely identifies the main change: using the first parent of merge_commit_sha as the diff base for merged PRs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch osterman/fix-merged-pr-diff-base

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@pkg/ci/providers/github/base_test.go`:
- Around line 697-727: Replace the external-command implementations of runGit
and runGitOutput with Go-native repository fixture and assertion helpers, using
the existing repository utilities or go-git APIs available in the test package.
Preserve their current setup behavior, including deterministic author and
committer identity, and update callers as needed so merge-state tests no longer
depend on an installed Git binary.

In `@pkg/ci/providers/github/base.go`:
- Around line 240-252: Update resolveMergedPRTier so commitParentSHA(0) is not
used unconditionally for merge_commit_sha; distinguish actual merge commits from
squash and rebase commits, and use an appropriate merge-base or fallback
resolution for non-merge commits. Preserve complete PR change coverage for
squash and multi-commit rebase merges, and add tests covering both cases.
- Around line 603-610: Update fetchCommitSHA to ensure the fetched history
includes the merge commit’s first parent before returning, either by fetching
the parent explicitly or deepening the repository until commitParentSHA
succeeds. Preserve the existing error reporting, and add a regression case
covering a merge commit available only on origin.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 17eaf4d4-d42b-482e-a218-93c5d42a0221

📥 Commits

Reviewing files that changed from the base of the PR and between 3ce4349 and 659a463.

📒 Files selected for processing (3)
  • docs/fixes/2026-08-06-github-merged-pr-diff-base.md
  • pkg/ci/providers/github/base.go
  • pkg/ci/providers/github/base_test.go

Comment on lines +697 to +727
// runGit runs a git command in dir, failing the test on error.
func runGit(t *testing.T, dir string, args ...string) {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
cmd.Env = append(
os.Environ(),
"GIT_AUTHOR_NAME=Test",
"GIT_AUTHOR_EMAIL=test@test.com",
"GIT_COMMITTER_NAME=Test",
"GIT_COMMITTER_EMAIL=test@test.com",
)
output, err := cmd.CombinedOutput()
require.NoError(t, err, "git %v failed: %s", args, string(output))
}

// runGitOutput runs a git command in dir and returns trimmed stdout.
func runGitOutput(t *testing.T, dir string, args ...string) string {
t.Helper()
cmd := exec.Command("git", args...)
cmd.Dir = dir
cmd.Env = append(
os.Environ(),
"GIT_AUTHOR_NAME=Test",
"GIT_AUTHOR_EMAIL=test@test.com",
"GIT_COMMITTER_NAME=Test",
"GIT_COMMITTER_EMAIL=test@test.com",
)
out, err := cmd.Output()
require.NoError(t, err, "git %v failed", args)
return strings.TrimSpace(string(out))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== file outline =="
ast-grep outline pkg/ci/providers/github/base_test.go --view compact || true

echo "== relevant lines =="
sed -n '660,740p' pkg/ci/providers/github/base_test.go | cat -n

echo "== git command usages in test =="
rg -n "runGit|exec\.Command\(\"git\"|Command\(\"git\"|exec\.Command\\(" pkg/ci/providers/github/base_test.go || true

echo "== Go imports mentioning git/gitlab/go-git =="
sed -n '1,80p' pkg/ci/providers/github/base_test.go | cat -n

echo "== existing repository helper candidates =="
rg -n "go-git|initRepo|InitRepo|CreateGitRepo|git init|RunCommand|Command\\(" pkg internal || true

Repository: cloudposse/atmos

Length of output: 50372


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo git files/status =="
git status --short || true
echo "== go.mod go-git references =="
rg -n "github\\.com/src-d/go-git|github\\.com/go-git/go-git|gopkg.in/src-d/go-git" go.mod go.sum || true
echo "== module files =="
[ -f go.mod ] && sed -n '1,120p' go.mod

Repository: cloudposse/atmos

Length of output: 6069


Replace Git CLI test helpers with Go-native fixture helpers.

runGit and runGitOutput spawn external git commands for repository setup and assertions. Build the merge-state fixture with an existing Go-native helper or go-git, since tests should not depend on installed platform binaries.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/ci/providers/github/base_test.go` around lines 697 - 727, Replace the
external-command implementations of runGit and runGitOutput with Go-native
repository fixture and assertion helpers, using the existing repository
utilities or go-git APIs available in the test package. Preserve their current
setup behavior, including deterministic author and committer identity, and
update callers as needed so merge-state tests no longer depend on an installed
Git binary.

Source: Coding guidelines

Comment on lines +240 to +252
func resolveMergedPRTier(payload map[string]any, action, headSHA, targetBranch, eventName string) *provider.BaseResolution {
if action != "closed" || !isPRMerged(payload) {
return nil
}

mergeCommitSHA := extractMergeCommitSHA(payload)
if mergeCommitSHA == "" {
return nil
}

sha, err := resolveMergeCommitParent(mergeCommitSHA)
if err != nil {
log.Debug("merge_commit_sha^1 failed, trying fallbacks", "merge_commit_sha", mergeCommitSHA, "error", err)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
fd -a 'base\.go$' . | sed 's#^\./##' | head -50
echo

echo "== target lines =="
if [ -f pkg/ci/providers/github/base.go ]; then
  sed -n '200,320p' pkg/ci/providers/github/base.go | cat -n
fi
echo

echo "== relevant helpers =="
rg -n "resolveMergeCommitParent|commitParentSHA|fetchCommitSHA|resolveMergedPRTier|merge_commit_sha|first.*parent|Parent\\("pkg/ci/providers/github/base.go pkg/ci/providers/github pkg/ci -S

Repository: cloudposse/atmos

Length of output: 12133


🌐 Web query:

GitHub REST API pulls merge_commit_sha rebase merge no merge commit

💡 Result:

The GitHub REST API provides the merge_commit_sha attribute in the pull request resource, but its meaning and behavior depend on the state of the pull request and the method used to merge it [1][2]. Before a pull request is merged, merge_commit_sha represents the SHA of a temporary test merge commit created by GitHub to verify if the branch can be automatically merged without conflicts [1][3][2]. This test commit is not added to the base or head branches [4][2]. After a pull request is merged, the value of merge_commit_sha changes based on the merge method used [1][2]: 1. Merge Commit: The attribute holds the SHA of the actual merge commit created on the base branch [1][2]. 2. Squash and Merge: The attribute holds the SHA of the single squashed commit added to the base branch [1][2]. 3. Rebase and Merge: Since rebase and merge adds commits from the head branch onto the base branch individually—creating a linear history without a traditional merge commit—the merge_commit_sha attribute represents the SHA of the last commit that the base branch was updated to (i.e., the final commit from the head branch that was rebased onto the base) [1][2][5]. Note that while the field is sometimes referred to as merge_commit_sha, for rebased pull requests, it identifies the commit resulting from the rebase process on the base branch [1][2]. If you are experiencing unexpected null values for this field after a merge, it may indicate a temporary system issue or that the background metadata generation process has not yet completed [6].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== helper implementations =="
sed -n '540,625p' pkg/ci/providers/github/base.go | cat -n
echo

echo "== tier 0 docs/comments around merge commits =="
sed -n '80,135p' pkg/ci/providers/github/base.go | cat -n
echo

echo "== tests around resolved merge commit and fallback behavior =="
sed -n '720,910p' pkg/ci/providers/github/base_test.go | cat -n
echo

echo "== git shallow/fetch-related refs in github provider =="
rg -n "depth|fetch \+|fetch --|git fetch|fetchCommitSHA|commitParentSHA|shallow|single-commit" pkg/ci/providers/github/base.go pkg/ci/providers/github/base_test.go pkg/ci providers pkg -S

Repository: cloudposse/atmos

Length of output: 50373


🌐 Web query:

site:docs.github.com/rest/pulls/pulls merge_commit_sha rebase squash merged

💡 Result:

In GitHub’s Pull Request REST API, merge_commit_sha means:

  • Merge commit: SHA of the merge commit on the base branch.
  • Squash merge: SHA of the newly created squashed commit on the base branch.
  • Rebase merge: SHA of the last commit added to the base branch.
  • Unmerged/open PR: May be null; GitHub may still populate it with a temporary test merge commit. (docs.github.com)

Citations:


Do not resolve every merge_commit_sha with commitParentSHA(0).

For squash and rebase merges, merge_commit_sha is a regular commit added to the target branch, not a merge commit with the target tip as parent(0). A multi-PR-commit rebase leaves parent(0) as an earlier rebased commit, so tier 0 can omit later PR changes. Gate this path on actual merge commits, add coverage for squash/rebase cases, or use merge-base as base resolution for all merge types.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/ci/providers/github/base.go` around lines 240 - 252, Update
resolveMergedPRTier so commitParentSHA(0) is not used unconditionally for
merge_commit_sha; distinguish actual merge commits from squash and rebase
commits, and use an appropriate merge-base or fallback resolution for non-merge
commits. Preserve complete PR change coverage for squash and multi-commit rebase
merges, and add tests covering both cases.

Source: MCP tools

Comment on lines +603 to +610
func fetchCommitSHA(sha string) error {
defer perf.Track(nil, "github.fetchCommitSHA")()

cmd := exec.Command("git", "fetch", "origin", sha, "--no-tags", "--depth=1")
cmd.Dir = "."
output, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("fetching commit %s from origin: %w\n%s", sha, err, string(output))

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== locate file =="
git ls-files | rg '(^|/)pkg/ci/providers/github/base\.go$' || true

echo "== outline around file =="
wc -l pkg/ci/providers/github/base.go
ast-grep outline pkg/ci/providers/github/base.go --view compact | sed -n '1,260p'

echo "== relevant lines 560-640 =="
sed -n '560,640p' pkg/ci/providers/github/base.go | nl -ba -v560

echo "== usages of fetchCommitSHA and Parent =="
rg -n "fetchCommitSHA|\\.Parent\\(|Parents\\(" pkg/ci/providers/github/base.go pkg -g '*.go' | sed -n '1,120p'

Repository: cloudposse/atmos

Length of output: 417


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant lines 560-640 =="
sed -n '560,640p' pkg/ci/providers/github/base.go | nl -ba -v560

echo "== usages of fetchCommitSHA and Parent/Parents =="
rg -n "fetchCommitSHA|\\.Parent\\(|Parents\\(" pkg/ci/providers/github/base.go pkg -g '*.go' | sed -n '1,160p'

echo "== surrounding function call paths =="
rg -n "fetchCommitSHA|GetCommit|CommitSHA|GetBuild\\(" pkg/ci/providers/github/base.go -C 3 | sed -n '1,220p'

Repository: cloudposse/atmos

Length of output: 223


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== relevant lines 560-640 =="
sed -n '560,640p' pkg/ci/providers/github/base.go | awk '{printf "%6d: %s\n", NR+559, $0}'

echo "== usages of fetchCommitSHA and Parent/Parents =="
rg -n "fetchCommitSHA|\\.Parent\\(|Parents\\(" pkg/ci/providers/github/base.go pkg -g '*.go' || true

echo "== surrounding function call paths =="
rg -n "fetchCommitSHA|GetCommit|CommitSHA|GetBuild\\(" pkg/ci/providers/github/base.go -C 3 | sed -n '1,220p' || true

Repository: cloudposse/atmos

Length of output: 6258


🌐 Web query:

git fetch --depth=1 documentation fetches only <commit> and objects needed by it

💡 Result:

When you use git fetch --depth=1, it limits the amount of history downloaded from the remote repository. Specifically, it fetches only the tip of the remote branch(es) (the most recent commit) and the objects required to support that commit, excluding any older historical commits [1][2]. However, the behavior depends on the target provided: 1. Standard usage: When running git fetch --depth=1 origin, Git fetches the latest commit (and its necessary objects) for the remote branches [1][2]. 2. Fetching a specific commit: If you provide a specific hash (e.g., git fetch --depth=1 origin ), Git will attempt to fetch that specific commit [3][4]. This requires the remote repository to have the uploadpack.allowReachableSHA1InWant configuration set to true [3][4]. If enabled, this command fetches the specified commit and the objects reachable from it, effectively creating a shallow repository containing only that commit and its dependencies [3]. Key technical points: - Reachability: Git downloads all objects (trees and blobs) reachable from the specified tip or commit [3]. - Server-side requirements: Fetching an arbitrary commit hash that is not a branch tip typically requires explicit server-side permission (allowReachableSHA1InWant) [3][4]. - Limitations: The --depth option is designed to create or update shallow repositories [1][5]. Tags associated with the deepened commits are not fetched by default [1]. In summary, while git fetch --depth=1 is commonly used to fetch the latest commit of a branch, it can also be used to fetch a specific commit if the server permits it, limiting the download to that commit's history and its necessary tree/blob objects [3].

Citations:


🌐 Web query:

git fetch shallow depth-1 object hierarchy blob tree ignore commit parent parent sha git

💡 Result:

When you run git fetch with --depth=1, Git retrieves only the most recent commit at the tip of the specified branch [1][2]. Regarding the object hierarchy and data downloaded: 1. Commits: Only the commit at the tip (and its associated metadata) is fetched [2]. The connection to parent commits is severed; these are treated as shallow commits, and their parent information is ignored or effectively removed in your local repository [3][2][4]. 2. Blobs and Trees: Git downloads all the tree and blob objects necessary to construct the file system state for that specific, most recent commit [2]. Even though you are restricting the history (the commit chain), you still receive the full content (files and directories) corresponding to that commit [2]. 3. Ignoring Parent/SHA: The shallow boundary is maintained by a file at.git/shallow [3][4]. This file lists the commit SHA-1s where the history has been truncated, instructing Git to treat these as root commits—meaning they are perceived as having no parents, regardless of their actual history on the remote repository [3][4]. In summary, a depth-1 fetch prioritizes the current state of the files (trees and blobs) while truncating the historical commit graph to just the tip [2].

Citations:


🌐 Web query:

git fetch --depth=1 fetch arbitrary sha commit parent object file .git/shallow

💡 Result:

Attempting to use git fetch --depth=1 to retrieve an arbitrary commit SHA directly is generally unreliable because most Git servers disable the ability to request unadvertised objects by default [1][2][3]. For a git fetch command to successfully retrieve a specific SHA-1, the Git server must have the configuration uploadpack.allowReachableSHA1InWant=true enabled [1][3]. If this setting is not configured, the server will reject the request with an error such as fatal: Server does not allow request for unadvertised object [2]. When this feature is disabled (which is the default for most hosting providers and servers for security and performance reasons), git fetch will only permit fetching objects that are advertised by the server, such as branch tips or tags [4][5]. To work around this limitation in shallow repositories or when trying to fetch a specific commit, consider the following approaches: 1. Identify the Ref: If the commit belongs to a branch or tag, fetch that ref instead of the raw SHA [4]. 2. Increase Depth: If you are in a shallow repository, you may need to gradually increase the depth of your fetch (e.g., --depth=50, --depth=100) until the commit becomes reachable within the fetched history [1][2]. 3. Shallow Clone with Revision (Git 2.49+): If your environment uses a modern version of Git, the --revision option can be used with git clone to fetch history leading up to a specific revision [1]. The.git/shallow file is an internal Git mechanism that tracks which commits are considered root commits in a shallow repository [6]. It is managed automatically by Git commands like git fetch --depth=N and should not be edited manually [6].

Citations:


🌐 Web query:

Git's shallow fetch commit objects blobs trees not parent objects fetch-depth

💡 Result:

When you perform a shallow fetch or clone in Git using the --depth option, Git limits the history of the repository by restricting how many commit objects are transferred from the remote server [1][2]. In a shallow repository: 1. Commit Objects: Only the specified number of commits (measured from the tip of the branches) are transferred [2][3]. The commit that sits at the boundary of this depth is marked as a shallow commit [1][4][5]. 2. Shallow Boundary and Parent Objects: A shallow commit does not have its parent commits available in the local repository [1][4][5]. To handle this, Git maintains a file named.git/shallow, which lists the object names of these commits [1][4]. Git treats these commits as if they were root commits (i.e., they have no parents), effectively "grafting" the history to make traversal stop at these points [1][4][6]. 3. Blobs and Trees: Git downloads only the tree and blob objects necessary to reconstruct the states of the commits that are fetched [7][5]. If a commit is not fetched, its corresponding tree and blob objects are generally not downloaded either, as they are not needed to represent the partial history [5]. 4. Depth Calculation: The --depth parameter refers to the number of commits in a chain [3]. If a commit in that chain is a merge commit, it contributes to the depth count just like any other commit; however, because merge commits have multiple parents, a shallow fetch can technically result in more than --depth commits being present if those parents are reachable within the truncated history [3]. In summary, shallow operations reduce repository size by omitting historical objects (commits, trees, and blobs) that fall outside the specified depth, using the.git/shallow file to track and manage these boundaries [1][2][4].

Citations:


Fetch the merge commit parent before retrying.

git fetch --depth=1 stops at the requested commit SHA, so commit.Parent(0) can still fail for a locally absent merge commit. Fetch the first parent as well, or deepen until commitParentSHA() succeeds before returning. Add a regression case where the merge commit exists only on origin.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@pkg/ci/providers/github/base.go` around lines 603 - 610, Update
fetchCommitSHA to ensure the fetched history includes the merge commit’s first
parent before returning, either by fetching the parent explicitly or deepening
the repository until commitParentSHA succeeds. Preserve the existing error
reporting, and add a regression case covering a merge commit available only on
origin.

Source: MCP tools

@osterman

Copy link
Copy Markdown
Member Author

Closing — the justification for this was a customer incident (510 false-positive affected components) that I attributed to this exact resolvePRBase failure mode. I've since confirmed that incident's actual root cause was unrelated: the customer's own workflow passed an explicit --base flag that never touches this code path at all. The self-referential merge-base result this PR guards against is still a real theoretical gap, but I no longer have a confirmed case of it happening. Reopening later with real evidence if one turns up.

@atmos-pro

atmos-pro Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Note

Atmos Pro  

Waiting for your GitHub Actions workflow to upload affected stacks.
Learn More.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

patch A minor, backward compatible change

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant