git-sync ======== 本项目使用 git 命令来实现文件同步, 该实现有如下特点: 1) 每个参与同步的设备都维护一个 git 库用来记录同步目录的修改, 默认只使用 master 一个分支. 2) 每个参与同步的设备通过 git 库中的远程分支 (refs/remotes/*) 来追踪其他设备的修改. 假设有 HostA, HostB, HostC 三个设备, 那么在 HostA 维护的 git 库中有如下分支. $ git show-ref 001724cbae16cfb822e94e4128f8892bc5ac41f9 refs/heads/master d7932d21c16a96eaa596e9ced8167ca0b978929f refs/remotes/HostB/master f579103238cb37d8858aed63fecc1f4d923ee698 refs/remotes/HostC/master 3) 如果某个设备对同步目录做了修改, 那么它会将这些修改 commit 到自己维护的 git 库的 master 分支上. 但是该设备不会主动通知其他设备自己产生了新的修改. 4) 每个设备定时询问其他设备是否产生了新的修改. 在 (2) 中的例子里, 如果 HostA 询问 HostB 是否有新的修改时, 会向 HostB 发出一条类似这样的请求 "SYNC d7932d21c16a96eaa596e9ced8167ca0b978929f". 请求中的 d7932d2 告诉 HostB, HostA 已经拥有 HostB 上截止到 d7932d2 的所有修改. 5) 一个设备收到其他设备发来的 "SYNC <SHA1>" 请求时, 它会将 <SHA1>..<refs/heads/master> 之间的所有 commit 导出到一个 *.bundle 文件中 (使用命令 git bundle create 生成), 并将该文件回复给发出请求的设备. 这样就实现了 简单的增量同步. 6) 一个设备收到其他设备回复的 *.bundle 文件后, 它首先将该文件导入到本地的远程分支 (refs/remotes/*) 上, 然后尝试将远程分支 merge 到自己的 git 库的 master 分支. 如果没有出现任何冲突, 那么这次同步就成功了. 如果 merge 过程中出现冲突, 我们使用一个简单且易于实现的自动解决方法. 假设出现冲突的文件名为 my_file.txt, 那么 a) 将本地原有的 my_file.txt 文件重名为 my_file.version-<tag1>.txt, 其中 <tag1> 是使用命令 git hash-object 获得的一个哈希值. b) 将取自 *.bundle 的 my_file.txt 文件重名为 my_file.version-<tag2>.txt, 其中 <tag2> 是使用命令 git hash-object 获得的一个哈希值. c) 然后将 my_file.version-<tag1>.txt 和 my_file.version-<tag2>.txt 加入本地维护的 git 库中, 原来的 my_file.txt 则从 git 库中删除. d) 使用 (a)-(c) 处理所有的冲突文件后, 对本地 git 库做 commit 操作, 于是本次同步结束. 类似的策略使用在 git-annex 中, 参见链接 http://git-annex.branchable.com/automatic_conflict_resolution/ http://git-annex.branchable.com/design/assistant/blog/day_18__merging/ TODO List ========= 1) 删除较老的 commit 历史 (snapshot and cut history commits). 2) 文本文件: merge or not merge