# aura merge *AST-level three-way merge that resolves conflicts at the function boundary, not the line.* ## Synopsis ```bash aura merge [--strategy=semantic|prefer-ours|prefer-theirs] [--dry-run] [--no-commit] [--continue] [--abort] ``` ## Description `aura merge` integrates changes from another branch into the current branch, operating on the **logic graph** rather than on raw text lines. Where `git merge` would produce a line-level conflict marker any time two developers touched the same hunk of a file, `aura merge` asks a finer-grained question: *did we touch the same logic node?* If two developers modified different functions in the same file, the merge completes cleanly. If they modified the same function, the conflict is reported on the function — with semantic before/after bodies — rather than as an interleaved line diff. The merge takes a three-way view. The **base** is the nearest common ancestor in the Aura semantic index (not the Git merge-base; these usually agree but can differ after rewinds). **Ours** is the current branch's logic graph; **theirs** is the graph at the tip of the named branch. For each logic node, Aura classifies the change as one of: *both unchanged*, *one side modified*, *both sides modified identically*, *both sides modified differently* (the only true conflict), *deleted by one side and modified by the other* (a delete-modify conflict), or *renamed on one side* (follow the rename and re-classify). When there are no true conflicts, `aura merge` produces a single merge commit with two parents, identical to a Git merge from the outside, but carrying an `aura.merge.graph` trailer describing which logic nodes came from which side. When there are conflicts, the command stops and writes a `MERGE_AURA` state file that `aura status` will surface. ## Flags | Flag | Description | | --- | --- | | `--strategy=semantic` | Default. Conflict only when both sides modified the same logic node. | | `--strategy=prefer-ours` | Auto-resolve every function-level conflict in favor of the current branch. | | `--strategy=prefer-theirs` | Auto-resolve every function-level conflict in favor of the incoming branch. | | `--dry-run` | Print the merge plan without modifying the working tree. | | `--no-commit` | Apply the merge to the working tree and index but do not create the merge commit. | | `--continue` | Resume a paused merge after all conflicts have been resolved. | | `--abort` | Discard the in-progress merge and restore the pre-merge `HEAD`. | | `--into ` | Merge into a branch other than the current one (checks it out first). | ## Conflict Output A function-level conflict looks like this: ```text CONFLICT (semantic): fn PaymentGateway::charge base: src/payments/gateway.rs @ a17f3c1 ours: modified on main - added: retry_on_network_error - added: log_audit_trail theirs: modified on feat/3ds - added: run_3ds_challenge - removed: legacy_cvv_check Resolution options: [o] keep ours [t] keep theirs [b] keep both (both-sides merge) [e] open $EDITOR on a 3-way AST template [s] suggest — ask Aura to synthesize a merge (experimental) > ``` Choosing `[b]` is the common case: Aura will emit the function twice with distinct suffixes (`charge_ours`, `charge_theirs`) and leave callsite rewiring to you. Choosing `[e]` opens an editor with a scaffolded function showing both sides' bodies as annotated regions. ## Differences from git merge - **Line moves do not conflict.** If you moved a function to a new file on one side and modified it on the other, `git merge` usually conflicts; `aura merge` follows the logic-node identity and merges cleanly. - **Renames do not conflict.** A rename on one side is treated as metadata; the modification on the other side is applied to the renamed node. - **Import reorderings do not conflict.** Import/use statements are canonicalized before diffing. - **Whitespace-only changes are invisible.** The AST ignores them entirely. - **Deleted-and-modified is surfaced loudly.** Git's default is to keep the modified version with a warning; Aura stops the merge and asks. ## Examples ### 1. A clean semantic merge ```bash aura merge feat/webhooks ``` ```text Merging feat/webhooks into main common ancestor: c4a19e0 logic nodes: ours 412, theirs 418, base 408 changes: +12 theirs, +6 ours, 0 overlapping result: 418 nodes, no conflicts Created merge commit 9b7a221. ``` ### 2. Seeing the plan first ```bash aura merge feat/billing --dry-run ``` Prints a classification table for every affected logic node without touching the working tree. Useful before a large merge to see whether a conflict storm is coming. ### 3. Resolving with a strategy ```bash aura merge hotfix/rate-limit --strategy=prefer-theirs ``` Any function that both sides modified is resolved in favor of `hotfix/rate-limit`. Functions only changed on the current branch are kept. This is much safer than `git merge -X theirs` because non-overlapping changes are preserved on both sides — the strategy only fires on true semantic conflicts. ### 4. Aborting a bad merge ```bash aura merge feat/storage # ... 14 conflicts, you decide this is the wrong time ... aura merge --abort ``` Restores `HEAD`, the index, and the working tree exactly as they were before the merge started. Any snapshots taken during conflict resolution are preserved and visible via `aura snapshot list`. ### 5. Continuing after manual resolution ```bash aura merge feat/search # ... 2 conflicts ... $EDITOR src/search/index.rs # resolve fn build_index aura save --paths src/search/index.rs --intent "Merge feat/search: keep ours for build_index, take theirs for query" aura merge --continue ``` Note that you commit the resolution with `aura save`, not `git commit`. This preserves the semantic record of which side each function came from. ## Exit Codes | Code | Meaning | | --- | --- | | `0` | Merge completed, commit created (or would be, with `--no-commit`). | | `1` | Generic failure. | | `2` | Conflicts remain. Resolve them and run `aura merge --continue`. | | `3` | `--continue` invoked with no merge in progress. | | `4` | `--abort` succeeded (returned as `0` in some shells; treat as informational). | | `5` | Target branch does not exist. | ## The three-way algorithm in more detail For each logic node that exists in any of base, ours, or theirs, Aura determines a state tuple `(in_base, in_ours, in_theirs)` and a hash tuple for the bodies. The state tuple has eight possible values; combined with the hash comparisons, it produces the following classification matrix. | State | Body relation | Classification | Resolution | | --- | --- | --- | --- | | in all three | all equal | unchanged | take either | | in all three | ours differs from base | one-side modified (ours) | take ours | | in all three | theirs differs from base | one-side modified (theirs) | take theirs | | in all three | both differ from base, equal to each other | converged | take either | | in all three | both differ from base and from each other | **conflict** | prompt | | in base and ours only | body equal to base | deleted by theirs, unchanged in ours | take the delete | | in base and ours only | body differs from base | **delete-modify conflict** | prompt | | in base and theirs only | symmetric to above | symmetric | symmetric | | in ours and theirs only | bodies equal | added independently, converged | take either | | in ours and theirs only | bodies differ | **add/add conflict** | prompt | | in ours only | — | added on our side | take ours | | in theirs only | — | added on their side | take theirs | Renames are applied before classification: if a node was renamed on one side, the renamed node is reunited with its pre-rename identity so the classification uses identity, not name. ## Configuration `aura.toml` can tune the merge under `[merge]`: ```text [merge] default_strategy = "semantic" auto_resolve_docs = true # doc-comment-only conflicts auto-resolve by combining both auto_resolve_imports= true # import/use reordering conflicts auto-resolve prompt_on_delete_modify = true editor_template = "three-way" ``` With `auto_resolve_docs`, a conflict where both sides only changed the doc comment on the same function is resolved by concatenating both comments with a divider. This is a pragmatic convenience: documentation conflicts are common and rarely important. ## Merge commits and the Git graph The commit that `aura merge` produces is a standard Git merge commit — two parents, a merge message, no surprises for tools like `git log` or `gh pr view`. The semantic metadata lives in the commit trailer and in `.aura/merges/.json`, which records the per-node resolution decisions. This file is checked in alongside the commit so later reviewers can see *why* a particular side was chosen for each conflicting node. If you rebase over an Aura merge commit, the metadata file is preserved and re-parented automatically. If you squash a merge, the metadata is aggregated into the resulting single commit's trailer. ## Post-merge checks Once the merge commit exists, Aura runs the same hooks as `aura save` against the merge's effective diff. Layer violations in the *merged* graph that did not exist on either parent are flagged — this is how refactors on two branches that are each valid locally can silently introduce cross-layer leaks when combined. Fix them with a follow-up save. ## See Also - [aura save](/aura-save) — used to commit merge resolutions - [aura pull](/aura-pull) — pull + merge in one step - [aura diff](/aura-diff) — inspect a pending merge's logic-node changes - [aura status](/aura-status) — shows `MERGE_AURA` in-progress state