Skip to content

feat(lanes): allow --squash on diverged components - #10387

Merged
davidfirst merged 9 commits into
masterfrom
feat/squash-on-diverged-lanes
Jun 5, 2026
Merged

feat(lanes): allow --squash on diverged components#10387
davidfirst merged 9 commits into
masterfrom
feat/squash-on-diverged-lanes

Conversation

@davidfirst

@davidfirst davidfirst commented May 20, 2026

Copy link
Copy Markdown
Member

Why

bit lane merge <other-lane> --squash previously threw when components were diverged in history, forcing users to either do a two-way merge dance or fall back to --no-squash. With --no-squash on large lanes hosted in different scopes, exporting after the merge pulls the other scope's full snap history into the merging scope, which can blow up memory on both client and remote.

What

Diverged components under --squash now produce a single-parent merge snap. The dropped foreign head is recorded in Version.squashed (previousParents + laneId), so when running bit lane merge lane-b --squash from lane-a (where lane-a lives on scope-a and lane-b on scope-b):

  • Lane-b's intermediate snaps stay on scope-b — exporting lane-a only ships the merge snap to scope-a, not lane-b's chain.
  • bit log from a fresh consumer of scope-a is clean (no ParentNotFound walking into scope-b).
  • Re-merging lane-b after it advances works correctly: the existing VersionHistory graph treats squashed.previousParents as edges (version-history.ts:199, 263), so diverge calculation finds the last-merged head as the base instead of re-merging history that was already squashed.

No remote-side changes were needed.

Implementation

  • UnmergedComponent gains shouldSquash?: boolean.
  • mergeSnaps/applyVersionMultiple/applyVersion thread shouldSquash through and set the flag on the unmerged entry when divergent.
  • Squash guard in merge-lanes.main.runtime.ts no longer throws on diverged — it returns false so the snap-time path handles it.
  • At snap creation (snapping.main.runtime.ts), the unmerged-component branch checks shouldSquash: if true, calls setSquashed with both pre-squash parents and skips addParent of the foreign head.

Test plan

E2e tests in e2e/harmony/lanes/merge-lanes-squash-diverge.e2e.ts (21 cases, all passing):

  • single-scope diverged squash → single-parent snap, squash metadata, clean bit log
  • multi-scope diverged squash → scope-a does NOT contain lane-b snaps; scope-b still does
  • fresh consumer imports lane-a from scope-a → bit log and bit status don't throw
  • re-merge of lane-b after it advances → second merge snap records new dropped head, not the previously-squashed one; re-export succeeds
  • sanity: diverged without --squash still produces two-parent merge snap
  • sanity: squash on fast-forward unchanged

Previously `bit lane merge <other-lane> --squash` threw when components
were diverged in history. Now it produces a single-parent merge snap with
the dropped other-lane head captured in Version.squashed metadata. This
keeps the merged lane's history self-contained on its own scope, so
exporting after merging from a foreign scope does not pull that scope's
intermediate snap objects.

The existing VersionHistory graph already treats squashed.previousParents
as edges, so subsequent re-merges from the same source lane correctly
identify the previously-merged head as the diverge base.
Copilot AI review requested due to automatic review settings May 20, 2026 20:18

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

Enables bit lane merge <other-lane> --squash to work even when component histories are diverged, by creating a single-parent merge snap and recording the dropped foreign head in Version.squashed metadata (preventing cross-scope history pull-in on export).

Changes:

  • Thread a shouldSquash flag through lane-merge → merge engine → snap creation, and persist it on UnmergedComponent.
  • Adjust squash handling so diverged components no longer throw in the pre-squash path; instead they’re handled at snap-creation time via squashed metadata.
  • Add comprehensive E2E coverage for single-scope and multi-scope diverged squash, re-merge behavior, and non-squash sanity checks.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 1 comment.

Show a summary per file
File Description
scopes/lanes/merge-lanes/merge-lanes.main.runtime.ts Stops throwing on diverged --squash and passes shouldSquash through to the merge engine.
scopes/component/merging/merging.main.runtime.ts Propagates shouldSquash into UnmergedComponent entries for diverged merges.
scopes/component/snapping/snapping.main.runtime.ts On snap creation, uses shouldSquash to record Version.squashed metadata and avoid adding the foreign head as a second parent.
components/legacy/scope/lanes/unmerged-components.ts Extends UnmergedComponent schema with optional shouldSquash.
e2e/harmony/lanes/merge-lanes-squash-diverge.e2e.ts Adds E2E tests covering diverged squash scenarios and regressions.

Comment thread scopes/component/snapping/snapping.main.runtime.ts Outdated
setSquashed appends the passed log to version.modified[] by reference.
Previously we passed version.log directly and then mutated
version.log.message a few lines below — which corrupted the modified-log
entry. Build a dedicated squash log (matching the pattern in
merge-lanes.main.runtime.ts:getLogForSquash) so the audit trail stays
correct.

Addresses Copilot review on PR #10387.
Copilot AI review requested due to automatic review settings June 4, 2026 17:53

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.

Copilot encountered an error and was unable to review this pull request. You can try again by re-requesting a review.

Four cleanups from the /simplify pass:

1. (reuse) Move getLogForSquash to @teambit/harmony.modules.get-basic-log
   so snapping and merge-lanes share one helper instead of two copies of
   the squash-log message format.
2. (simplify) Drop the defensive currentParent ternary in snapping —
   the diverged path always has a current-lane parent (that's what makes
   it diverged); the empty-parents fallback was unreachable.
3. (simplify) Shorten the verbose modified-log comment in snapping.
4. (simplify) Drop the redundant inner Boolean() in the shouldSquash
   coercion in merging — outer Boolean() is enough.

Skipped findings (out of scope for /simplify):
- E1: hoist getBasicLog out of the per-component path — needs plumbing
  through the merge flow.
- A1: unify the two squash code paths (squashSnaps vs new branch) —
  major refactor.
- A2/A3/A4: move shouldSquash off UnmergedComponent / make it mutually
  exclusive with unrelated — correctness/design concerns, not simplification.

All 21 squash-on-diverged e2e tests pass.
Copilot AI review requested due to automatic review settings June 4, 2026 20:54

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated 1 comment.

Comment thread scopes/harmony/modules/get-basic-log/basic-log.ts
getBasicLog calls getBitCloudUser, which hits api.<cloudDomain>/user every
invocation. When called per-component during a merge or large snap, that's
N HTTP requests — slow and rate-limit-prone.

Cache the promise resolving to {username, email} at the module level. Date
stays fresh per call. Single user, single process → single network round-trip
regardless of caller count.

Addresses Copilot review on PR #10387.
Copilot AI review requested due to automatic review settings June 5, 2026 17:36

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

Mirrors the lane-to-lane diverged-squash describe block, but merges dev
into main when both have advanced from their common ancestor. Locks
down the symmetry: the snap-time shouldSquash branch produces a
single-parent merge snap on main with the dropped lane head recorded
in Version.squashed, just as it does for a lane destination.

Asserts: single-parent merge snap on main, squashed metadata records
the lane head, bit log on main shows main chain + merge snap (no lane
intermediates), and exporting main after the merge succeeds.
…r verification

dev lives on scope-b, main on scope-a. dev advances by 3 snaps (L1..L3)
and main advances independently by 3 snaps (M1..M3), so the histories
are diverged AND multi-snap on both sides.

bit lane merge -b/dev --squash --auto-merge-resolve theirs is run
from main. After export of the merge snap to scope-a, a fresh consumer
adds ONLY scope-a as a remote (not scope-b), imports comp1, and verifies:

- bit status / bit log don't throw
- log includes common ancestor + main chain + merge snap
- log does NOT include L1, L2, or L3 — the dev lane intermediates stayed
  on scope-b, never shipped to scope-a, and the consumer can read the
  full main history without needing scope-b reachable
Copilot AI review requested due to automatic review settings June 5, 2026 18:37

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

Copilot reviewed 7 out of 7 changed files in this pull request and generated no new comments.

@davidfirst
davidfirst merged commit f67d560 into master Jun 5, 2026
13 checks passed
@davidfirst
davidfirst deleted the feat/squash-on-diverged-lanes branch June 5, 2026 19:22
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.

3 participants