Doc: Resolves #16603: Add documentation for conflict auto-merge and desktop resolution UI (#16604)

Co-authored-by: Laurent Cozic <laurent22@users.noreply.github.com>
This commit is contained in:
Sriram Varun KumarandLaurent Cozic authored and GitHub committed 2026-10-05 14:09:44 +01:00
1 parent 618f7e4ff7
commit aaa6f8e3ae
1 file changed
+38
+38
View File
@@ -0,0 +1,38 @@
# Conflict auto-merge
When the same note is edited on two devices before they sync, if a change is made when one of the devices was not fully synced, Joplin creates a conflict note. Auto-merge avoids this when the edits don't overlap, using a three-way merge. It runs on the client that syncs second; other devices just receive the merged note.
## Base version
The merge needs three versions: **base** (the last version this device and the sync target agreed on), **local** and **remote**.
The base is stored per sync target in `sync_items.base_body` and `base_title`. It is recorded after an upload, after a download, and after a conflict, whichever version is left in the original note. If a downloaded note is encrypted, the base is cleared until it is decrypted, so that a conflict before decryption creates a normal conflict note.
## Merge
The merge is line-based. Regions changed by only one side are taken as-is. Regions changed by both sides are auto-merged if both made the same change, otherwise they become a conflict. Edits on the same or adjacent lines therefore conflict. Word-level merging was tried and dropped because it could silently duplicate text. Titles follow the same rule.
If everything merges, the merged note is saved over the local note, becomes the new base, and its `updated_time` is moved past the remote time so it gets uploaded. If some regions conflict, a conflict note is still created, but both notes already include the non-conflicting changes, so they differ only where there's a real conflict.
## Fallback to a conflict note
The merge is skipped, and a normal conflict note is created, when:
- `sync.autoMergeConflicts` setting is off
- there is no recorded base
- the item is read-only or either note is locked
- either note cannot be decrypted
- an edit which touches identical lines, since the diff can't tell which one changed and may duplicate content.
- the diff exceeds its bounds (`maxEditLength` 5000, timeout 1000ms)
## E2EE
An encrypted remote note is decrypted in memory for the merge without being saved. If a conflict note is created, the original note is saved with the decrypted (and partially merged) remote content.
## Code
- `services/conflict/diffNotes.ts`: merge engine (`autoMerge()`) and the duplicate-line guard
- `services/conflict/boundedDiff3.ts`: fork of `node-diff3`'s `diff3MergeRegions()` using a bounded diff
- `services/conflict/autoMergeNote.ts`: merges title and body of a note
- `services/synchronizer/utils/handleConflictAction.ts`: runs the merge during sync and applies the result
- `models/BaseItem.ts`: stores the base and keeps it when a sync doesn't provide a new one