
1. 多机共用一个账号的真实需求拆解1.1 为什么会有“多机共用一个 WorkBuddy 账号”这种场景先说清楚这个场景是怎么来的。WorkBuddy 这类工具的核心价值在于它会在本地维护一份持续演进的“工作记忆”——包括你积累的 skill、项目上下文、缓存索引、会话历史等等。很多人一开始只在公司台式机上用后来出差带笔记本、家里还有一台备用机就自然产生了“同一个账号多台机器都要能用”的需求。最直觉的做法当然是每台机器各自登录同一个账号让云端去同步。但实际用下来你会发现两个问题第一云端同步有延迟而且部分本地缓存目录、skill 的中间产物并不在同步范围内第二有些团队是共享一个“团队账号”来跑自动化任务多台机器同时读写同一份工作区冲突几乎是必然的。所以真正靠谱的方案是把 WorkBuddy 的工作目录本身做成一个Git 裸仓 多机双写同步的结构让每台机器既是消费者也是生产者。这里的关键词是双写同步和commit-aware。所谓双写就是两台机器都能往同一个仓库写所谓 commit-aware就是同步逻辑要能感知到 commit 的边界而不是简单粗暴地覆盖文件。这套思路本质上和 Git 本身的工作流是一致的只是 WorkBuddy 的工作目录里有很多“不该进版本库”的东西需要精细地做过滤。1.2 这个方案到底解决了什么问题我把这套方案要解决的问题归纳成四条你可以对照自己的情况看看是否命中账号记忆不丢失换机器后之前积累的 skill、项目配置、缓存索引能完整带过去而不是从零开始。多机并行不打架两台机器同时改不同文件时能自动合并改同一个文件时能明确报冲突而不是静默覆盖。历史可回溯任何一次同步都是一个 commit出问题可以回滚到任意时间点。不依赖云端整个同步链路走的是你自己的 Git 裸仓数据完全可控。适合谁来参考如果你符合下面任意一条这篇内容就对你有用经常在两台以上机器之间切换的 WorkBuddy 用户团队里共享一个账号跑任务的运维或开发对数据可控性有要求、不想完全依赖云端同步的人。1.3 整体架构长什么样在动手之前先把架构在脑子里过一遍不然后面配 Git 的时候容易迷路。整个结构是三层第一层是裸仓bare repo放在一台常开的机器上或者你自己的服务器上。它没有工作区只存 Git 对象是所有机器同步的“中转站”。第二层是各台机器上的工作副本也就是 WorkBuddy 实际运行的工作目录每台机器各自 clone 一份。第三层是同步脚本负责在合适的时机执行 pull、commit、push并且处理冲突。裸仓的好处是它不会因为某台机器关机而消失也不会出现“谁是主副本”的争论。每台机器都是平等的写入方裸仓只做汇聚。这个设计比“一台机器当主机、其他机器当从机”要稳得多因为主机一挂全盘皆输。提示裸仓所在机器最好是有固定内网地址或者你能稳定访问的地址否则每次同步都要改 remote很烦。2. 核心细节解析与实操要点2.1 裸仓的创建与初始化裸仓的创建本身很简单但有几个细节决定了后面顺不顺。先在一台常开的机器上执行mkdir -p /srv/workbuddy-sync.git cd /srv/workbuddy-sync.git git init --bare--bare是关键它表示这个仓库没有工作区只接受 push 和提供 clone。创建完之后建议立刻设置一下默认分支名避免不同机器 Git 版本默认分支不一致导致 push 失败git symbolic-ref HEAD refs/heads/main这一步很多人会忽略结果一台机器默认master、一台默认mainpush 上去变成两个分支同步就乱了。我踩过这个坑排查了半天才发现是分支名不一致。接下来是权限问题。如果裸仓放在共享服务器上要确保每台机器的 SSH 公钥都能访问。这里涉及ssh认证失败 git这个高频问题后面第 4 章会专门讲排查方法。初始化阶段先确认一件事裸仓目录的属主和权限要允许所有需要访问的机器读写否则 push 会被拒。2.2 WorkBuddy 工作目录里哪些该进版本库这是整个方案里最需要动脑子的部分。WorkBuddy 的工作目录通常包含几类内容内容类型是否进版本库原因skill 定义文件是核心资产必须同步项目配置是换机器后要复用会话历史视情况体积大可能含敏感信息缓存目录否可重建且体积巨大临时文件否无意义且频繁变动日志文件否频繁变动冲突高发判断标准其实就一条这个文件换台机器后能不能自动重建能重建的就不进版本库。缓存目录、索引文件、临时产物都属于这一类。而 skill、配置、自定义脚本这些是“人工积累”的必须进。具体操作上在 WorkBuddy 工作目录根下建一个.gitignore把缓存和临时目录排除掉。这里有个高频坑git 的过滤文件没有作用。原因通常是.gitignore只对“未跟踪文件”生效如果某个缓存目录之前已经被git add过那.gitignore对它无效。解决办法是先把它从索引里移除git rm -r --cached ./cache git rm -r --cached ./logs然后再确认.gitignore里已经写了对应规则。这个顺序不能反反了就是白忙活。2.3 commit-aware 同步的核心逻辑“commit-aware”这个词听起来玄乎其实说的是一件很朴素的事同步的时候不要按文件时间戳去判断谁新谁旧而是按 commit 来判断。因为多机双写场景下文件时间戳完全不可靠——A 机器时钟快、B 机器时钟慢或者某台机器刚同步完时间戳全变了都会导致误判。正确的做法是每次同步前先git fetch看看远端有没有新 commit。如果有先git pull --rebase把本地未推送的 commit 叠到远端最新之上如果没有直接 commit 本地改动再 push。整个流程用一句话概括就是“先拉后推拉的时候用 rebase 而不是 merge”。为什么用 rebase 不用 merge因为 merge 会产生额外的合并 commit多机频繁同步时这些合并 commit 会迅速堆积历史变得极其难看而且容易在下次同步时产生新的冲突。rebase 把本地改动“搬”到远端最新之上历史是一条直线清晰得多。git fetch origin git pull --rebase origin main # 处理可能的冲突 git add . git commit -m sync: $(date %Y%m%d-%H%M%S) git push origin main这段脚本就是同步的核心。注意 commit message 里带了时间戳方便回溯是哪次同步产生的。如果你用git commit --amend去改历史 commit要特别小心因为 amend 会改写 commit hash如果那个 commit 已经 push 过其他机器再 pull 就会冲突。已推送的 commit 不要 amend这是铁律。2.4 双写冲突的预防与处理双写冲突分两种一种是不同文件Git 能自动合并不用管另一种是同一文件的同一区域Git 会报冲突需要人工介入。预防冲突的核心思路是减少同一文件的并发写入。具体做法有几个把频繁变动的状态文件拆成“每台机器一份”比如state-$(hostname).json这样两台机器各写各的永远不会冲突把共享的只读配置和每台机器独有的配置分开存放对于必须共享又频繁变动的文件约定“只有一台机器负责写”其他机器只读。真遇到冲突了怎么办先别慌git status会告诉你哪些文件冲突了。打开冲突文件会看到、、标记。手动编辑保留正确内容删掉标记然后git add该文件再git rebase --continue。如果实在搞不定git rebase --abort可以回到 rebase 之前的状态重新想办法。注意冲突处理期间不要执行其他 Git 命令否则容易把仓库搞成中间状态恢复起来很麻烦。3. 实操过程与核心环节实现3.1 第一台机器的完整配置流程假设你已经在第一台机器上装好了 WorkBuddy工作目录是~/workbuddy。第一步是把这个目录变成 Git 仓库cd ~/workbuddy git init git branch -M main然后创建.gitignore把不该进版本库的排除掉。这一步要仔细宁可多排除也不要漏排除因为一旦大文件进了版本库后面清理起来非常痛苦。一个参考配置# 缓存与索引 cache/ index/ *.cache # 日志 logs/ *.log # 临时文件 tmp/ *.tmp *.swp # 机器独有状态 state-*.json写完.gitignore后先git status看一眼确认缓存目录没被跟踪。如果发现已经被跟踪了用前面说的git rm -r --cached移除。确认干净后做第一次提交git add . git commit -m init: workbuddy workspace接着关联裸仓并推送git remote add origin ssh://userhost/srv/workbuddy-sync.git git push -u origin main到这里第一台机器就配好了。注意 remote 地址用的是 SSH 协议因为后面要免密推送SSH 比 HTTPS 方便。如果你用的是 HTTPS每次 push 都要输密码多机同步会疯掉。3.2 第二台机器的接入与首次同步第二台机器的操作就简单多了直接 clonegit clone ssh://userhost/srv/workbuddy-sync.git ~/workbuddyclone 下来之后WorkBuddy 的工作目录就有了第一台机器的全部内容。但这时候有个细节要注意clone 下来的目录里没有缓存目录因为被.gitignore排除了WorkBuddy 第一次启动时会自动重建缓存。这是正常的不用手动去补。接下来配置免密推送。生成 SSH 密钥如果还没有的话ssh-keygen -t ed25519 -C workbuddy-sync然后把公钥加到裸仓所在机器的~/.ssh/authorized_keys里。这一步做完git push就不用输密码了。如果这一步报ssh认证失败 git先检查公钥有没有正确追加、权限是不是 600、裸仓机器的 SSH 服务有没有限制。这些排查方法在第 4 章展开。3.3 同步脚本的编写与定时触发手动敲同步命令太累写个脚本让它自动跑。脚本的核心逻辑就是前面说的“先拉后推”#!/bin/bash set -e cd ~/workbuddy # 拉取远端最新 git fetch origin main # 检查是否有本地改动 if ! git diff --quiet || ! git diff --cached --quiet; then git add -A git commit -m sync: $(hostname)-$(date %Y%m%d-%H%M%S) fi # rebase 到远端最新 git pull --rebase origin main # 推送 git push origin main这个脚本有几个设计考量。set -e保证任何一步失败就退出不会带着错误状态继续跑。先 commit 再 pull是因为 rebase 要求工作区干净如果有未提交的改动 rebase 会拒绝执行。commit message 里带了主机名和时间戳方便回溯是哪台机器哪次同步产生的。触发方式有两种一种是定时任务比如每 5 分钟跑一次另一种是手动触发在 WorkBuddy 关闭或者切换机器前跑一次。我个人的习惯是两者结合——定时任务兜底关键节点手动跑一次确保同步到位。定时任务用 cron 的话*/5 * * * * /home/user/sync-workbuddy.sh /home/user/sync.log 21日志重定向很重要不然出问题了没有任何线索。sync.log 里能看到每次同步的结果排查问题时非常有用。3.4 参数选择与性能权衡同步频率是个需要权衡的参数。频率太高比如每分钟一次Git 操作本身的开销会累积而且频繁产生小 commit历史很碎频率太低比如一小时一次两台机器之间的差异会很大冲突概率上升。5 分钟是个比较平衡的值实测下来既能及时同步又不会太频繁。另一个参数是 rebase 还是 merge。前面说了用 rebase但 rebase 有个代价如果本地有多个未推送的 commitrebase 会逐个重放冲突时可能要处理多次。如果你的同步频率足够高本地通常只有一两个 commitrebase 的代价可以忽略。所以高频同步 rebase 是配套的低频同步 merge 反而更合适。这个权衡要根据自己的使用习惯来定。还有一个容易忽略的点是大文件。WorkBuddy 的某些缓存或索引文件可能很大如果误入了版本库每次同步都要传输非常慢。除了用.gitignore排除还可以用git gc定期清理仓库里的冗余对象。如果已经误提交了大文件用git filter-repo清理历史但这个操作会改写所有 commit hash多机场景下要所有机器重新 clone代价很大所以预防比补救重要得多。4. 常见问题与排查技巧实录4.1 ssh认证失败 git 的排查路径这是多机同步里最高频的问题没有之一。表现是git push或git clone时报Permission denied (publickey)。排查按这个顺序来第一步确认本地有没有密钥。ls ~/.ssh/看看有没有id_ed25519和id_ed25519.pub。没有就生成一对。第二步确认公钥有没有加到裸仓机器。cat ~/.ssh/id_ed25519.pub复制内容追加到裸仓机器的~/.ssh/authorized_keys。第三步确认权限。本地~/.ssh是 700authorized_keys是 600权限不对 SSH 会直接拒绝。第四步用ssh -v userhost看详细日志日志里会明确告诉你卡在哪一步。我遇到过一次特别隐蔽的情况公钥加对了、权限也对但还是失败。最后发现是裸仓机器的 SSH 配置里限制了允许登录的用户而当前用户不在白名单里。这种问题只能看服务端日志/var/log/auth.log里会有记录。4.2 冲突频发怎么破如果同步日志里经常出现冲突说明双写策略有问题。先分析冲突集中在哪些文件上通常是状态文件、日志文件、索引文件这几类。针对性地处理状态文件改成每台机器一份用主机名区分。日志文件直接加进.gitignore不同步。索引文件如果 WorkBuddy 能重建就不同步不能重建就约定单机写入。还有一个技巧是缩小同步粒度。不要整个工作目录一起同步而是把“必须共享”的部分单独抽成一个子目录只同步这个子目录。这样冲突面大大缩小同步也更快。这个改造需要一点前期规划但长期收益很大。4.3 常见问题速查表问题现象可能原因解决方向push 被拒提示 non-fast-forward远端有新 commit本地没拉先 pull --rebase 再 push.gitignore 不生效文件已被跟踪git rm --cached 后重新提交同步后文件丢失误把重要文件排除了检查 .gitignore从历史恢复仓库体积暴涨大文件误入版本库git gc必要时 filter-repo分支名不一致不同机器默认分支不同统一 symbolic-ref 为 mainrebase 卡住有未提交改动先 commit 或 stash 再 rebase4.4 几个踩过的坑和独家心得第一个坑是时钟不同步。两台机器时间差了几分钟导致 commit 时间戳乱序rebase 时 Git 判断顺序出错。解决办法是确保两台机器都开了时间同步。这个坑很隐蔽因为平时感觉不到只有同步出问题时才会暴露。第二个坑是WorkBuddy 运行中同步。如果 WorkBuddy 正在写文件同步脚本同时 commit可能抓到写了一半的文件。解决办法是同步前先确认 WorkBuddy 处于空闲状态或者干脆在 WorkBuddy 关闭后同步。我现在的习惯是切换机器前先关 WorkBuddy再跑一次同步脚本确保状态干净。第三个心得是定期验证恢复流程。同步方案最怕的是“平时看着正常真要用的时候恢复不了”。我每个月会挑一台机器删掉工作目录重新 clone 一次验证能不能完整恢复。这个习惯帮我发现过好几次.gitignore配置错误导致的关键文件缺失。第四个心得是commit message 要规范。多机同步会产生大量 commit如果 message 都是乱写的回溯时根本找不到哪次改了什么。我现在的格式是sync: 主机名-时间戳-简要说明简要说明里写清楚这次同步涉及了什么改动比如“新增了 xx skill”。这样翻历史的时候一目了然。5. 方案扩展与长期维护建议5.1 从双机扩展到多机这套方案从两台机器扩展到三台、四台原理上完全一样都是 clone 裸仓、跑同步脚本。但机器多了之后有两个新问题一是冲突概率上升因为写入方变多了二是裸仓所在机器的负载上升如果所有机器都高频 push裸仓的磁盘 IO 会成为瓶颈。应对办法是分层同步。选一台机器作为“汇聚点”其他机器先同步到汇聚点汇聚点再同步到裸仓。这样裸仓的压力就小多了。或者干脆把裸仓放到性能更好的机器上磁盘用 SSD能扛住更高的并发。5.2 备份策略不能省裸仓虽然比单机副本稳但它本身也是单点。如果裸仓所在机器的磁盘坏了所有数据都没了。所以裸仓必须定期备份。最简单的办法是每天把裸仓目录打包一份存到另一块盘或者另一台机器上。Git 裸仓的备份很轻量因为它只存对象压缩后体积不大。备份之外还建议保留一份“冷副本”——就是某台机器上长期不动的完整工作目录。万一裸仓和备份都出问题冷副本还能救急。这个冷副本不需要参与同步定期手动更新一次就行。5.3 安全与权限的长期管理多机共享一个账号权限管理容易被忽视。裸仓的访问权限要定期审查离职或者换机器后及时移除对应的公钥。如果裸仓暴露在公网上务必限制访问来源只允许已知的 IP 段。SSH 服务本身也要加固禁用密码登录只允许密钥登录。WorkBuddy 的工作目录里可能包含敏感信息比如项目配置里的密钥、会话历史里的业务数据。这些内容进版本库之前要仔细评估。如果确实需要同步考虑用加密的方式存储或者把敏感部分单独抽出来不同步。这个判断没有标准答案取决于你的具体使用场景但一定要有意识地去评估而不是无脑全同步。5.4 后续可以怎么优化如果这套方案跑顺了还有几个优化方向。一是把同步脚本做成服务用 systemd 或者类似的机制管理比 cron 更可控能看到运行状态和日志。二是加一个简单的监控同步失败时发通知而不是等你自己去翻日志才发现。三是把裸仓的访问从 SSH 换成更轻量的协议减少握手开销但这个改动收益有限优先级不高。我个人在实际操作中的体会是这套方案的价值不在于技术多复杂而在于它把“多机共用账号”这个模糊需求拆解成了清晰的 Git 工作流。一旦拆解清楚剩下的就是配置和调优都是体力活。真正需要动脑子的是前期想清楚哪些文件该同步、哪些不该这个判断做对了后面就顺了。最后再分享一个小技巧同步脚本第一次跑之前先手动跑一遍每一步命令确认每步的输出都符合预期再把它写成脚本。这样能提前发现大部分配置问题比脚本跑失败了再回头排查要省事得多。