Android: merge on pull, resolve merge conflicts, check of the stream #339

Merged
kfickel merged 6 commits from 337_merge-conflicts into main 2026-10-26 18:41:52 +01:00
Collaborator

Closes #337, following the plan in the issue.

What changes

A pull merges instead of failing. When both the phone and the remote changed:

  • What the phone changed and didn't commit is committed first. With no author set, the pull says so and changes nothing.
  • The remote branch is then merged in a merge commit (Merge origin/<branch>), which is pushed if "Push right after committing" is on.
  • The pull in the background (reminder worker) and the pull after a rejected auto-push merge only when the merge is clean. They never commit for the user, and undo a merge that would conflict. They then show "This phone and the remote changed the same lines. Pull here to resolve them." (PullState.NeedsYou), with a Pull button on the Changes tab and an amber dot on the tab.

Merge conflicts can be resolved on the phone. A pull by the user that conflicts leaves a merge waiting; it survives an app restart.

  • Changes tab while merging: the subtitle gets · merging, the sync card shows the conflict, and a list of conflicted files ("X of Y resolved") replaces the commit section. The buttons are Abort and Finish merge ("& push" when push is on). The menu gains Pull / Push / Abort merge. Auto-commit is paused, and its switch says so.
  • Resolver (ui/Conflict.kt): one card per conflict, with both sides and Keep phone / Keep origin / Keep both. A chosen conflict shows its result with Change. Mark resolved writes and stages the file.
  • Edit by hand opens the editor with the markers (<<<<<<< this phone … >>>>>>> origin/main) tinted and a banner. Saving a note with no markers left marks it resolved.

A card shows the check of the stream. It replaces the green sentence on the Changes tab.

  • The check runs every time the tab is shown and every time the stream changes.
  • It shows "Checking stream…" only if it takes longer than 150 ms, then either "Stream is valid" or the problems in groups: can't read file (unreadable files, leftover conflict markers), file name, and not defined in .streamd.toml.
  • A file row opens the editor at its line, with a banner that turns green once the file is fixed. A tag row opens the form of a new tag with that name.

Layers

  • Core: document/conflicts.rs (conflicts, resolve_conflicts, conflict_markers), maintenance/check.rs (stream_check), and document::file_name_problems, now shared with api::file_problems. All of it is exposed in api.rs.
  • FFI: conflicts, resolveConflicts, conflictMarkers, project.streamCheck and their records.
  • Git layer: pull(remote, Merging?), merge(), markResolved, finishMerge, abortMerge. The files a merge marked are kept in .git/STREAMD_CONFLICTS, because Git no longer reports a file as conflicted once it is staged.
  • App: StreamCheck replaces StreamHealth, TextCheck is added for the editor, and ConflictsAdapter implements a new Resolving interface. The number badge on Changes becomes an amber dot.

Differences from the handover spec

  • There are no "can't read file" parse errors with quick fixes: the core never fails to parse a note, and an unclosed code fence is valid Markdown. The group lists unreadable files and leftover conflict markers instead. Misnamed files get a group of their own, because they can be read.
  • The editor has no gutter, so the failing line and the marker lines are tinted in the text instead.
  • The app doesn't know the other device's name, so the text says "another device".
  • The Discard menu item is kept, and disabled while a merge waits.

Checks

  • nix flake check passes.
  • nix build .#streamd-app passes: 493 unit tests, plus the screenshot tests with new references for the check states, the merge states, the resolver and the editor banners.
  • Not tried on a device or emulator; the merge itself is tested with JGit against a bare repository.
Closes #337, following the plan in the issue. ## What changes **A pull merges instead of failing.** When both the phone and the remote changed: - What the phone changed and didn't commit is committed first. With no author set, the pull says so and changes nothing. - The remote branch is then merged in a merge commit (`Merge origin/<branch>`), which is pushed if "Push right after committing" is on. - The pull in the background (reminder worker) and the pull after a rejected auto-push merge only when the merge is clean. They never commit for the user, and undo a merge that would conflict. They then show "This phone and the remote changed the same lines. Pull here to resolve them." (`PullState.NeedsYou`), with a **Pull** button on the Changes tab and an amber dot on the tab. **Merge conflicts can be resolved on the phone.** A pull by the user that conflicts leaves a merge waiting; it survives an app restart. - **Changes tab while merging:** the subtitle gets ` · merging`, the sync card shows the conflict, and a list of conflicted files ("X of Y resolved") replaces the commit section. The buttons are **Abort** and **Finish merge** ("& push" when push is on). The menu gains Pull / Push / Abort merge. Auto-commit is paused, and its switch says so. - **Resolver** (`ui/Conflict.kt`): one card per conflict, with both sides and **Keep phone / Keep origin / Keep both**. A chosen conflict shows its result with **Change**. **Mark resolved** writes and stages the file. - **Edit by hand** opens the editor with the markers (`<<<<<<< this phone` … `>>>>>>> origin/main`) tinted and a banner. Saving a note with no markers left marks it resolved. **A card shows the check of the stream.** It replaces the green sentence on the Changes tab. - The check runs every time the tab is shown and every time the stream changes. - It shows "Checking stream…" only if it takes longer than 150 ms, then either "Stream is valid" or the problems in groups: can't read file (unreadable files, leftover conflict markers), file name, and not defined in .streamd.toml. - A file row opens the editor at its line, with a banner that turns green once the file is fixed. A tag row opens the form of a new tag with that name. ## Layers - **Core:** `document/conflicts.rs` (`conflicts`, `resolve_conflicts`, `conflict_markers`), `maintenance/check.rs` (`stream_check`), and `document::file_name_problems`, now shared with `api::file_problems`. All of it is exposed in `api.rs`. - **FFI:** `conflicts`, `resolveConflicts`, `conflictMarkers`, `project.streamCheck` and their records. - **Git layer:** `pull(remote, Merging?)`, `merge()`, `markResolved`, `finishMerge`, `abortMerge`. The files a merge marked are kept in `.git/STREAMD_CONFLICTS`, because Git no longer reports a file as conflicted once it is staged. - **App:** `StreamCheck` replaces `StreamHealth`, `TextCheck` is added for the editor, and `ConflictsAdapter` implements a new `Resolving` interface. The number badge on Changes becomes an amber dot. ## Differences from the handover spec - There are no "can't read file" parse errors with quick fixes: the core never fails to parse a note, and an unclosed code fence is valid Markdown. The group lists unreadable files and leftover conflict markers instead. Misnamed files get a group of their own, because they can be read. - The editor has no gutter, so the failing line and the marker lines are tinted in the text instead. - The app doesn't know the other device's name, so the text says "another device". - The Discard menu item is kept, and disabled while a merge waits. ## Checks - `nix flake check` passes. - `nix build .#streamd-app` passes: 493 unit tests, plus the screenshot tests with new references for the check states, the merge states, the resolver and the editor banners. - Not tried on a device or emulator; the merge itself is tested with JGit against a bare repository.
kfickel deleted branch 337_merge-conflicts 2026-10-26 18:41:53 +01:00
Sign in to join this conversation.
No reviewers
No labels
android
cli
epic
planned
tui
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Reference
kfickel/streamd!339
No description provided.