Skip to content

fix(bundle): fetch prerequisite commits by SHA instead of broad deepen#39466

Merged
dsyme merged 2 commits into
mainfrom
dsyme/bundle-prereq-direct-fetch
Jun 15, 2026
Merged

fix(bundle): fetch prerequisite commits by SHA instead of broad deepen#39466
dsyme merged 2 commits into
mainfrom
dsyme/bundle-prereq-direct-fetch

Conversation

@dsyme

@dsyme dsyme commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

fix(bundle): fetch prerequisite commits by SHA instead of broad deepen

Summary

Replaces the iterative-deepen-only strategy for resolving bundle prerequisites with a targeted three-tier approach. The primary path now fetches the exact prerequisite SHA directly from origin (git fetch origin <sha>), falling back to fixed-step iterative deepening only if that fails, and resorting to --unshallow as a last option. This is more efficient and reliable — especially in shallow clones where the prerequisite object already exists remotely and a broad deepen would waste network and time.

Alongside the fetch strategy change, the missing-object check was corrected from an ancestry probe (merge-base --is-ancestor) to a plain object-presence probe (cat-file -e), matching its actual purpose: confirming the object exists in the local repo, not that it sits on a specific branch.


What changed

actions/setup/js/git_helpers.cjscore logic (high impact)

Before After
Single-strategy iterative deepen via BUNDLE_DEEPEN_STEPS array Three-tier: (1) direct SHA fetch, (2) fixed-step deepen, (3) --unshallow
findMissingAncestors using merge-base --is-ancestor findMissingObjects using cat-file -e
Variable step sizes from array BUNDLE_DEEPEN_STEP=5, BUNDLE_DEEPEN_MAX_ITERATIONS=200

The rename from findMissingAncestorsfindMissingObjects reflects the corrected semantics: the function answers "does this object exist locally?", not "is this commit an ancestor of HEAD?".

actions/setup/js/git_helpers.test.cjs — unit tests (medium impact)

  • Replaced merge-base --is-ancestor mocks with cat-file -e mocks throughout.
  • Added a dedicated test for the fixed-step fallback deepen path.
  • Covers the new direct-SHA-fetch as the primary success path.

actions/setup/js/create_pull_request.test.cjs — integration test (medium impact)

  • Shallow-checkout bundle test rewritten to assert the exact prerequisite SHA is fetched directly from origin with no --deepen or --unshallow involved.
  • beforeEach mock updated from merge-base --is-ancestor to cat-file -e returning success by default.

actions/setup/js/push_to_pull_request_branch.test.cjs — shallow-checkout test (medium impact)

  • Introduced prereqSha/prereqFetched tracking variables.
  • git merge-base --is-ancestor stub replaced with git cat-file -e stub whose exit code is gated on whether a direct fetch has already occurred.
  • mockExec.exec implementation now sets prereqFetched = true when it observes git fetch origin <prereqSha>.
  • Assertions tightened: verifies a direct SHA fetch happened and that no iterative --deepen call was made.

Why this matters

In shallow checkouts, iterating --deepen to hunt for a prerequisite SHA is a best-effort heuristic — it may overshoot (fetching far more history than needed) or fail entirely if the prerequisite is on a different branch. Fetching by SHA directly is deterministic, minimal, and works regardless of branch topology. The test coverage changes confirm the happy path no longer touches --deepen at all.


Risk

Low. No public API or workflow interface changes. The three-tier fallback ensures behaviour degrades gracefully to the previous deepen strategy if the direct fetch fails for any reason. Breaking: false across all changed files.

Generated by PR Description Updater for issue #39466 · 276.6 AIC · ⌖ 13.4 AIC · ⊞ 19.9K ·

The safe_outputs bundle-application path could hang while "iteratively
deepening origin/<base>" when updating a PR branch with a long, complex
history. The deepen gate asked whether each bundle prerequisite was an
ancestor of origin/<base>; for push-to-PR-branch updates the prerequisite
lives on the PR branch and is never an ancestor of base, so deepening base
could never satisfy it and escalated toward a full --unshallow that timed
out.

Rework ensureFullHistoryForBundle:
- Primary path now fetches the exact prerequisite commit SHAs (declared by
  git bundle verify) directly from origin via `git fetch origin <sha>...`,
  which GitHub honors and which brings precisely the needed objects
  regardless of which branch they live on.
- Gate changed from base-branch ancestry (merge-base --is-ancestor) to
  object presence (git cat-file -e <sha>^{commit}), which is what bundle
  apply actually requires.
- Fallback deepen now steps by a small fixed increment (5 commits) up to a
  bounded iteration cap, re-checking presence each step, so a single fetch
  cannot pull a huge slice of monorepo history. --unshallow remains a last
  resort only.

Update tests to cover the direct-SHA primary path and the deepen-by-5
fallback.
Copilot AI review requested due to automatic review settings June 15, 2026 23:35
@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copilot AI 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.

Pull request overview

This PR fixes bundle-application hangs in shallow clones by switching prerequisite satisfaction from “is ancestor of origin/” checks + broad deepening to a targeted “object exists locally” gate with a primary fetch-by-SHA strategy, plus a bounded deepen fallback.

Changes:

  • Reworked ensureFullHistoryForBundle to fetch missing bundle prerequisite commit SHAs directly from origin and gate on git cat-file -e <sha>^{commit}.
  • Added a bounded fallback deepen strategy using a small fixed step (--deepen=5) with an iteration cap before last-resort --unshallow.
  • Updated unit/integration tests to cover the direct-SHA path and the new fallback deepen behavior.
Show a summary per file
File Description
actions/setup/js/git_helpers.cjs Implements direct prerequisite SHA fetch, presence-based gating, and bounded deepen fallback.
actions/setup/js/git_helpers.test.cjs Updates tests to validate direct SHA fetch and fixed-step deepen fallback.
actions/setup/js/create_pull_request.test.cjs Adjusts create PR bundle shallow-checkout tests to assert direct SHA prerequisite fetch and no deepen/unshallow.
actions/setup/js/push_to_pull_request_branch.test.cjs Adjusts push-to-PR bundle application test to assert direct SHA fetch and no deepen.

Copilot's findings

Tip

Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

  • Files reviewed: 4/4 changed files
  • Comments generated: 1

Comment thread actions/setup/js/git_helpers.test.cjs Outdated
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
@dsyme dsyme merged commit 355cb87 into main Jun 15, 2026
9 checks passed
@dsyme dsyme deleted the dsyme/bundle-prereq-direct-fetch branch June 15, 2026 23:42
@github-actions

Copy link
Copy Markdown
Contributor

✅ smoke-ci: safeoutputs CLI comment + comment-memory run (27583773725)

Generated by 🧪 Smoke CI for issue #39466 ·

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants