> ## Documentation Index
> Fetch the complete documentation index at: https://docs.tensorlake.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Merging Changes

> Land workspace changes on branches that moved, rebase a workspace, resolve conflicts, and merge branches directly.

Two situations bring you to this page:

* **A workspace and its branch both changed.** This needs a [mounted workspace](/git/workspace-mounts): the commands take a mount path like `/work`, and the mounted directory is where conflicts get resolved. The normal loop is: rebase onto the branch, fix any conflicts, snapshot the resolution, then promote. The daemon seals pending local WAL before the rebase starts.
* **Two branches diverged.** [`tl git merge`](#merge-branches-directly) merges them directly on the server — no mount, no clone, no working copy. It runs from any machine with the CLI, or from your control plane with the SDKs.

Tensorlake never force-overwrites a branch. If a merge cannot land cleanly, nothing is published unless you explicitly choose a mode that materializes conflict markers.

<Note>
  If you are working in a **clone**, none of this applies — use ordinary `git merge`, `git rebase`, and `git push`; Tensorlake is a normal remote. See [Git or the tl CLI?](/git/introduction#git-or-the-tl-cli) for the one-rule guide.
</Note>

## Promote a Workspace

If the target branch has not moved since the workspace was created, promotion lands the workspace on that branch:

```bash theme={null}
$ tl git promote /work main
Promoted workspace 3f9a2b7e1c4d -> main at 1c4d9a2f7e5b3d8c6a4f2e0d9b7c5a3f1e8d6b4c (squashed)
```

If the branch moved, a plain promotion fails instead of overwriting someone else's work. Add `--merge` when Tensorlake should merge the workspace with the latest branch tip:

```bash theme={null}
$ tl git promote /work main --merge
Promoted workspace 3f9a2b7e1c4d -> main at 7a1c3e5b9d2f4b6d8e0a2c4f6e8b0d3a5c7e9f1b (merge)
```

When the changes do not overlap, this lands one merge commit combining both histories. If the branch did not move, it fast-forwards like a plain promotion.

## When Promotion Conflicts

If the workspace and branch changed the same content, promotion publishes nothing. The target branch and workspace stay unchanged, and the command reports each conflicted path:

```bash theme={null}
$ tl git promote /work main --merge
error: promote to main conflicts on 2 path(s); nothing was published:
  content        src/parser.py
  delete_modify  notes/plan.md
```

Resolve the conflict in the workspace:

1. Run `tl git rebase /work main`.
2. Edit the conflicted files.
3. Snapshot the resolved files.
4. Promote again.

## Rebase the Branch into a Workspace

`tl git rebase` replays the workspace onto the target branch. The mounted directory updates in place:

```bash theme={null}
$ tl git rebase /work main
Rebased workspace 3f9a2b7e1c4d onto main at 9c4e2a7b1f3d (3 snapshot(s) replayed).
```

If the rebase conflicts, Tensorlake writes standard Git diff3 markers into the workspace files:

```bash theme={null}
$ tl git rebase /work main
note: 1 conflict(s) materialized as diff3 markers:
  content        src/parser.py
Resolve the markers, then `tl git snapshot /work`.
```

The file carries the branch version, the merge base, and the workspace version:

```text theme={null}
<<<<<<< main
def parse(text: str) -> Ast:
||||||| base
def parse(text):
=======
def parse(text, *, strict=False):
>>>>>>> workspace 3f9a2b7e1c4d
```

Edit the file to resolve the conflict, then snapshot and promote:

```bash theme={null}
$ tl git snapshot /work -m "resolve parser conflict"
$ tl git promote /work main --merge
Promoted workspace 3f9a2b7e1c4d -> main at 4b6d8e0a2c4f6e8b0d3a5c7e9f1b7a1c3e5b9d2f (fast-forward)
```

The daemon serializes the transition with autosave and seals pending local WAL before rebasing, so edits do not race the base change.

## Sync a View

`tl git sync /work [<ref-or-full-commit>]` has a narrower role than rebase. It refreshes the current source or switches a read-only or snapshot-free writable view, carrying any unsnapshotted WAL tail onto the new base. It refuses a switch that would rewrite an established workspace snapshot chain and directs you to `tl git rebase` instead.

Use sync for checkout-like source changes before a workspace has snapshot history. Use rebase once workspace commits must be replayed onto another base.

## Structured Conflict Records

A merge that materializes conflicts — a `--materialize` direct merge or a conflicted rebase — records more than markers. Each conflicted commit carries a structured record naming every path and the three-way terms it was merged from, available to the control plane and queryable without parsing file contents:

```bash theme={null}
$ tl git conflicts agent-outputs 7a1c3e5b9d2f
ours: 4b6d8e0a2c4f
theirs: 9c4e2a7b1f3d
base: 5f2c8e1a9b3d
conflicts: 1 path(s):
  content        src/parser.py
```

Nothing is silently overwritten: both sides of every conflict remain reachable from the record.

## Merge Branches Directly

Use `tl git merge` when you want to merge one branch into another without creating a workspace.

Preflight first to see what would happen. It never writes:

```bash theme={null}
$ tl git merge agent-outputs main dev --preflight
merge base: 5f2c8e1a9b3d
changed paths: 12
Clean merge.
```

A shallow preflight reports same-file collisions as *potential* conflicts. Add `--deep` when you need exact text-merge results:

```bash theme={null}
$ tl git merge agent-outputs main dev --preflight --deep
merge base: 5f2c8e1a9b3d
changed paths: 12
conflicts: 1 conflict(s):
  content        src/parser.py (potential)
  (run with --deep for exact content-merge answers)
```

Drop `--preflight` to land the merge:

```bash theme={null}
$ tl git merge agent-outputs main dev -m "land dev"
merge base: 5f2c8e1a9b3d
changed paths: 12
Merged dev into main at 7a1c3e5b9d2f
```

A conflicted direct merge publishes nothing and exits non-zero. Add `--materialize` to land it anyway with markers and a structured conflict record. Add `--json` for machine-readable reports.

## How Merging Runs

Merges run server-side with a three-way merge engine. Tensorlake does not clone the repository, check out a working tree, or scan every file.

Merge cost scales with the number of changed paths, not total repository size.

## Conflict Kinds

Conflict reports classify each path:

| Kind            | Meaning                                                                      |
| --------------- | ---------------------------------------------------------------------------- |
| `content`       | Both sides edited the same region of a text file.                            |
| `delete_modify` | One side deleted the path; the other modified it. The modified side is kept. |
| `add_add`       | Both sides added the path with different content.                            |
| `kind_mismatch` | The path is a file on one side and a directory or symlink on the other.      |
| `mode`          | Both sides changed the file mode to different values.                        |
| `too_large`     | A side is binary or over the text-merge size limit; never text-merged.       |

## Next Steps

<CardGroup cols={2}>
  <Card title="Repository Mounts" icon="folder-tree" href="/git/workspace-mounts">
    Mount, snapshot, and promote — the workflow merging builds on.
  </Card>

  <Card title="Architecture" icon="cubes" href="/filesystems/architecture">
    How the merge engine and conflict policies work internally.
  </Card>
</CardGroup>
