Repository navigation
feat(sync): rebuild the Git sync experience and close its edge cases - #365
Merged
Merged
Conversation
…oned locks - A mirror now names the repository it was filled from and is rebuilt for another one, and a branch the remote does not hold starts a new history. Before, switching to an empty repository, or pushing after the remote branch was deleted, published every earlier payload there again. - A push Git refuses because another machine published meanwhile folds the branch in again and is tried once more. - The sync lock records its owner, so a Host that stopped mid-operation no longer leaves every later sync refused; the one-command credential files such a Host left behind are removed. - Remote failures carry a reason (credentials, not found, network, timeout, push rejected, empty branch, busy, no repository, Git missing, GitHub grant refused) through the RPC details, and the status probe gives up after 20 s. - A blank commit author uses this machine's configured Git identity, else the channel's own instead of failing or publishing a guessed host name. - git init no longer passes --initial-branch, which needs Git 2.28 while the channel declares 2.20. - The preview no longer counts the re-stamped manifest as a changed file, compaction sizes are converted from KiB, the push reads the remote once instead of twice, and the last finished sync is kept on disk. - The GitHub repository list reads up to five pages. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The block now reads in the order it is set up, as preference rows in one card like a Provider row's editor: - Repository: connect through a GitHub sign-in (one click copies the device code and opens GitHub; then search, pick or create a private repository, with a confirmation before a public one) or by address, checked before it is sent, with a token field only for HTTPS. Once connected, the row shows the repository, whether it answers and how it authenticates, names a failure (credentials, not found, network, timeout) with a way to fix credentials, and offers Change and Disconnect (confirmed, deletes nothing). - Sync: one primary "Sync now", which merges the repository and pushes, and "Check remote", one dialog comparing each component, conflicts and held deletions, with "Merge here only" and "Sync now". The preview bar, the second merge button and the history dialog that opened on Check remote are gone. Results and failures are said beside the row, in the page's language, with the last sync time. - Automatic sync uses the DSH selector and says when it runs next or why it last failed; Backup history lists versions by time and computer. - Advanced keeps branch, directory and an explicit commit author choice (default, this computer's Git, custom) behind a disclosure. Memory reconciliation drops its standing three-paragraph rules; each consequence is stated where it applies. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…guarantees The operations guide, the UI guide, the README and the configuration and storage references follow the new order (repository, sync, automatic sync, backup history, advanced), name the guarantees behind it, and drop the reconciliation rules block the page no longer shows. The end-to-end script also checks that a refused operation reaches the page with its reason. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Moving the repository to another host forgot nothing, so the next status probe and push handed a token saved for one host to another. The Host now drops a saved token when the repository moves to another host and no new token comes with the move; the address form stops offering the old token for a different host. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…timer on disconnect - "Copy and open GitHub" passed `noopener` to window.open, which then always answered null, so the page claimed the browser had blocked a tab it had opened. The opener is now cut after opening, and the copy starts first. - Check remote on a branch that holds no backup yet says so and still offers Sync now, the step that creates it. - Disconnecting also turns automatic sync off, as the confirmation says, so connecting another repository later does not start syncing to it on the old timer. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Host: - give the status probe its 20-second limit; git() ignored timeoutMs - count what both attempts of a retried push brought in - keep one copy per diverging document or space when the same version is merged again, as a retried push does - take over an ownerless lock after 15 minutes and rename it aside before removing it, so two Hosts cannot both take it - keep an automatic sync failure's reason only while its error stands, and do not report a busy channel as a failure Page: - read the preview and the file diff one after the other, so the comparison never meets its own lock - count remote-only and local-only entries without the held-back and removed ones the other lines already show - show a sync started from the comparison failing inside the dialog, and a failed disconnect inside its confirmation - offer Set credentials for a repository reported not found, and say "needs an access token" for HTTPS hosts other than github.com - keep polling a GitHub device code through a transient error - match an existing repository per owner before creating one - say that the history lists the latest 100 versions - accept an unchanged author saved by an earlier version, and name it by its email when it has no name - move the token's Remove button out of its label, and read the state again after a removal or a cancelled change - let the buttons return before the state line is read again Docs: say when a sync fails before committing, that the environment token still comes first, the lock takeover times and the 100-version history limit. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Screens of v0.5.27 and this change on DSH 0.2.0-rc.2 with two instances syncing through one repository, in Chinese and English; the Host edge cases with the 16 tests that fail on main's Host sources; the review follow-up; and the verify, verify:plugins and three-host end-to-end results for 95bd46c. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Backup history and Check remote say which computer a version came from, and that name was the host name an identity was minted with, frozen there and changeable only by editing state/machine.json. Advanced now holds This computer's name: Host name follows the computer's current host name, and Custom keeps a typed name of up to 120 characters on one line. The name travels through the sync configure call, is checked before anything in the same request is written, and is reported in status with its source and the host name. machine.json records labelSource. A record from an earlier version whose label is still the host name keeps following it; any other label was set by hand and stays. The store reads the file on every call, so a rename reaches the pack manager's stamps at once, and the next sync publishes the new name in a version of its own. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The after screens show the name under Advanced and in the disclosure summary, the record counts the new tests (30 page cases, the machine identity cases) and the 64 end-to-end checks on three DSH releases, and verify and verify:plugins are recorded for 8a73ab0. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
12 of 28 tasks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
摘要 / Summary
优化 Git 同步的操作体验与界面,并修复一批边界问题。界面按“连接仓库 → 立即同步 → 自动同步 → 备份历史 → 高级设置”的顺序重建为一张卡片:用 GitHub 账号(一键复制设备码并打开 GitHub,可搜索/新建私有仓库,选公开仓库前确认)或地址连接;立即同步一步完成合并与推送,检查远端先说明会发生什么且不写入;失败用页面语言说明原因并给出修正入口。Host 端修复:换到另一个仓库或远端分支被删后不再重新发布旧历史;并发推送被拒时自动合并重试一次,同一版本再次合并不再多出副本;高级设置里可以给这台电脑命名(默认用主机名),备份历史与检查远端用它标明版本来自哪台电脑;中途停止留下的锁可被接管;Git 2.20–2.27 可初始化镜像;本机无 Git 身份时使用通道身份;令牌不再跟随仓库到其他主机;状态探测 20 秒超时;失败原因以
details.reason传给页面。Rebuild the Git sync block around what a person does, and fix the Host edge cases found while running it. The block is now one card in the order it is set up — Repository, Sync, Automatic sync, Backup history, Advanced. A repository connects through a GitHub sign-in (one click copies the device code and opens GitHub; then search, pick or create a private repository, with a confirmation before a public one) or by an address checked before it is sent. Sync now merges and pushes in one step, Check remote says what a sync would change and writes nothing, and a failure is said in the page's language with the way to fix it. On the Host, switching to another repository or syncing after the remote branch was deleted no longer republishes earlier history; a push raced by another machine is merged and retried once, and merging the same version again adds no second copy; Advanced names this computer (its host name by default, or a custom name), which backup history and Check remote show; an abandoned lock no longer blocks every later sync; the mirror initializes on Git 2.20–2.27; a blank author on a machine without a Git identity uses the channel identity; a saved token no longer follows the repository to another host; the status probe gives up after 20 s; and failures reach the page with a
details.reason.关联 Issue 或背景 / Related Issue or Context
N/A. A maintainer-requested follow-up to the Git sync feature from #335 (released in v0.5.26): its operation, UI/UX consistency, flow and edge cases. The Host defects fixed here were found while running it and are each covered by a test that fails on
main.涉及区域 / Affected Areas
PR 类型 / PR Type
变更内容 / Changes
Host (
src/host/git-sync.ts,pack.ts,auto-backup.ts,github-auth.ts,rpc.ts,protocol.ts, newsync-error.ts):mnemon.originin its Git config) and is rebuilt for another one; a branch the remote does not hold starts a new history. Before, the first push to an empty repository, or after the remote branch was deleted, published the earlier history there.pack.ts, which ZIP import shares).owner.json). A lock whose process is gone is taken over at once, one an older version left without a record after 15 minutes, any lock after an hour, each renamed aside before removal. One-command credential files a stopped Host left are removed.git initno longer passes--initial-branch(Git 2.28), so the declared Git 2.20 floor holds. A blank author uses this computer's configured identity, else the channel identity, instead of failing or publishing one Git guessed.MNEMON_SYNC_STATUS_TIMEOUT_MS) and names why a remote could not be read (issue: auth, not-found, network, timeout). A refused operation carries a fixed category indetails.reason(MnemonSyncError).status.lastRun(state/sync/last-run.json) keeps the last sync for the page. The preview counts payload files only; compaction sizes are bytes; a push reads the remote once per attempt.github-unauthorized. Automatic sync keeps the reason of its last failure.machine-identity.ts):state/machine.jsonrecordslabelSource. A host-name label follows the current host name, and a custom one stays as typed;configuretakesmachineLabel(a string, ornullfor the host name), checked before anything in the request is written, andstatus.machineLabelreports the source and the host name. The store re-reads the file on every call, so a rename reaches the pack manager's stamps at once.Page (
src/client/MnemonSyncSection.tsx, newMnemonSyncDialogs.tsxandsync-view.ts,locales.ts,MnemonSettingsCard.module.css):Docs:
docs/{en,zh-CN}/guides/operations.md(Git repository sync, what the channel guarantees, backups and differences), the UI guides, README,configuration.md,storage-model.md; changesetdsh-mnemon: patch.Tests:
tests/git-sync.spec.ts,pack.spec.ts,sync-rpc.spec.ts,github-auth.spec.ts,machine-identity.spec.ts, newtests/client-sync.spec.tsx(30 cases),client-memory-enhancements.spec.tsx, the copy fingerprints;scripts/verify-sync-git.mjschecks the reasons and the computer's name through real DSH (64 checks).最新代码确认 / Latest Codebase Confirmation
main分支开发,或在提交前已 rebase 或合并最新main。 / I developed from the latestmain, or rebased or merged the latestmainbefore submitting.同步命令 / Sync command:
Based on
main1a6ff881(v0.5.27).AI 编码披露 / AI Coding Disclosure
使用的 AI 模型 / AI model used: Claude Opus 5.5 (
claude-opus-5-5)使用的编码 Agent 工具 / Coding Agent tool used: Claude Code
仓库规范检查 / Repository Rules
@deepseek-ai/*NPM 契约。 / I did not modify DSH source or point tsconfig at a DSH source checkout, and used only published@deepseek-ai/*NPM contracts.src/host/protocol.ts为准,没有在两侧重复定义 wire DTO。 / The Client and Host boundary still uses browser-safesrc/host/protocol.tsas the source for wire DTOs.lib/文件。 / I did not commit tokens, credentials, private memory, unredacted logs, or generatedlib/files.Credentials: a saved token no longer follows the repository to another host (before, the next status probe and push handed it to the new host); the GitHub grant is still used only for
github.comover HTTPS;MNEMON_SYNC_GIT_TOKENstill comes first; tokens are never echoed (tests/sync-rpc.spec.ts); one-command credential files left by a stopped Host are removed.details.reasonis a fixed category name, never Git's text or a credential. The lock takeover and the mirror rebuild touch onlystate/sync. No RPC method or permission is added.兼容性与数据安全 / Compatibility and Data Safety
latestandnext) and 0.2.1-alpha.2; no DSH contract changes.autoBackupMinutesto 0.state/machine.jsongainslabelSource, which earlier versions ignore. A record without it whose label is still the host name keeps following the host name; any other label was set by hand and stays. A renamed computer's next sync records the new name in a version of its own; earlier versions keep theirs.mnemon.originand is rebuilt from the remote on the first sync after the upgrade. It only ever held a copy of the remote branch (each sync reset it to the fetched tip), so nothing is lost; the cost is one full fetch.state/sync/last-run.jsonis new and optional.mnemon.origin,last-run.jsonand the lock'sowner.json; a lock left by this version is treated as before.本地验证 / Local Validation
执行的命令 / Commands run:
结果摘要 / Result summary:
pnpm run verifyon a clean checkout of8a73ab09(Node 24.19.0, pnpm 11.25.0, frozen lockfile, pinned DSH 0.1.7-rc.2): passed. 132 root test files passed and 2 skipped (1,880 tests passed, 6 skipped); Headless 37 tools with 8 Mnemon tools; package 59 files, 1,910,270 unpacked bytes.pnpm run verify:plugins: 17 plugin repositories and 18 packed artifacts passed.scripts/verify-sync-git.mjs, two DSH instances, a real bare repository): 64 of 64 checks on DSH 0.1.7-rc.2, 0.2.0-rc.2 and 0.2.1-alpha.2.main: withmain's Host sources under this branch's tests, 16 of 106 Host cases fail; all pass here. The list is inverification.json.verify:docswith the evidence commit65f3151a: passed.tests/github-auth.spec.tsand page tests with a fake Host); Windows and Linux; the DSH desktop app.用户可见变更证据 / Local Feature Evidence
证据 / Evidence: Git sync record · 中文 · verification.json
The record also shows the address check, backup history, advanced settings with this computer's name, the disconnect confirmation and the English page. The screens use a demonstration repository and a demonstration token; no real credential, private memory or local path appears.
🤖 Generated with Claude Code