Skip to content

1Crazy/git-tools

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

2 Commits
 
 
 
 

Repository files navigation

跨仓库目录/文件同步脚本使用说明

1. 这个脚本现在支持什么

sync.js 现在支持两种源路径:

  • 同步整个目录
  • 同步单个文件

也就是说,--source 不再只支持目录,它现在支持:

  • 远端目录 → 本地目录
  • 远端单文件 → 本地单文件
  • 远端单文件 → 本地目录

并且如果是文本冲突,脚本会尽量像 Git 一样,直接把冲突标记写进目标文件:

<<<<<<< LOCAL:...
你的本地内容
=======
远端内容
>>>>>>> REMOTE:...

2. 单文件模式怎么理解

这是你刚刚提到的重点:

只拉取某个文件也是可以的。

现在脚本已经按这个思路支持了,而且规则比较明确。

2.1 单文件同步到另一个文件

如果 --source 是单文件,且 --target 不是已存在目录,也不是以 / 结尾,那么:

  • --target 会被当成最终文件路径
  • 这意味着你可以把远端单文件同步到本地不同名字的文件

例如:

node sync.js \
  --repo template-remote \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/app-theme.ts

这表示:

  • 远端是 packages/config/src/theme.ts
  • 本地目标是 projects/sandbox/src/ui/theme/app-theme.ts
  • 文件名可以不同

2.2 单文件同步到某个目录

如果 --source 是单文件,且 --target 满足下面任意一个条件:

  • --target 本身就是一个已存在目录
  • --target/ 结尾

那么脚本会:

  • --target 当成目录
  • 保留远端原文件名,放进这个目录里

例如:

node sync.js \
  --repo template-remote \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/

结果会落到:

projects/sandbox/src/ui/theme/theme.ts

3. 目录模式怎么理解

如果 --source 是目录,那么 --target 也按目录处理。

例如:

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch main \
  --source packages/widgets/src/elements \
  --target projects/sandbox/src/ui/elements

这表示把远端 packages/widgets/src/elements 下面的内容,同步到本地 projects/sandbox/src/ui/elements

4. 同步规则

脚本执行时,按下面的规则处理:

  1. 远端有、本地目标里没有的文件:直接新增
  2. 远端有、本地也有,而且内容完全一样:跳过
  3. 远端有、本地也有,而且都是文本文件:
    • 能自动合并的:直接自动合并
    • 不能自动合并的:直接把 <<<<<<< / ======= / >>>>>>> 写进目标文件
  4. 如果遇到结构冲突,脚本不会乱写内容,而是只在控制台和报告里提示你手动处理。
  5. 如果遇到二进制文件冲突,脚本会生成远端副本,并把副本路径写到控制台和报告里。

5. 一个重要细节:脚本会在 .git 里保存上次同步的远端版本

为了让后续同步更像真正的“三方合并”,脚本会把“上一次成功看到的远端版本”保存在:

.git/sync-state/

注意:

  • 这个目录在 .git 里面
  • 不会出现在你的工作区文件列表里
  • 不会被 Git 当成项目代码变更

这样第二次、第三次同步时,冲突判断会比“纯两份文件硬比”更准确。

6. 使用前准备

请先确认以下几点:

  • 你当前在这个仓库根目录:/
  • 你的 Node 版本是 >= 20.10.0
  • 你对远端仓库有拉取权限
  • 最好先把当前仓库代码提交一次,或者至少 git status 看一下,确认没有重要未提交改动

如果你当前工作区有未提交改动,脚本默认会拦住你,避免误操作。

7. 最常用的命令

7.1 先预览,不真正写文件

目录模式预览:

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch main \
  --source packages/widgets/src/elements \
  --target projects/sandbox/src/ui/elements \
  --dry-run

单文件模式预览:

node sync.js \
  --repo template-remote \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/theme.ts \
  --dry-run

7.2 确认没问题后,正式执行

目录模式:

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch main \
  --source packages/widgets/src/elements \
  --target projects/sandbox/src/ui/elements

单文件模式:

node sync.js \
  --repo template-remote \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/app-theme.ts

执行完成后,脚本会输出:

  • 源路径类型是目录还是单文件
  • 新增了多少文件
  • 跳过了多少相同文件
  • 自动合并了多少文件
  • 写入了多少个 Git 风格冲突标记
  • 生成了多少个二进制冲突副本
  • 有多少个文件需要你额外手动处理

7.3 直接填写 DEFAULT_CONFIG 的使用方式

如果你不想每次都在命令行里写一长串参数,也可以直接修改 sync.js 顶部的默认配置:

const DEFAULT_CONFIG = {
  repo: "git@example.com:demo/blueprint-kit.git",
  branch: "main",
  sourceDir: "packages/widgets/src/elements",
  targetDir: "projects/sandbox/src/ui/elements",
  reportFile: "sync-report.json",
  failOnDirty: true,
};

改完后,直接运行:

node sync.js

这样脚本就会直接使用你写在 DEFAULT_CONFIG 里的值,不需要再额外传:

  • --repo
  • --branch
  • --source
  • --target

这种方式适合什么场景

这种方式比较适合:

  • 你经常同步同一个远端仓库
  • 你经常同步同一个分支
  • 你经常同步固定的源目录 / 文件 到固定目标位置

如果你的同步目标比较固定,这样会比每次手输命令方便很多。

命令行参数和 DEFAULT_CONFIG 谁优先

如果你既写了 DEFAULT_CONFIG,又在命令行里传了参数,那么:

命令行参数优先级更高。

例如你在 sync.js 里写了:

const DEFAULT_CONFIG = {
  repo: "git@example.com:demo/blueprint-kit.git",
  branch: "main",
  sourceDir: "packages/widgets/src/elements",
  targetDir: "projects/sandbox/src/ui/elements",
  reportFile: "sync-report.json",
  failOnDirty: true,
};

但你实际执行:

node sync.js --branch release --source packages/widgets/src/advanced

那么本次实际生效的是:

  • repo: 用 DEFAULT_CONFIG.repo
  • branch: 用命令行里的 release
  • sourceDir: 用命令行里的 packages/widgets/src/advanced
  • targetDir: 用 DEFAULT_CONFIG.targetDir

单文件模式怎么写

如果你要同步的是单个文件,也可以直接这样写:

const DEFAULT_CONFIG = {
  repo: "template-remote",
  branch: "release",
  sourceDir: "packages/config/src/theme.ts",
  targetDir: "projects/sandbox/src/ui/theme/app-theme.ts",
  reportFile: "sync-report.json",
  failOnDirty: true,
};

然后执行:

node sync.js

推荐使用顺序

如果你采用 DEFAULT_CONFIG 方式,我建议还是按这个顺序:

  1. 先把配置写进 sync.js
  2. 先执行一次:
node sync.js --dry-run
  1. 确认输出没问题,再正式执行:
node sync.js

这样更稳。

8. 参数说明

必填参数

  • --repo

    • 远端 Git 地址。
    • 也可以写成你已经配置好的 remote 名,比如 template-remote
  • --source

    • 远端仓库里要取的路径。
    • 支持目录,也支持单个文件。
    • 例如:packages/widgets/src/elements
    • 例如:packages/config/src/theme.ts
  • --target

    • 当前仓库里要合并到的路径。
    • 支持目录,也支持单个文件。
    • 如果 --source 是单文件,--target 可以是不同名字的文件。

可选参数

  • --branch

    • 远端分支名。
    • 默认值:main
  • --dry-run

    • 只预览,不真正写文件。
    • 新手第一次务必先用这个。
  • --allow-dirty

    • 就算当前仓库有未提交改动,也允许继续执行。
    • 不推荐新手直接用
  • --report

    • 指定运行报告输出路径。
    • 默认值:sync-report.json
    • 必须是当前仓库内的相对路径。
  • --help

    • 查看帮助。

9. 冲突出现后你怎么处理

如果某个文件无法自动合并,正式执行后,它会直接变成这样:

<<<<<<< LOCAL:projects/sandbox/src/ui/elements/button.ts
const title = '本地版本';
=======
const title = '远端版本';
>>>>>>> REMOTE:packages/widgets/src/elements/button.ts

你要做的是:

  1. 打开这个文件
  2. 看清楚 <<<<<<<======= 是本地部分
  3. 看清楚 =======>>>>>>> 是远端部分
  4. 手动整理成你最终想保留的内容
  5. 删除冲突标记行
  6. 保存文件

处理完之后,文件里就不应该再有:

<<<<<<<
=======
>>>>>>>

10. 哪些情况不会直接写冲突标记

有两类情况,脚本不会直接往目标文件里写冲突标记:

10.1 结构冲突

例如:

  • 远端希望写入:a/b.ts
  • 但你本地目标里 a 已经是一个文件,不是目录

这种情况没法正常往目标文件里写入冲突标记,所以脚本只会提示你手动处理。

10.2 二进制文件冲突

例如图片、字体、某些非文本资源文件。

这类文件不适合插入 <<<<<<< 标记,所以脚本不会往原文件里乱写冲突标记,而是会额外生成一份远端副本。

默认情况下,副本会优先生成在目标文件旁边,例如:

logo.png.sync-binary-incoming

如果目标位置旁边没法安全落副本,脚本会退回到:

.sync-conflicts/

同时它也会:

  • 在控制台里提示副本路径
  • sync-report.jsonbinaryCopies 字段里记录下来

你后续只需要自己对比:

  • 原目标文件
  • 新生成的二进制副本

确认怎么保留,然后再手动处理即可。

11. 运行报告会生成什么

正式执行后,默认会在仓库根目录生成:

sync-report.json

这个文件里会记录:

  • 运行时间
  • 远端仓库地址
  • 分支名
  • 源路径
  • 源路径类型(目录 / 单文件)
  • 目标路径
  • 新增文件列表
  • 自动合并文件列表
  • 写入冲突标记的文件列表
  • 生成的二进制副本列表
  • 需要额外手动处理的文件列表

如果控制台输出太多,直接看这个报告就行。

12. 推荐的标准操作流程

建议你每次都按下面流程走:

第一步:先看当前仓库状态

git status

如果有很多未提交改动,先提交或暂存。

第二步:先预览

node sync.js --repo <你的git地址> --branch main --source <远端路径> --target <本地路径> --dry-run

第三步:正式同步

node sync.js --repo <你的git地址> --branch main --source <远端路径> --target <本地路径>

第四步:搜索冲突标记

执行完成后,可以全局搜索:

<<<<<<<

找到所有需要你手动处理的文本冲突。

第五步:查看报告里剩余的 manual

如果报告里还有 manual 列表,说明这些不是普通文本冲突,脚本没法直接帮你插标记,需要你自己判断怎么处理。

第六步:检查变更

git status
git diff

第七步:本地验证后提交

按你的实际项目流程做验证,然后再提交代码。

13. 常见报错说明

13.1 缺少必要参数

说明你没有传完整参数,至少需要:

  • --repo
  • --source
  • --target

13.2 当前仓库存在未提交改动

说明脚本检测到你本地有未提交代码。

处理方式:

  • 推荐:先提交或暂存
  • 不推荐但可用:追加 --allow-dirty

13.3 远端分支中没有找到路径

一般有三种可能:

  • --source 路径写错了
  • --branch 写错了
  • 你拉的那个仓库/分支里确实没有这个路径

13.4 git fetch 失败

通常是:

  • 远端仓库地址写错
  • 你没有权限
  • SSH key / Token 没配置好

先单独试一下:

git ls-remote <你的git地址>

如果这一步都不通,先把 Git 权限问题解决掉。

13.5 --target / --report 不能包含 ..

这是脚本的安全保护。

因为目标路径和报告文件都要求位于当前仓库内,所以不允许写类似下面这种路径:

../outside-dir

14. 一个完整示例

示例 1:同步整个目录

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch release \
  --source packages/widgets/src/elements \
  --target projects/sandbox/src/ui/elements

示例 2:只同步一个文件,并改名

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/app-theme.ts

示例 3:只同步一个文件到某个目录

node sync.js \
  --repo git@example.com:demo/blueprint-kit.git \
  --branch release \
  --source packages/config/src/theme.ts \
  --target projects/sandbox/src/ui/theme/

15. 最后给你的建议

如果你是第一次用,强烈建议:

  1. git status
  2. 再执行 --dry-run
  3. 确认输出没问题后再正式跑
  4. 跑完后全局搜索 <<<<<<<
  5. 再看 sync-report.json 里的 binaryCopiesmanual
  6. 全部处理完再提交

这样最稳,也最接近你想要的 Git merge 体验。

16. 本地模拟脚本 sync-local.js

如果你只是想本地验证整条合并链路,不想走 git fetch,可以直接用:

node sync-local.js --source <本地源路径> --target <本地目标路径> [options]

这个脚本的特点:

  • 不依赖远端仓库
  • 不会走 git fetch
  • 复用 sync.js 的核心合并逻辑
  • 如果你传了 --base,它会按三方合并来模拟
  • 它不会把基线状态持久化到 .git/sync-state,每次运行都是独立模拟

16.1 用 sycn1.js 验证 sync.js 是否能 merge

你现在这个场景,推荐直接执行:

node sync-local.js   --source sycn1.js   --target sync.js   --base sync.js   --dry-run

这条命令的意思是:

  • sycn1.js 当成“新版本”
  • 把当前 sync.js 当成“目标文件”
  • 再把当前 sync.js 同时当成“基线版本”
  • 然后只做预览,不真正写文件

这非常适合用来验证:

  • 能不能自动 merge
  • 会不会出现 <<<<<<< 冲突标记
  • 二进制/结构冲突会怎么提示

16.2 如果你想模拟“首次同步”

不传 --base 就行:

node sync-local.js   --source sycn1.js   --target sync.js   --dry-run

这时脚本会按“没有历史基线”的方式来模拟。

About

git tools node script

Resources

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors