Conflict API
Audience
This document is aimed at programmers working on magic-folder. It is a proposed design.
Motivation
Under normal operation, magic-folders scans other participants’ Snapshots and reflects those changes locally; sometimes, these changes can conflict.
It is desirable to have an explicit API for noticing and resolving conflicts. The core of magic-folders operation with users is the filesystem. This makes the filesystem also an API and conflicts must be part of that API. The command-line interface shall have sub-commands for listing and resolving conficts; these commands will use the HTTP APIs.
Filesystem API for Conflicts and Resolution
When a Snapshot is found to conflict with a particluar local file (say foo
) a “conflict file” is written beside it, reflecting the other participants’ content (foo.conflict-laptop
).
That is, foo.conflict-laptop
indicates that the participant “laptop
” has a conflicting update
The file foo.conflict-laptop
will contain the downloaded .content
of “laptop
“‘s Snapshot.
The content of foo
remains what it was when the conflict was detected.
(Note that when multiple participants exist it’s possible to have multiple *.confict-*
files pertaining to a single local file).
List Conflicts
One can use normal directory-browsing tools such as ls
to notice conflict files.
Resolve a Conflict
When _all_ <relpath>.confict-*
files for a given root are deleted, the conflict is deemed resolved.
The resolution is whatever the contents of <relpath>
are currently.
So, to resolve a conflict as “take theirs”, one could run: mv foo.conflict-laptop foo
if there was a single conflict from participant “laptop
”.
To resolve a conflict as “take mine”, one simply deletes foo.conflict-laptop
if there was a single conflict from participant “laptop
”.
HTTP API for Conflicts and Resolution
GET /v1/magic-folder/<folder-name>/conflicts
Returns a list (possibly empty) of local filesystem paths corresponding to each Snapshot that is currently in a Conflict state in the given magic-folder.
Our content is in the path itself.
The conflicting “other” content is in <path>.conflict-<name>
where <name>
is the petname of the participant who is provided the conflicted content.
This endpoint returns a JSON dict mapping any local conflicted relpath
to a list of authors.
Following this example:
{
"foo": ["laptop"]
}
This indicates that a single file foo
has a conflict with a single other participant laptop
.
POST /v1/magic-folder/<folder-name>/resolve-conflict
A JSON body is passed to this endpoint, following this example:
{
"relpath": "local/path",
"resolution": "author"
}
The relpath
key is required.
It must be a filesystem path relative to the selected magic-folder.
The resolution
key is required.
It must be the name of a current participant in the given magic-folder.
It is an error if the given relpath
in the given magic-folder is not currently in a conflicted state.
If the resolution
is our user-name then all conflict files are deleted new (local) Snapshot is created (with parents corresponding to all conflicting participants).
If instead the resolution is some other participant, then the content of <relpath>.conflict-<participant>
is moved to <relpath>
and any other conflict files are deleted.
Then a new (local) Snapshot is created (with parents corresponding to all conflicting participants).
The response is delayed until the local state tracking the new Snapshot has been created.
The response code is CREATED and the Content-Type is application/json
.
The response body follows the form of this example:
{}