feat(lanes): allow --squash on diverged components - #10387
Merged
Conversation
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.
Contributor
There was a problem hiding this comment.
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
shouldSquashflag through lane-merge → merge engine → snap creation, and persist it onUnmergedComponent. - 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. |
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.
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.
GiladShoham
approved these changes
Jun 4, 2026
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.
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
bit lane merge <other-lane> --squashpreviously 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-squashon 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
--squashnow produce a single-parent merge snap. The dropped foreign head is recorded inVersion.squashed(previousParents + laneId), so when runningbit lane merge lane-b --squashfrom lane-a (where lane-a lives on scope-a and lane-b on scope-b):bit logfrom a fresh consumer of scope-a is clean (noParentNotFoundwalking into scope-b).VersionHistorygraph treatssquashed.previousParentsas 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
UnmergedComponentgainsshouldSquash?: boolean.mergeSnaps/applyVersionMultiple/applyVersionthreadshouldSquashthrough and set the flag on the unmerged entry when divergent.merge-lanes.main.runtime.tsno longer throws on diverged — it returns false so the snap-time path handles it.snapping.main.runtime.ts), the unmerged-component branch checksshouldSquash: if true, callssetSquashedwith both pre-squash parents and skipsaddParentof the foreign head.Test plan
E2e tests in
e2e/harmony/lanes/merge-lanes-squash-diverge.e2e.ts(21 cases, all passing):bit logbit logandbit statusdon't throw--squashstill produces two-parent merge snap