> For the complete documentation index, see [llms.txt](https://docs.flxbl.io/flxbl/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.flxbl.io/flxbl/sfp/building-artifacts/automated-package-versioning/versioning-across-branches.md).

# Versioning across branches

{% hint style="warning" %}
**This feature is in limited preview** — it is available to selected customers only, and behaviour and commands may change based on feedback. Reach out to the flxbl team if you would like early access.
{% endhint %}

Versions are tracked on the server per **branch**. Each branch line versions itself independently, and git history reconciles them whenever lines merge. This page walks through the behaviours you will encounter in multi-branch development.

### New branches

A new branch does not require any onboarding step. Its packages resolve their base versions from the release tags reachable in its history, and the first publish on the branch creates its own server records.

```
main:            sales published as 2.5.0
└── release/1.0  (branched from main)
      first build of a fix → sales 2.5.1.<build>
```

### Parallel branches version independently

Two active branches do not coordinate. If both apply a `feat:` to a package last published as `2.5.0`, **both** lines produce a `2.6.0.<build>` — distinguished only by their build numbers. This is by design: artifact versions within a line stay meaningful, and uniqueness across lines is restored the moment the lines merge.

### Merges reconcile through the version floor

When one line merges into another, the receiving branch's next build never goes backwards: its base version is lifted to the highest version visible in its history — server records or merged release tags, whichever is higher.

Just as importantly, **a change only ever pays for its version once**. Commits and pull requests that were already converted into a version on the source branch are recognised as "spent" and are not re-applied on the receiving branch. A cascaded change lands as a single patch — "same content, new artifact on this branch" — while new work on the receiving branch keeps expressing its own intent normally.

```
main:         feat(sales) shipped → 2.6.0
                    │
                    ▼  merge into release/1.0 (sales last at 2.5.2)
release/1.0:  next build → 2.6.1    ✓ floor lifted past 2.6.0, feat not re-applied
              (not 2.7.0 — the minor was already spent on main)
```

The same applies to explicit intents: a `/version sales:major` that produced `3.0.0` on main lands on a release branch as `3.0.1`, not `4.0.0`.

A typical hotfix round trip:

```
release/1.0:  fix(sales) → 2.5.3 published, merged back to main
main:         (sales last at 2.6.0) next build → 2.6.1
              ✓ monotonic, hotfix commits spent, no double bump
```

### Already-built content is skipped

The floor stops a cascaded change from bumping twice. A change that is *only* a cascade — the receiving branch adds nothing of its own — produces no new artifact at all. When a merge brings in a package whose content is identical to a version already published on the source line, the build recognises it as already built and skips it:

```
release/1.0:  merge main (sales already published as 2.6.0), no local change to sales
              → sales skipped — "already built in merged branch (tag: sales_v2.6.0.<build>)"
              → no new artifact, no version minted
```

The receiving branch keeps the source line's artifact instead of rebuilding an identical one. The package is rebuilt and versioned only when the receiving branch has its own change to it — then the floor lifts its base to the merged version and it takes a single patch, as above.

### Mixed adoption across branches

It is perfectly fine for one branch to have adopted automated versioning while a parallel branch still uses manual versions — the sentinel lives in `sfdx-project.json`, so adoption is part of the branch's content:

* While parallel, the adopted branch computes versions and the manual branch behaves classically. The project-level setting is shared, but manual packages always resolve through the user-override stage.
* When the adopted branch merges into the manual one, the `0.0.0.NEXT` sentinel merges with it (a normal three-way merge — only one side changed the line), and the receiving branch becomes server-managed for that package too. Its next build floors past the adopted line's published versions and continues monotonically.
* The reverse merge leaves adoption intact, since the manual branch never touched the sentinel line.

{% hint style="warning" %}
One case needs a human decision: if the manual branch **bumped the same package's version** while the other branch adopted, the merge raises a conflict between `0.0.0.NEXT` and the bumped version. Keep `0.0.0.NEXT` and re-express the intended bump as a `version:` footer or `/version` comment — resolving to the bumped number would silently take the package back to manual management.
{% endhint %}

### Use merge commits between long-lived branches

Squash-merging a pull request **within** a branch is always fine — lazy adoption PRs, feature PRs and onboarding PRs can all be squashed.

Merging **between long-lived branches** (release lines, cascade flows, hotfix back-merges) must use real merge commits. A squash rewrites the incoming commits, which disconnects the source branch's release tags from the receiving branch's history — the version floor can no longer see what the other line already published, and intents are re-derived from the squashed message. The symptoms are duplicate versions across lines and double bumps.

### Manual version bumps still trigger rebuilds

For packages you have **not** opted in, changing the version number in `sfdx-project.json` keeps its classic meaning — the package is rebuilt. This holds even in projects where other packages are server-managed: the descriptor comparison ignores the version field only for `0.0.0.NEXT` packages, and any *other* descriptor change (dependencies, installation behaviour, scripts) triggers a rebuild for every package, managed or not.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://docs.flxbl.io/flxbl/sfp/building-artifacts/automated-package-versioning/versioning-across-branches.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
