Skip to content

fix(describe-affected): resolve PR base via merge-base with shallow-clone self-heal#2380

Merged
aknysh merged 12 commits intomainfrom
osterman/describe-affected-pr-ref
May 1, 2026
Merged

fix(describe-affected): resolve PR base via merge-base with shallow-clone self-heal#2380
aknysh merged 12 commits intomainfrom
osterman/describe-affected-pr-ref

Conversation

@osterman
Copy link
Copy Markdown
Member

@osterman osterman commented Apr 30, 2026

what

Fix atmos describe affected reporting many more affected components
than the PR actually modified, specifically when the PR is out of
date with the target branch
.

  • pkg/git/merge_base.go adds MergeBaseWithAutoFetch that runs a
    targeted git fetch origin <target> (and optionally one
    --deepen=200) when MergeBase can't resolve. Bounded retries.
  • pkg/ci/providers/github/base.go:resolvePRBase keeps merge-base as
    the primary strategy and drops the buggy last-resort path that
    returned refs/remotes/origin/<target> (which downstream resolved
    to the current tip of the target branch, producing the
    false-positives). New last-resort is event.pull_request.base.sha,
    which is frozen at the last PR sync and never points to the
    current tip.
  • ExecuteDescribeAffectedWithTargetRefCheckout accepts a new
    targetBranch parameter and self-heals via git fetch when
    worktree creation hits a missing target commit.
  • Adds pkg/git/fetch.go (FetchRef, DeepenFetch) lifted from
    PR fix: prefer GitHub PR payload base SHA #2285. New TargetBranch field on BaseResolution and
    DescribeAffectedCmdArgs.

why

A customer reported that atmos describe affected on an out-of-date
PR listed components the PR did not touch. The root cause was a
fallback path documented as "handles this gracefully" in the PRD
that, in practice, silently produced wrong results when the local
repo was a shallow checkout (the actions/checkout@v4 default).
Walkthrough and rationale are in
docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md.

The user's suggestion — using pull_request.merge_commit_sha as
the base — would also work and is documented as a considered
alternative in the fixes doc. We chose merge-base + auto-fetch
because it preserves the existing PRD architecture, doesn't require
fetching M's parent separately, and works naturally with
actions/checkout@v4's default merge-ref checkout.

supersedes #2285

PR #2285 proposed promoting pull_request.base.sha to the primary
strategy. This PR keeps merge-base as primary (gold standard) and
uses base.sha only as a fallback that replaces the buggy
ref-tip path. The fetch helpers and signature plumbing are lifted
from #2285; credit to the original work.

tests

  • pkg/git/merge_base_test.go: new
    TestMergeBaseWithAutoFetch_RecoversFromMissingRef builds an
    origin/clone pair, deletes origin/main to simulate a shallow
    CI checkout, and asserts the recovered SHA is the fork point —
    not the current main tip.
  • pkg/ci/providers/github/base_test.go:
    TestResolveBase_PullRequest_OutOfDate_FallsBackToPayloadSHA
    reproduces the customer scenario at unit-test level.
  • internal/exec/describe_affected_test.go:TestResolveBaseFromCI
    hardened to require describe.SHA is populated and
    describe.Ref empty — guards against any future regression that
    re-introduces the ref-tip fallback.

references

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Bug Fixes
    • More reliable PR base resolution in CI: auto-fetch + one-step deepen for shallow checkouts, targeted ref retry when refs are missing, and safer fallback to event payload base SHA to reduce false positives.
  • New Features
    • Merge-base recovery with targeted fetch/deepen, worktree retry on missing commits, and explicit propagation of PR target branch for CI resolutions.
  • Documentation
    • Updated CI/base-resolution docs and troubleshooting note for out-of-date PRs.
  • Tests
    • New and expanded unit/integration tests covering recovery, fetch/deepen, and fallback paths.

…lone self-heal; drop buggy origin-tip fallback

When `ci.enabled: true` and the PR was not up to date with the target
branch, `atmos describe affected` could report many more affected
components than the PR actually modified. Root cause: when
`MergeBase(HEAD, origin/<target>)` failed (shallow CI checkouts where
`origin/<target>` is not fetched), the GitHub provider fell through to
returning the *ref* `refs/remotes/origin/<target>`, which downstream
resolved to the *current tip* of the target branch. The resulting
tree-to-tree diff included every commit on `<target>` that the PR
hadn't pulled in.

Fix:

- `pkg/git/merge_base.go` adds `MergeBaseWithAutoFetch` that
  transparently runs `git fetch origin <target>` (and optionally one
  `--deepen=200` fetch) when `MergeBase` cannot resolve a fork point.
  Bounded — at most one fetch and one deepen per call.
- `pkg/ci/providers/github/base.go` uses `MergeBaseWithAutoFetch` and
  changes the post-fallback chain. The new last-resort is
  `event.pull_request.base.sha` (frozen at last PR sync, never the
  current tip). The legacy `refs/remotes/origin/<target>` ref path is
  kept only for hand-crafted payloads with no `base.sha`, with a
  `log.Warn` explaining it may include unrelated commits.
- `ExecuteDescribeAffectedWithTargetRefCheckout` accepts a new
  `targetBranch` parameter; on worktree creation failure it runs
  `git fetch origin <targetBranch>` and retries once. Threaded
  through `DescribeAffectedCmdArgs` and all callers.
- `BaseResolution` gains a `TargetBranch` field so providers can
  surface the target branch alongside the resolved base.
- Adds `pkg/git/fetch.go` (`FetchRef`, `DeepenFetch`) lifted from
  the open PR #2285 (which this PR supersedes — see the docs/fixes
  entry).

Tests:

- `pkg/git/merge_base_test.go` — new
  `TestMergeBaseWithAutoFetch_RecoversFromMissingRef` builds an
  origin/clone pair, deletes `origin/main` to simulate the shallow
  case, and asserts the recovered SHA is the fork point. Also
  covers `ErrHeadOnTargetBranch` propagation and the no-recovery
  path. Existing `MergeBase` tests migrated to `t.Chdir`.
- `pkg/ci/providers/github/base_test.go` — adds
  `TestResolveBase_PullRequest_OutOfDate_FallsBackToPayloadSHA`
  that reproduces the customer scenario at unit-test level and
  asserts we end up with a SHA, not the buggy origin-tip ref.
- `internal/exec/describe_affected_test.go:TestResolveBaseFromCI`
  hardened to require `describe.SHA` is populated and
  `describe.Ref` empty — guards against any future regression.

Docs:

- `docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md`
  documents the issue, root cause, fix, and the rejected
  `pull_request.merge_commit_sha` alternative the user suggested.
- PRD `docs/prd/native-ci/framework/base-resolution.md`: removed
  "handles this gracefully" language; updated resolution matrix.
- Blog post and command docs updated to reflect that merge-base
  is the actual primary strategy (the previous text predated
  PR #2241's merge-base implementation).

Refs: #2241, supersedes #2285.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@osterman osterman requested a review from a team as a code owner April 30, 2026 17:05
@atmos-pro
Copy link
Copy Markdown
Contributor

atmos-pro Bot commented Apr 30, 2026

Tip

Atmos Pro  

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

View pull request changes on Atmos Pro

@github-actions github-actions Bot added the size/m Medium size PR label Apr 30, 2026
@github-actions
Copy link
Copy Markdown

github-actions Bot commented Apr 30, 2026

Dependency Review

✅ No vulnerabilities or license issues found.

Scanned Files

None

@osterman osterman added the patch A minor, backward compatible change label Apr 30, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 30, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 79415773-2bec-4581-943e-ce85fe173fce

📥 Commits

Reviewing files that changed from the base of the PR and between 4f00ebe and 43c4eac.

📒 Files selected for processing (3)
  • errors/errors.go
  • pkg/git/worktree.go
  • pkg/git/worktree_test.go
✅ Files skipped from review due to trivial changes (1)
  • errors/errors.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/git/worktree_test.go

📝 Walkthrough

Walkthrough

Threads PR targetBranch through describe-affected, adds git FetchRef/DeepenFetch and MergeBaseWithAutoFetch to recover shallow/out-of-date PR checkouts, updates GitHub base-resolution order to prefer merge-base-with-auto-fetch then event payload base SHA, and updates callsites, tests, and docs.

Changes

Cohort / File(s) Summary
Documentation & Blog
docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md, docs/prd/native-ci/framework/base-resolution.md, website/blog/2026-03-21-describe-affected-auto-detection.mdx, website/docs/cli/commands/describe/describe-affected.mdx
Add docs describing MergeBaseWithAutoFetch, targeted fetch/deepen recovery, new fallback ordering including event.pull_request.base.sha, and targetBranch plumbing.
Git Utilities
pkg/git/fetch.go, pkg/git/fetch_test.go, pkg/git/merge_base.go, pkg/git/merge_base_test.go, pkg/git/worktree.go, pkg/git/worktree_test.go
Add FetchRef and DeepenFetch with branch validation and sentinel errors; refactor MergeBase to accept repoDir; add MergeBaseWithAutoFetch to auto-recover via fetch/deepen; add CreateWorktreeWithFetchRecovery; extensive tests.
GitHub Base Resolution
pkg/ci/providers/github/base.go, pkg/ci/providers/github/base_test.go, pkg/ci/internal/provider/types.go
PR base resolver now prefers MergeBaseWithAutoFetch, inserts event.pull_request.base.sha as a fallback before the final ref fallback (Warn), and populates TargetBranch on BaseResolution; tests updated/added.
Describe-Affected Pipeline
internal/exec/describe_affected.go, internal/exec/describe_affected_helpers.go, internal/exec/describe_affected_test.go, internal/exec/atlantis_generate_repo_config.go
Plumb TargetBranch through DescribeAffected args and callbacks; ExecuteDescribeAffectedWithTargetRefCheckout gains targetBranch param and uses worktree fetch-retry when target commit missing; tests/stubs updated.
Terraform Affected Integration
internal/exec/terraform_affected.go, internal/exec/terraform_affected_graph.go, internal/exec/terraform_affected_test.go, pkg/list/list_affected.go
Updated call sites and mocks to pass new targetBranch (and updated auth mock) into ExecuteDescribeAffectedWithTargetRefCheckout; behavior otherwise unchanged.
CLI/Tools Callsites
internal/exec/atlantis_generate_repo_config.go, pkg/list/list_affected.go
Supply new targetBranch argument (empty-string where unavailable).
Errors
errors/errors.go
Add exported sentinel errors ErrGitWorktreeAdd, ErrFetchOrigin, and ErrDeepenOrigin for worktree/fetch/deepen flows.
NOTICE
NOTICE
Replace several dependency license URLs with Unknown literal values.

Sequence Diagram(s)

sequenceDiagram
    participant GH as GitHub Provider
    participant MergeBase as MergeBaseWithAutoFetch
    participant Fetch as FetchRef/DeepenFetch
    participant Repo as Local Git Repo
    participant Exec as Describe-Affected Executor

    GH->>MergeBase: resolve merge-base(repoDir, targetBranch)
    MergeBase->>Repo: git merge-base HEAD refs/remotes/origin/<target>
    Repo-->>MergeBase: success / ErrReferenceNotFound / ErrNoCommonAncestor / ErrHeadOnTargetBranch
    alt ErrReferenceNotFound
        MergeBase->>Fetch: FetchRef(repoDir, targetBranch)
        Fetch->>Repo: git fetch origin refs/heads/<target>:refs/remotes/origin/<target> --no-tags
        Repo-->>Fetch: success / failure
        MergeBase->>Repo: retry git merge-base
    else ErrNoCommonAncestor
        MergeBase->>Fetch: DeepenFetch(repoDir, targetBranch, deepenStep)
        Fetch->>Repo: git fetch --deepen / --unshallow
        Repo-->>Fetch: success / failure
        MergeBase->>Repo: retry git merge-base
    end
    MergeBase-->>GH: return resolved SHA (or empty + ref)
    GH-->>Exec: BaseResolution{SHA, TargetBranch}
    Exec->>Exec: ExecuteDescribeAffectedWithTargetRefCheckout(..., targetBranch)
    Exec->>Repo: CreateWorktree(targetCommit)
    Repo-->>Exec: success / ErrGitRefNotFound
    alt ErrGitRefNotFound and targetBranch present
        Exec->>Fetch: FetchRef(repoDir, targetBranch) (one-shot)
        Fetch->>Repo: git fetch origin refs/heads/<target>
        Repo-->>Fetch: success / failure
        Exec->>Repo: retry create worktree -> success or joined error
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Suggested reviewers

  • aknysh
  • milldr
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 75.47% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main fix: resolving PR base via merge-base with shallow-clone recovery, which is the core change across all affected files.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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/describe-affected-pr-ref

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
Review rate limit: 6/8 reviews remaining, refill in 13 minutes and 32 seconds.

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

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 the current code and only fix it if needed.

Inline comments:
In `@docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md`:
- Around line 56-60: The fenced diagram block lacks a language hint; update the
opening fence from ``` to ```text so the block becomes ```text ... ``` (e.g.,
change the diagram block in the file to start with ```text and end with ```),
ensuring markdown tooling/linting recognizes it as plain text.

In `@docs/prd/native-ci/framework/base-resolution.md`:
- Around line 125-127: Update the two Validation bullets that still describe the
old fallback order so they match the matrix and new fallback chain: ensure they
list event.pull_request.base.sha (and MergeBaseWithAutoFetch use of base.sha) as
the primary source before any ref fallback, then the GITHUB_BASE_REF ref (and
any refs/remotes/origin/main fallback) afterward; adjust text to reference
MergeBaseWithAutoFetch(HEAD, origin/<target>), event.pull_request.base.sha, and
GITHUB_BASE_REF in that order and remove the previous
direct-GITHUB_BASE_REF-first wording.

In `@pkg/git/fetch.go`:
- Around line 49-50: Replace the ad-hoc formatted error returns in
pkg/git/fetch.go (the returns that currently use fmt.Errorf("fetching origin/%s:
%w\n%s", branch, err, string(output)) and the similar deepen-failure returns
around lines 84–85) so they wrap the original error with the project’s shared
static errors from errors/errors.go (for example the fetch-related and
deepen-related static errors) using fmt.Errorf and %w as the primary wrapped
error; keep the original err as additional context wrapped with %w and include
the command output as a string suffix in the message. Ensure you reference the
same static symbols from the errors package (e.g., the fetch/deepen error
constants) when wrapping and apply the same change to the other deepen/fetch
return paths in this file.
🪄 Autofix (Beta)

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

Run ID: b53bdf0e-ee8b-41d3-815c-159c7b3fd709

📥 Commits

Reviewing files that changed from the base of the PR and between 8965bc6 and fad6ec1.

📒 Files selected for processing (19)
  • docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md
  • docs/prd/native-ci/framework/base-resolution.md
  • internal/exec/atlantis_generate_repo_config.go
  • internal/exec/describe_affected.go
  • internal/exec/describe_affected_helpers.go
  • internal/exec/describe_affected_test.go
  • internal/exec/terraform_affected.go
  • internal/exec/terraform_affected_graph.go
  • internal/exec/terraform_affected_test.go
  • pkg/ci/internal/provider/types.go
  • pkg/ci/providers/github/base.go
  • pkg/ci/providers/github/base_test.go
  • pkg/git/fetch.go
  • pkg/git/fetch_test.go
  • pkg/git/merge_base.go
  • pkg/git/merge_base_test.go
  • pkg/list/list_affected.go
  • website/blog/2026-03-21-describe-affected-auto-detection.mdx
  • website/docs/cli/commands/describe/describe-affected.mdx

Comment thread docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md Outdated
Comment thread docs/prd/native-ci/framework/base-resolution.md
Comment thread pkg/git/fetch.go Outdated
osterman and others added 2 commits April 30, 2026 12:15
Container jobs in GitHub Actions run as a different user than the
checkout owner. Without `safe.directory` set, every git command
(including the auto-fetch we just added in MergeBaseWithAutoFetch
and the worktree-creation retry) fails with "fatal: detected dubious
ownership in repository at '/github/workspace'".

Call git.EnsureGitSafeDirectory() at the top of ResolveBase() so all
git operations the GitHub provider triggers (merge-base, fetch,
parent-commit lookup) succeed in container runners. The function is
a no-op outside GitHub Actions so it's safe to call unconditionally.

Also: hardened the two new fallback tests to use a non-existent
target branch name. They previously assumed merge-base would fail in
the test environment, which broke when running locally inside a real
clone of the atmos repo (the host repo *has* a `main` branch and
merge-base succeeded). Using `nonexistent-target-for-pr2380` makes
the assertions deterministic regardless of host state.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…led is true

EnsureGitSafeDirectory mutates the runner's global git config (adds
GITHUB_WORKSPACE to safe.directory). It should only run when the user
has explicitly opted into CI auto-detect via ci.enabled: true.

Move the call from pkg/ci/providers/github/base.go:ResolveBase()
(unconditional) to internal/exec/describe_affected.go:resolveBaseFromCI()
(only invoked when ci.enabled is true). The provider-layer doc-comment
notes that callers are responsible for safe-directory setup.

EnsureGitSafeDirectory itself remains a no-op outside GitHub Actions,
so the practical effect is: with this commit, Atmos only writes to
~/.gitconfig when both ci.enabled is true AND we're running inside
GitHub Actions.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@codecov
Copy link
Copy Markdown

codecov Bot commented Apr 30, 2026

Codecov Report

❌ Patch coverage is 85.23490% with 22 lines in your changes missing coverage. Please review.
✅ Project coverage is 77.99%. Comparing base (39fd0a4) to head (9fcfba3).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
pkg/ci/providers/github/base.go 77.77% 6 Missing and 2 partials ⚠️
pkg/git/merge_base.go 79.41% 5 Missing and 2 partials ⚠️
internal/exec/describe_affected.go 50.00% 1 Missing and 1 partial ⚠️
internal/exec/atlantis_generate_repo_config.go 0.00% 1 Missing ⚠️
internal/exec/describe_affected_helpers.go 0.00% 1 Missing ⚠️
internal/exec/terraform_affected_graph.go 0.00% 1 Missing ⚠️
pkg/git/fetch.go 97.43% 1 Missing ⚠️
pkg/list/list_affected.go 0.00% 1 Missing ⚠️
Additional details and impacted files

Impacted file tree graph

@@            Coverage Diff             @@
##             main    #2380      +/-   ##
==========================================
+ Coverage   77.95%   77.99%   +0.04%     
==========================================
  Files        1090     1091       +1     
  Lines      103078   103206     +128     
==========================================
+ Hits        80355    80499     +144     
+ Misses      18316    18298      -18     
- Partials     4407     4409       +2     
Flag Coverage Δ
unittests 77.99% <85.23%> (+0.04%) ⬆️

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
errors/errors.go 100.00% <ø> (ø)
internal/exec/terraform_affected.go 92.00% <100.00%> (+0.08%) ⬆️
pkg/git/worktree.go 100.00% <100.00%> (+6.06%) ⬆️
internal/exec/atlantis_generate_repo_config.go 60.60% <0.00%> (-0.19%) ⬇️
internal/exec/describe_affected_helpers.go 41.13% <0.00%> (ø)
internal/exec/terraform_affected_graph.go 50.69% <0.00%> (-0.36%) ⬇️
pkg/git/fetch.go 97.43% <97.43%> (ø)
pkg/list/list_affected.go 39.26% <0.00%> (-0.21%) ⬇️
internal/exec/describe_affected.go 78.64% <50.00%> (-0.42%) ⬇️
pkg/git/merge_base.go 73.68% <79.41%> (+21.68%) ⬆️
... and 1 more

... and 2 files with indirect coverage changes

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…c fallback chain

Address PR review feedback:
- Add ErrFetchOrigin / ErrDeepenOrigin sentinels in errors/errors.go.
- Wrap pkg/git/fetch.go FetchRef and DeepenFetch errors with the new
  sentinels (double %w preserves both sentinel and underlying err for
  errors.Is checks).
- Update TestFetchRef_NonexistentBranch to assert errors.Is(err,
  ErrFetchOrigin) instead of matching the old message string.
- Fix base-resolution PRD Validation bullets to match the documented
  fallback chain (MergeBaseWithAutoFetch -> base.sha -> ref-with-warn).
- Add `text` language hint to fenced diagram block in fixes doc to
  satisfy markdownlint MD040.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/git/fetch_test.go`:
- Around line 119-123: Add a test that triggers the actual git fetch failure
path so the DeepenFetch error wrapping (ErrDeepenOrigin) is exercised: call
DeepenFetch with a syntactically valid branch name (so it doesn't return
ErrInvalidBranchName) against a repo setup that causes a real fetch to fail
(e.g., a temp directory that is not a git remote or a repo with no origin), then
assert require.Error(t, err) and assert.ErrorIs(t, err, ErrDeepenOrigin);
reference DeepenFetch and ErrDeepenOrigin in the new test to ensure the
deepen-path sentinel is covered.
🪄 Autofix (Beta)

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

Run ID: de229022-fcfb-4cfb-87f6-2a495234a4a3

📥 Commits

Reviewing files that changed from the base of the PR and between 3498ea1 and 4f0ed08.

📒 Files selected for processing (5)
  • docs/fixes/2026-04-30-describe-affected-out-of-date-pr.md
  • docs/prd/native-ci/framework/base-resolution.md
  • errors/errors.go
  • pkg/git/fetch.go
  • pkg/git/fetch_test.go
✅ Files skipped from review due to trivial changes (1)
  • errors/errors.go
🚧 Files skipped from review as they are similar to previous changes (1)
  • pkg/git/fetch.go

Comment thread pkg/git/fetch_test.go
Mirrors TestFetchRef_NonexistentBranch: clones a temp origin and asks
DeepenFetch to fetch a branch that does not exist there, asserting the
error chain wraps errUtils.ErrDeepenOrigin so a regression in the
sentinel wrapping cannot slip through.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
coderabbitai[bot]
coderabbitai Bot previously approved these changes Apr 30, 2026
Bring pkg/git coverage from 75.x% to 80.3% by exercising two previously
untested paths:

- TestDeepenFetch_Unshallow: shallow-clones a multi-commit origin (depth=1),
  then calls DeepenFetch(repo, branch, 0) to take the depth<=0 --unshallow
  branch and asserts the .git/shallow marker is removed afterwards. Brings
  DeepenFetch from 80% -> 100% line coverage.

- TestMergeBaseWithAutoFetch_DeepenPathExhausted: uses an orphan branch
  to deterministically produce ErrNoCommonAncestor (rather than the
  shallow-clone "object not found" path, which the auto-fetch logic does
  not classify as deepen-recoverable). Exercises the deepen branch end
  to end and asserts the function propagates the error when deepen
  cannot recover. Brings MergeBaseWithAutoFetch from 53.6% -> 71.4%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/git/fetch_test.go`:
- Around line 160-162: Replace the string-concatenated file URI with a properly
built file:// URL using net/url and filepath to be Windows-safe: create a
url.URL with Scheme "file" and Path set to the OS-normalized absolute originDir
(use filepath.ToSlash or ensure a leading slash on Windows), call u.String() and
pass that to runGit instead of "file://"+originDir; update imports (net/url,
path/filepath and runtime/strings if needed) and modify the test in
fetch_test.go where clone is invoked (the cloneDir / runGit call).

In `@pkg/git/merge_base_test.go`:
- Around line 173-179: Update the test to assert the specific propagated error
instead of any error: when calling MergeBaseWithAutoFetch (the symbol to
modify), replace the generic require.Error check with an assertion that uses
errors.Is(err, ErrNoCommonAncestor) (or equivalent require.True with errors.Is)
so the test fails if a different error is returned; reference the
ErrNoCommonAncestor sentinel and ensure the errors package is imported in
pkg/git/merge_base_test.go.
- Around line 197-199: Update the test to assert the specific original
ref-missing error instead of only checking for any error: in the test that calls
MergeBaseWithAutoFetch(cloneDir, "release") replace the generic assert.Error(t,
err) with assert.ErrorIs(t, err, plumbing.ErrReferenceNotFound) (or use
errors.Is(err, plumbing.ErrReferenceNotFound) if not using testify) so the test
guarantees the fallback returns the original MergeBase failure when the branch
is absent; keep the call to MergeBaseWithAutoFetch and the setup unchanged.
- Around line 107-115: The test shows MergeBaseWithAutoFetch(cloneDir, "main")
still calls MergeBase("main") which resolves the repo from '.' and misses the
intended repoDir; update MergeBaseWithAutoFetch to call into a repo-aware
variant of MergeBase (e.g., pass repoDir through) or modify MergeBase to accept
and use an explicit repoDir parameter so both the initial call and the retry
after fetch operate on the same repo; ensure references to MergeBase and
MergeBaseWithAutoFetch in merge_base.go use the repoDir value rather than
relying on working directory.
🪄 Autofix (Beta)

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

Run ID: fe290f74-414f-4811-b220-b1cb9aa720bc

📥 Commits

Reviewing files that changed from the base of the PR and between b7859b6 and 6ee872c.

📒 Files selected for processing (2)
  • pkg/git/fetch_test.go
  • pkg/git/merge_base_test.go

Comment thread pkg/git/fetch_test.go Outdated
Comment thread pkg/git/merge_base_test.go Outdated
Comment thread pkg/git/merge_base_test.go
Comment thread pkg/git/merge_base_test.go Outdated
…s file URI

Address PR review feedback:

- MergeBase now takes (repoDir, targetBranch) and opens the repo at
  repoDir directly via go-git PlainOpenWithOptions, instead of relying
  on the cwd-scoped GetLocalRepo() helper. MergeBaseWithAutoFetch
  threads its repoDir argument into all three MergeBase calls so the
  initial computation, post-fetch retry, and post-deepen retry all
  operate on the same repository. Previously MergeBaseWithAutoFetch's
  repoDir parameter was used only for FetchRef/DeepenFetch, leaving
  the merge-base steps silently dependent on cwd.

- Tests no longer t.Chdir into the test repo before exercising
  MergeBase / MergeBaseWithAutoFetch — the explicit repoDir argument
  is sufficient and removing chdir actually verifies the new contract.

- Tighten error assertions:
  * TestMergeBase_ErrorWhenTargetRefMissing: errors.Is plumbing.ErrReferenceNotFound
  * TestMergeBaseWithAutoFetch_DeepenPathExhausted: errors.Is ErrNoCommonAncestor
  * TestMergeBaseWithAutoFetch_ReturnsErrorWhenFetchImpossible: errors.Is plumbing.ErrReferenceNotFound

- Windows-safe file URI in TestDeepenFetch_Unshallow — build with
  net/url + filepath.ToSlash instead of "file://"+originDir, which
  is malformed on drive-letter paths.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
…h tests

The shallow-clone self-heal logic in
ExecuteDescribeAffectedWithTargetRefCheckout (8 lines) was hard to
unit-test because it lived inside a 350-line orchestrator that the
existing tests stub via gomonkey. Hoist it into a small package-level
helper in pkg/git so it can be exercised end-to-end with real git
operations.

- New CreateWorktreeWithFetchRecovery(repoDir, targetCommit,
  targetBranch) in pkg/git/worktree.go: tries CreateWorktree, and if
  that fails AND a non-empty targetBranch is supplied, performs a
  one-shot FetchRef(repoDir, targetBranch) and retries. On final
  failure, joins the original CreateWorktree error with the fetch
  error so callers retain hints about both failures.

- describe_affected_helpers.go now calls this helper directly,
  shrinking the inline self-heal block from 8 lines to 1.

- Three tests cover the three branches at 100% line coverage:
  * SuccessNoFetchNeeded — first CreateWorktree succeeds; fetch
    must NOT run even when targetBranch is non-empty.
  * SuccessAfterFetch — clone tracks an older origin/main; a new
    commit lands on origin after the clone; missing SHA becomes
    available after FetchRef and the retry succeeds. (This is the
    customer-reported PR base.sha scenario.)
  * FailsWhenFetchFails — bogus SHA + non-existent target branch;
    asserts the joined error chain is reachable via errors.Is on
    ErrFetchOrigin.

Net effect: pkg/git coverage 80.3% -> 81.1%, and the
describe_affected_helpers self-heal lines that were 0% covered are
gone (replaced with one delegation line).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
pkg/git/merge_base_test.go (1)

131-170: ⚡ Quick win

Exercise the real shallow-boundary deepen case too.

This only proves the ErrNoCommonAncestor branch on unrelated histories. The production path you’re fixing is a shallow clone with shared ancestry, where the fork point exists on origin but not in the local object graph yet. Please add a --depth=1 scenario that forces MergeBaseWithAutoFetch to deepen and then return the fork-point SHA; otherwise a shallow-history regression can still slip through here.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@pkg/git/merge_base_test.go` around lines 131 - 170, The test only covers
unrelated histories; add a shallow-depth scenario to exercise the real deepen
path: in TestMergeBaseWithAutoFetch_DeepenPathExhausted (or a small new test),
create an origin with a shared fork point (make a commit on main, create a
branch, add further commits), then create a local repo that is a shallow clone
of origin using "--depth=1" so the fork point is missing locally, call
MergeBaseWithAutoFetch(dir, "main"), and assert it returns the fork-point SHA
(not ErrNoCommonAncestor); keep existing unrelated-history assertions but add
this shallow-case setup and the assertion that MergeBaseWithAutoFetch
successfully deepens and returns the expected commit SHA.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/git/fetch_test.go`:
- Around line 115-118: The tests that call FetchRef (and the other failing path
at 141-144) currently only assert the high-level sentinel
errUtils.ErrFetchOrigin; add a check that the underlying error preserves the
shell-command exit-code contract by using errors.As or assert.ErrorAs to confirm
the wrapped error is an errUtils.ExitCodeError (i.e., assert that errors.As(err,
new(errUtils.ExitCodeError)) or assert.ErrorAs(t, err,
&errUtils.ExitCodeError{}) for the variable err returned by FetchRef and the
other failing call).

In `@pkg/git/worktree.go`:
- Around line 75-83: The code currently retries via FetchRef for any
CreateWorktree error when targetBranch is set; change this to only attempt the
fetch when CreateWorktree failed due to a missing/ref-not-found error. In the
CreateWorktree call handling in worktree.go, replace the broad check (err == nil
|| targetBranch == "") with a guard that (1) leaves early on success, (2)
returns immediately for empty targetBranch, and (3) inspects err to detect a
ref-missing condition (e.g., match the git error string or use/add a helper like
isRefMissing(err)) and only then call FetchRef(repoDir, targetBranch); on fetch
failure still return errors.Join(err, fetchErr). Ensure you reference
CreateWorktree, FetchRef, and the error join path so only missing-ref errors
trigger the auto-fetch retry.

---

Nitpick comments:
In `@pkg/git/merge_base_test.go`:
- Around line 131-170: The test only covers unrelated histories; add a
shallow-depth scenario to exercise the real deepen path: in
TestMergeBaseWithAutoFetch_DeepenPathExhausted (or a small new test), create an
origin with a shared fork point (make a commit on main, create a branch, add
further commits), then create a local repo that is a shallow clone of origin
using "--depth=1" so the fork point is missing locally, call
MergeBaseWithAutoFetch(dir, "main"), and assert it returns the fork-point SHA
(not ErrNoCommonAncestor); keep existing unrelated-history assertions but add
this shallow-case setup and the assertion that MergeBaseWithAutoFetch
successfully deepens and returns the expected commit SHA.
🪄 Autofix (Beta)

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

Run ID: 073f4bb3-a906-417b-9dc0-cd1cccda5b9a

📥 Commits

Reviewing files that changed from the base of the PR and between b7859b6 and caf119e.

📒 Files selected for processing (6)
  • internal/exec/describe_affected_helpers.go
  • pkg/git/fetch_test.go
  • pkg/git/merge_base.go
  • pkg/git/merge_base_test.go
  • pkg/git/worktree.go
  • pkg/git/worktree_test.go

Comment thread pkg/git/fetch_test.go
Comment thread pkg/git/worktree.go
…racts in tests

Address PR review feedback:

- CreateWorktreeWithFetchRecovery now gates the fetch/retry path on
  errors.Is(err, errUtils.ErrGitRefNotFound). Previously *any*
  CreateWorktree failure with a non-empty targetBranch would log
  "Target commit not available locally, fetching base branch" and
  attempt a fetch — including unrelated infrastructure failures
  (temp-dir creation, permission denied, repo state corruption). Those
  errors now propagate directly.

- Negative-path test TestCreateWorktreeWithFetchRecovery_GateSkipsNonRefNotFoundError
  forces os.MkdirTemp to fail (TMPDIR/TEMP/TMP point at a nonexistent
  path) and asserts the gate skips the recovery path: neither
  ErrFetchOrigin nor ErrGitRefNotFound appear in the returned chain.
  Side-benefit: this test exercises the MkdirTemp failure branch in
  CreateWorktree itself, bringing it from 92.3% to 100%.

- TestFetchRef_NonexistentBranch and TestDeepenFetch_NonexistentBranch
  now also assert errors.As(&exec.ExitError{}) and a non-zero exit
  code, pinning the contract that the wrapped error chain preserves
  the underlying shell exit-status info. (FetchRef/DeepenFetch use
  raw exec.Command rather than the workflow ShellRunner that produces
  errUtils.ExitCodeError, so *exec.ExitError is the equivalent
  contract here.)

Coverage: pkg/git 81.1% -> 81.6%; CreateWorktree 92.3% -> 100%;
CreateWorktreeWithFetchRecovery stays at 100%.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@pkg/git/worktree_test.go`:
- Around line 385-388: The test currently asserts the joined error chain
contains errUtils.ErrFetchOrigin but omits the original create-worktree error;
update the test in worktree_test.go (the fetch-failure recovery case where
errors.Join(originalCreateWorktreeErr, fetchErr) is returned) to also assert the
original error is preserved by adding an assertion like assert.ErrorIs(t, err,
ErrGitRefNotFound) (use the same ErrGitRefNotFound symbol used where
originalCreateWorktreeErr is defined/imported) so both ErrFetchOrigin and
ErrGitRefNotFound are verified in the joined error chain.

In `@pkg/git/worktree.go`:
- Around line 82-83: CreateWorktree currently marks every git worktree add
failure as errUtils.ErrGitRefNotFound, causing non-missing-ref errors to trigger
recovery; change CreateWorktree so it parses the git stderr and only returns
errUtils.ErrGitRefNotFound when the error text matches a genuine missing-ref
pattern (e.g., "fatal: reference .* not found" or similar), and for other
failures return a distinct sentinel error (create e.g.
errUtils.ErrWorktreeAddFailed or ErrGitWorktreeAddFailed) or wrap the original
error with that sentinel; then update the caller check in
CreateWorktree/Worktree logic (the code that currently does errors.Is(err,
errUtils.ErrGitRefNotFound)) to only treat the missing-ref sentinel as retryable
and not the generic worktree-add sentinel, ensuring other failures skip
recovery.
🪄 Autofix (Beta)

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

Run ID: f6fd5e21-9bad-43b9-adbd-e1d2acf86144

📥 Commits

Reviewing files that changed from the base of the PR and between caf119e and ce9720a.

📒 Files selected for processing (3)
  • pkg/git/fetch_test.go
  • pkg/git/worktree.go
  • pkg/git/worktree_test.go

Comment thread pkg/git/worktree_test.go
Comment thread pkg/git/worktree.go
Previously CreateWorktree blanket-tagged every git worktree add failure
as ErrGitRefNotFound, causing CreateWorktreeWithFetchRecovery's gate to
fire its fetch retry on unrelated failures (path conflicts, repo state
corruption, permissions) and to surface a misleading "make sure the ref
is correct" hint for non-ref problems.

Add ErrGitWorktreeAdd sentinel for generic worktree-add failures and
parse git stderr to reserve ErrGitRefNotFound for true missing-ref
cases. The recovery gate stays on ErrGitRefNotFound, which now fires
only when fetch is actually a candidate fix. Test coverage includes
the new non-ref classification and a parallel gate-skip case through
the recovery helper. Also pins both joined sentinels (ErrFetchOrigin
and ErrGitRefNotFound) in the fetch-failure recovery test so the
preserved hint context can't regress.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@aknysh aknysh merged commit 2e36b9d into main May 1, 2026
95 of 96 checks passed
@aknysh aknysh deleted the osterman/describe-affected-pr-ref branch May 1, 2026 05:29
@atmos-pro
Copy link
Copy Markdown
Contributor

atmos-pro Bot commented May 1, 2026

Note

Atmos Pro  

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

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 2, 2026

These changes were released in v1.217.0-rc.3.

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 size/l Large size PR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants