2026-07-30
今日主题
- git 集成命令语义
- 命令行 synopsis 语法
- Git 提交对象与身份语义
- Git 安全强推与 CAS
- npm 命令别名与执行引擎边界
- 桌面 OS 应用生命周期模型
新增认知
git 集成命令语义
-
merge 无 rebase 开关:git merge 没有"切换成 rebase"的参数,merge 就是 merge;
rebase 的开关在 git pull 上(--rebase/--no-rebase),不在 merge 上。
merge 自己只控制 fast-forward:--ff(默认,
能快进只移指针否则建 merge commit)、--no-ff(强制 merge commit)、--ff-only(不能快进就报错退出)。
想线性历史要用 rebase,而非找 merge 的 rebase 参数。 -
rebase 与 -i 同源:
交互式 rebase git rebase -i 和普通 rebase 是同一条命令同一种底层机制(逐条 pick 重放),
差别只在是否弹编辑器让你改每个提交的动作表。指令表默认全 pick,可改成 reword/edit/squash/fixup/drop/重排。
普通 rebase 只想线性接上游;-i 还想整理提交(合并/改消息/拆分/丢弃)。 -
rebase 两 ref 角色不对等:
git rebase [[ ]] 中 upstream 是新基/落点、branch 是被搬端(默认 HEAD),
重放范围是 upstream..HEAD(可达自 HEAD 但不可达自 upstream 的提交)。upstream 不必是 HEAD 祖先但须有共同祖先,
否则报错(无 --allow-unrelated-histories)。upstream..HEAD 为空时等于快进。
命令行 synopsis 语法
- 嵌套方括号表顺序依赖:命令用法里 [A [B]] 的嵌套方括号:外层可选、内层也可选但内层依赖外层已给出。原因是位置参数按顺序填位,
不能跳过 A 单独给 B。对比 [A] [B] 两个独立方括号则各自独立可选。读 synopsis 时嵌套深度即依赖链。
Git 提交对象与身份语义
-
提交保存快照:Git 的 commit object 不直接保存 diff,而是引用一次完整目录快照的 tree、一个或多个 parent,
并记录 author、committer、时间和提交说明。diff 是比较当前 tree 与 parent tree 时动态计算出来的,
因此不能把 Author 理解成 tree 或 diff 对象的作者。 -
身份职责分离:Author 表示这项改动最初由谁创作,Committer 表示谁在当前历史中创建或落下这个 commit。普通提交二者通常相同;
cherry-pick、rebase 或代他人合入时通常保留原 Author,同时由执行历史变换的人成为新的 Committer。 -
重放必生新提交:cherry-pick 是从旧 commit 及其 parent 计算补丁,把补丁应用到当前 HEAD,生成新 tree,
并以当前 HEAD 为 parent 创建新 commit。只要 tree、parent、身份、时间或提交说明任一字段变化,commit ID 就会变化;
即使 tree 相同,不同 parent 也会生成不同 commit,但 Committer 不要求必须是不同的人。
Git 安全强推与 CAS
-
隐式 Lease 即 CAS:
不带参数的 --force-with-lease 会把本地 remote-tracking ref(例如 origin/develop)记录的 object ID 作为 expected value。
只有远端目标 ref 仍等于该值时,Git 才将它更新为本地分支的新 commit;否则以 stale info 拒绝,
因此本质是针对远端 ref 的 compare-and-swap。 -
显式 Hash 更严格:默认 lease 的 expected value 会随本地 fetch 更新,
后台工具或其他终端执行 fetch 后可能改变保护基准。
高风险历史重写可使用 --force-with-lease=refs/heads/develop:固定比较值,
使远端 ref 只有仍等于明确的旧 commit 时才能更新;前提是 expected hash 已通过可信 fetch 或远端核验获得。
npm 命令别名与执行引擎边界
-
三层执行模型:npm 这组命令表面都是"跑点什么",实际分三层——npm run 只执行 package.json 里预写的脚本字符串,不获取任何包;
npm exec 是唯一真正"跑包"的执行引擎,本地已装则直接执行,未装则临时从 registry 拉取;
npm init/create 和 npx 都是 npm exec 的外壳,但方式不同:
init/create 是把命令编译时转换成对应的 npm exec create-调用,
npx 则是与 npm exec 共享同一套底层逻辑的独立入口(并列实现而非转换),这也是二者参数解析规则会出现差异的原因。 -
npm exec 才是跑包引擎:npm exec 允许执行来自 npm 包的任意命令,无论该包已本地安装还是需要临时从 registry 拉取;
这是它与 npm run 的根本区别——npm run 不做任何下载/安装动作,
只是把 node_modules/.bin 加入 PATH 后执行 scripts 字段里预写的命令,本身不涉及包的获取。 -
npx 与 npm exec 的参数解析差异:
npx 把位置参数之后的 flag 直接转发给目标命令(如 npx foo bar --package=x 会把 --package=x 传给 foo);
npm exec 默认会自己解析这些 flag 而不转发,除非用 -- 分隔(npm exec -- foo bar --package=x)。底层引擎相同,
但不加 -- 时行为可能不同,这是判断两者是否"等价"的关键边界条件。 -
npm init/create 是编译时转换而非并列实现:
npm init foo 在执行前会被 npm 直接转换成 npm exec create-foo(作用域包则是 @usr/create-foo),
本质是给 npm exec 加了一层命名规则(create-前缀)和语义包装,不是像 npx 那样的独立并列实现。 -
install 与 ci 是两套不同目标的机制:npm install 与 npm ci 不是别名关系,而是面向不同场景的安装机制——
install 面向日常开发,允许增删包并可能改写 lock 文件;ci 面向可复现构建,严格按 lock 文件安装,安装前清空 node_modules,
且 lock 与 package.json 不一致时直接报错退出,不会自动修复或改写 lock。 -
内置脚本别名的兜底例外:npm start/test/stop/restart 本质是 npm run start 等的别名,
但当 package.json 未定义对应 scripts 字段时,npm start 有内置默认兜底行为(如尝试执行 node server.js),
而自定义 script 名称(如 npm run build)没有这种兜底,未定义就直接报错——这是"别名"外表下隐藏的一条例外规则。
桌面 OS 应用生命周期模型
-
App-centric 与 Window-centric 的本质分野:macOS 与 Windows 对"关窗"的语义完全不同——
macOS 是 App-centric,App 是运行实体,Window 只是视图之一,
关窗后 App 仍存活(Dock 图标是"App 还活着"的精确语义而非残留);Windows 是 Window-centric,
App 与 Window 强绑定,最后一个窗口关闭即视为退出。判断标准:"App 关闭所有窗口后是否还有存在的理由"——能继续播放音乐/收邮件/跑下载的,
归前者。Spotify、Mail、Docker 关闭窗口后仍在工作,是 macOS 模型的一等公民;
Windows 下要做类似的事得塞进"系统托盘"这个补丁机制。 -
macOS 选 App-centric 的具体考虑:(1)"我可能还要用"假设——关窗只是暂时不需要看,
留在内存里 Cmd+Tab / 点 Dock 可瞬时唤起,免冷启动;(2)后台工作是合理的一等需求——
邮件、网盘、剪贴板、输入法、下载工具的核心价值就是"没窗口也要工作",如果关窗=退出,这类 App 就不存在;(3)多窗口是常态——
一个 App 多个文档窗口(Pages 同时开 5 个文档)很常见,关一个不该影响其他;(4)Unix 血统——传统 Unix 进程本就长存,
退出需显式 exit;(5)明确代价是内存常驻,macOS 接受此 trade-off 换响应速度与持续能力。 -
Windows 选 Window-centric 的历史与权衡:(1)早期内存稀缺年代的务实选择,跑不动多个常驻 App;
(2)单窗口应用占多数(记事本、计算器、资源管理器),关窗=退出自然合理;(3)任务栏宽度有限,每开窗口占一格,常驻 App 占位问题严重;
(4)"用完即走"的心智模型符合很多用户直觉。代价是后台任务沦为特殊需求,必须靠托盘或注册成服务实现,而托盘又被广泛吐槽(视觉杂乱、隐私泄露隐患)。 -
两套系统正互相借鉴:现代 macOS 对常驻 App 越来越严——App Nap、内存压缩、强制签名、能耗监控,
本质在抑制 App-centric 的内存成本;现代 Windows 越来越像 macOS——UWP/WinUI App 倾向 macOS 模型,
Spotify 关窗后台播放、Edge 关窗后台下载,Windows 11 的小组件与 Copilot 也是后台常驻形态。"窗口即应用"的边界正在模糊,
但底层心智模型差异仍会影响日常开发与用户习惯。