[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fJBAI0z6-ItaSqEFYFZhBsYpebkB0wOp1HKR961zw7gI":3,"$fJU-4tot_gC5fDkujNeoE-cGsdMy5V_KcdUXLuAnTFgw":14,"$fmr9orWNJaq7A7bjg-hJDi_YlTOqMacaYIczbinnFYFU":423},{"slug":4,"title":5,"description":6,"content":7,"content_html":8,"pub_date":9,"tags":10,"draft":13},"git-advanced-workflow","Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用","merge 会了，但 rebase 总搞错？bisect 找 bug 提交？interactive rebase 整理历史？这篇一次说清楚。","# Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用\n\n> Git 的基础操作网上到处是，但 rebase 踩坑、bisect 不会用、interactive rebase 懵逼才是大多数开发者的真实状态。\n\n## merge vs rebase 的本质区别\n\n**merge：** 创建一个新的合并提交，保留完整历史分支结构。\n\n```\n     A---B---C  feature\n    \u002F         \\\nD---E---F---G---H  main（merge 后）\n```\n\n**rebase：** 把 feature 上的提交一个个\"重放\"到 main 的最新提交之后，历史是线性的。\n\n```\n             A'--B'--C'  feature（rebase 后）\n            \u002F\nD---E---F---G  main\n```\n\n**选择逻辑：**\n\n| 场景 | 用哪个 |\n|------|--------|\n| 同步主线到 feature branch | `rebase` |\n| 合并 feature 到 main | 看团队约定 |\n| PR 合并 | `squash merge` 或 `rebase merge` |\n| hotfix 合并到多个分支 | `cherry-pick` |\n\n## rebase 正确使用\n\n### 基本流程\n\n```bash\ngit checkout feature\u002Flogin\ngit fetch origin\ngit rebase origin\u002Fmain\n\n# 如果有冲突：\n# 1. 解决冲突\n# 2. git add \u003Cresolved-files>\n# 3. git rebase --continue\n# 4. 如果想放弃：git rebase --abort\n```\n\n### rebase 的黄金禁忌\n\n**永远不要 rebase 已经 push 到远程的公共分支。**\n\n原因：rebase 会改变提交的 SHA，导致其他人的本地历史与远程不一致，造成灾难性的合并冲突。\n\n```bash\n# 危险操作（不要做）\ngit checkout main\ngit rebase feature\u002Fnew-api  # 改写了 main 的历史！\n\n# 安全操作\ngit checkout feature\u002Fnew-api\ngit rebase main  # 只改写自己的 feature 分支\n```\n\n## Interactive Rebase：整理提交历史\n\n```bash\n# 整理最近 5 个提交\ngit rebase -i HEAD~5\n```\n\n编辑器打开后每行代表一个提交：\n\n```\npick a1b2c3d 添加用户登录功能\npick e4f5g6h 修复登录 bug\npick i7j8k9l 添加单元测试\npick m1n2o3p 修改注释拼写\npick q4r5s6t WIP: 半成品代码\n\n# 命令说明：\n# pick   = 保留此提交\n# reword = 保留但修改提交信息\n# edit   = 暂停以修改内容\n# squash = 合并到上一个提交（保留提交信息）\n# fixup  = 合并到上一个提交（丢弃本提交信息）\n# drop   = 删除此提交\n```\n\n**squash 合并示例：**\n```\npick a1b2c3d 添加用户登录功能\nsquash e4f5g6h 修复登录 bug\nsquash i7j8k9l 添加单元测试\ndrop m1n2o3p 修改注释拼写\ndrop q4r5s6t WIP: 半成品代码\n# 结果：一个干净的提交\"添加用户登录功能（含测试）\"\n```\n\n## cherry-pick：精准摘取提交\n\n```bash\n# 从另一个分支摘取单个提交\ngit cherry-pick \u003Ccommit-sha>\n\n# 摘取一个范围（不含起点）\ngit cherry-pick A..B\n\n# 摘取一个范围（含起点）\ngit cherry-pick A^..B\n\n# 保留原始作者信息\ngit cherry-pick -x \u003Ccommit-sha>\n\n# 有冲突时\ngit cherry-pick --continue  # 解决后继续\ngit cherry-pick --abort      # 放弃\n```\n\n**典型使用场景：**\n\n```bash\n# hotfix 需要同步到 release 分支\ngit log main --oneline -5\n# abc1234 fix: 修复 XSS 漏洞（需要 cherry-pick）\n# def5678 feat: 新增 dark mode（不需要）\n\ngit checkout release\u002F2.1\ngit cherry-pick abc1234\ngit push origin release\u002F2.1\n```\n\n## bisect：二分查找 bug 引入的提交\n\n这是很多人不知道的神器，能在有几百个提交的历史中快速定位引入 bug 的提交。\n\n```bash\ngit bisect start\ngit bisect bad              # 当前版本有 bug\ngit bisect good v1.0.0      # 某个已知正常的版本\n\n# Git 自动 checkout 中间的提交，测试后标记：\ngit bisect bad    # 有 bug\ngit bisect good   # 没有 bug\n\n# 几次后 Git 精确定位到引入 bug 的提交\ngit bisect reset  # 完成，重置\n```\n\n**自动化 bisect（推荐）：**\n\n```bash\n# 写一个测试脚本（退出码 0=good, 非0=bad）\ncat > \u002Ftmp\u002Ftest.sh \u003C\u003C 'EOF'\n#!\u002Fbin\u002Fbash\nnpm test -- --testPathPattern=\"login\" 2>\u002Fdev\u002Fnull\nEOF\nchmod +x \u002Ftmp\u002Ftest.sh\n\ngit bisect start\ngit bisect bad HEAD\ngit bisect good v2.0.0\ngit bisect run \u002Ftmp\u002Ftest.sh\n# Git 全自动完成二分查找\n```\n\n## reflog：后悔药\n\nreflog 记录了 HEAD 的所有移动历史，是找回\"丢失\"提交的救命工具。\n\n```bash\ngit reflog\n# HEAD@{0}: commit: 添加功能 X\n# HEAD@{1}: reset: moving to HEAD~3  \u003C- 这里 reset 了\n# HEAD@{2}: commit: 添加功能 W\n\n# 找回被 reset 丢弃的提交\ngit checkout HEAD@{2}  # 查看\ngit branch recovery HEAD@{2}  # 创建分支保存\n\n# 撤销错误的 rebase\ngit reflog | grep \"rebase\"\ngit reset --hard HEAD@{N}\n```\n\n## worktree：同时工作在多个分支\n\n```bash\n# 不用 stash，直接开多个工作目录\ngit worktree add ..\u002Fhotfix hotfix\u002Fcritical-bug\ncd ..\u002Fhotfix\ngit commit -am \"fix: 修复关键 bug\"\ngit push\n\ncd -  # 回到主目录，继续原来的工作\n\ngit worktree remove ..\u002Fhotfix\n```\n\n## submodule vs subtree\n\n```bash\n# submodule：引用另一个仓库的特定提交\ngit submodule add https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git libs\u002Flib\ngit submodule update --init --recursive  # 克隆时需要\n\n# subtree：把另一个仓库的内容合并进来\ngit subtree add --prefix=libs\u002Flib https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git main --squash\ngit subtree pull --prefix=libs\u002Flib https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git main --squash\n```\n\n**选择：** 不需要向上游推送改动用 subtree；需要独立开发并推回上游用 submodule。\n\n## 团队工作流对比\n\n**GitHub Flow：**\n```\nmain ---------------------------------------- (永远可部署)\n      └── feature\u002Fxxx -------- PR --------┘\n```\n\n**Git Flow：**\n```\nmain ---------------------------------------- (版本发布)\ndevelop ------------------------------------- (集成)\n         └── feature\u002Fxxx -----------------┘\nhotfix -- (从 main 分出，修完合回 main 和 develop)\nrelease -- (从 develop 分出，修 bug，合回两者)\n```\n\n**Trunk-based Development：**\n```\nmain -------- (直接提交或极短命 feature 分支，\u003C= 1天)\n```\n需要强大的 CI\u002FCD 和 feature flags，是 Google、Meta 的选择。\n\n## hooks：自动化质量保障\n\n```bash\n# .git\u002Fhooks\u002Fpre-commit（提交前自动 lint）\n#!\u002Fbin\u002Fbash\nset -e\nnpx eslint --ext .js,.ts src\u002F || exit 1\nnpx tsc --noEmit || exit 1\n```\n\n```bash\n# .git\u002Fhooks\u002Fcommit-msg（校验提交信息格式）\n#!\u002Fbin\u002Fbash\nCOMMIT_MSG=$(cat \"$1\")\nPATTERN=\"^(feat|fix|docs|style|refactor|test|chore|perf)(\\(.+\\))?: .{1,72}$\"\nif ! echo \"$COMMIT_MSG\" | grep -qE \"$PATTERN\"; then\n    echo \"提交信息格式错误！正确格式：type(scope): description\"\n    exit 1\nfi\n```\n\n**使用 husky 管理 hooks（推荐）：**\n```bash\nnpm install --save-dev husky lint-staged\nnpx husky init\n```\n\n```json\n{\n  \"lint-staged\": {\n    \"*.{js,ts}\": [\"eslint --fix\", \"git add\"],\n    \"*.{json,md}\": [\"prettier --write\", \"git add\"]\n  }\n}\n```\n\n## 常用别名和配置\n\n```ini\n# ~\u002F.gitconfig\n[alias]\n    lg = log --oneline --graph --decorate --all\n    st = status -sb\n    unstage = reset HEAD --\n    undo = reset --soft HEAD~1\n    wip = commit -am \"WIP\"\n\n[pull]\n    rebase = true  # git pull 默认用 rebase\n\n[push]\n    default = current  # 默认推送当前分支\n```\n","\u003Ch1>Git 进阶工作流：rebase、cherry-pick、bisect 的正确使用\u003C\u002Fh1>\n\u003Cblockquote>\n\u003Cp>Git 的基础操作网上到处是，但 rebase 踩坑、bisect 不会用、interactive rebase 懵逼才是大多数开发者的真实状态。\u003C\u002Fp>\n\u003C\u002Fblockquote>\n\u003Ch2 id=\"merge-vs-rebase-的本质区别\">merge vs rebase 的本质区别\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>merge：\u003C\u002Fstrong> 创建一个新的合并提交，保留完整历史分支结构。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>     A---B---C  feature\n    \u002F         \\\nD---E---F---G---H  main（merge 后）\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>rebase：\u003C\u002Fstrong> 把 feature 上的提交一个个&quot;重放&quot;到 main 的最新提交之后，历史是线性的。\u003C\u002Fp>\n\u003Cpre>\u003Ccode>             A'--B'--C'  feature（rebase 后）\n            \u002F\nD---E---F---G  main\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>选择逻辑：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Ctable>\n\u003Cthead>\n\u003Ctr>\n\u003Cth>场景\u003C\u002Fth>\n\u003Cth>用哪个\u003C\u002Fth>\n\u003C\u002Ftr>\n\u003C\u002Fthead>\n\u003Ctbody>\n\u003Ctr>\n\u003Ctd>同步主线到 feature branch\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>rebase\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>合并 feature 到 main\u003C\u002Ftd>\n\u003Ctd>看团队约定\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>PR 合并\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>squash merge\u003C\u002Fcode> 或 \u003Ccode>rebase merge\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003Ctr>\n\u003Ctd>hotfix 合并到多个分支\u003C\u002Ftd>\n\u003Ctd>\u003Ccode>cherry-pick\u003C\u002Fcode>\u003C\u002Ftd>\n\u003C\u002Ftr>\n\u003C\u002Ftbody>\n\u003C\u002Ftable>\n\u003Ch2 id=\"rebase-正确使用\">rebase 正确使用\u003C\u002Fh2>\n\u003Ch3 id=\"基本流程\">基本流程\u003C\u002Fh3>\n\u003Cpre>\u003Ccode class=\"language-bash\">git checkout feature\u002Flogin\ngit fetch origin\ngit rebase origin\u002Fmain\n\n# 如果有冲突：\n# 1. 解决冲突\n# 2. git add &lt;resolved-files&gt;\n# 3. git rebase --continue\n# 4. 如果想放弃：git rebase --abort\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch3 id=\"rebase-的黄金禁忌\">rebase 的黄金禁忌\u003C\u002Fh3>\n\u003Cp>\u003Cstrong>永远不要 rebase 已经 push 到远程的公共分支。\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cp>原因：rebase 会改变提交的 SHA，导致其他人的本地历史与远程不一致，造成灾难性的合并冲突。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 危险操作（不要做）\ngit checkout main\ngit rebase feature\u002Fnew-api  # 改写了 main 的历史！\n\n# 安全操作\ngit checkout feature\u002Fnew-api\ngit rebase main  # 只改写自己的 feature 分支\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"interactive-rebase-整理提交历史\">Interactive Rebase：整理提交历史\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 整理最近 5 个提交\ngit rebase -i HEAD~5\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>编辑器打开后每行代表一个提交：\u003C\u002Fp>\n\u003Cpre>\u003Ccode>pick a1b2c3d 添加用户登录功能\npick e4f5g6h 修复登录 bug\npick i7j8k9l 添加单元测试\npick m1n2o3p 修改注释拼写\npick q4r5s6t WIP: 半成品代码\n\n# 命令说明：\n# pick   = 保留此提交\n# reword = 保留但修改提交信息\n# edit   = 暂停以修改内容\n# squash = 合并到上一个提交（保留提交信息）\n# fixup  = 合并到上一个提交（丢弃本提交信息）\n# drop   = 删除此提交\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>squash 合并示例：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>pick a1b2c3d 添加用户登录功能\nsquash e4f5g6h 修复登录 bug\nsquash i7j8k9l 添加单元测试\ndrop m1n2o3p 修改注释拼写\ndrop q4r5s6t WIP: 半成品代码\n# 结果：一个干净的提交&quot;添加用户登录功能（含测试）&quot;\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"cherry-pick-精准摘取提交\">cherry-pick：精准摘取提交\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 从另一个分支摘取单个提交\ngit cherry-pick &lt;commit-sha&gt;\n\n# 摘取一个范围（不含起点）\ngit cherry-pick A..B\n\n# 摘取一个范围（含起点）\ngit cherry-pick A^..B\n\n# 保留原始作者信息\ngit cherry-pick -x &lt;commit-sha&gt;\n\n# 有冲突时\ngit cherry-pick --continue  # 解决后继续\ngit cherry-pick --abort      # 放弃\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>典型使用场景：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># hotfix 需要同步到 release 分支\ngit log main --oneline -5\n# abc1234 fix: 修复 XSS 漏洞（需要 cherry-pick）\n# def5678 feat: 新增 dark mode（不需要）\n\ngit checkout release\u002F2.1\ngit cherry-pick abc1234\ngit push origin release\u002F2.1\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"bisect-二分查找-bug-引入的提交\">bisect：二分查找 bug 引入的提交\u003C\u002Fh2>\n\u003Cp>这是很多人不知道的神器，能在有几百个提交的历史中快速定位引入 bug 的提交。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">git bisect start\ngit bisect bad              # 当前版本有 bug\ngit bisect good v1.0.0      # 某个已知正常的版本\n\n# Git 自动 checkout 中间的提交，测试后标记：\ngit bisect bad    # 有 bug\ngit bisect good   # 没有 bug\n\n# 几次后 Git 精确定位到引入 bug 的提交\ngit bisect reset  # 完成，重置\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>自动化 bisect（推荐）：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 写一个测试脚本（退出码 0=good, 非0=bad）\ncat &gt; \u002Ftmp\u002Ftest.sh &lt;&lt; 'EOF'\n#!\u002Fbin\u002Fbash\nnpm test -- --testPathPattern=&quot;login&quot; 2&gt;\u002Fdev\u002Fnull\nEOF\nchmod +x \u002Ftmp\u002Ftest.sh\n\ngit bisect start\ngit bisect bad HEAD\ngit bisect good v2.0.0\ngit bisect run \u002Ftmp\u002Ftest.sh\n# Git 全自动完成二分查找\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"reflog-后悔药\">reflog：后悔药\u003C\u002Fh2>\n\u003Cp>reflog 记录了 HEAD 的所有移动历史，是找回&quot;丢失&quot;提交的救命工具。\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">git reflog\n# HEAD@{0}: commit: 添加功能 X\n# HEAD@{1}: reset: moving to HEAD~3  &lt;- 这里 reset 了\n# HEAD@{2}: commit: 添加功能 W\n\n# 找回被 reset 丢弃的提交\ngit checkout HEAD@{2}  # 查看\ngit branch recovery HEAD@{2}  # 创建分支保存\n\n# 撤销错误的 rebase\ngit reflog | grep &quot;rebase&quot;\ngit reset --hard HEAD@{N}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"worktree-同时工作在多个分支\">worktree：同时工作在多个分支\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\"># 不用 stash，直接开多个工作目录\ngit worktree add ..\u002Fhotfix hotfix\u002Fcritical-bug\ncd ..\u002Fhotfix\ngit commit -am &quot;fix: 修复关键 bug&quot;\ngit push\n\ncd -  # 回到主目录，继续原来的工作\n\ngit worktree remove ..\u002Fhotfix\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"submodule-vs-subtree\">submodule vs subtree\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\"># submodule：引用另一个仓库的特定提交\ngit submodule add https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git libs\u002Flib\ngit submodule update --init --recursive  # 克隆时需要\n\n# subtree：把另一个仓库的内容合并进来\ngit subtree add --prefix=libs\u002Flib https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git main --squash\ngit subtree pull --prefix=libs\u002Flib https:\u002F\u002Fgithub.com\u002Fxxx\u002Flib.git main --squash\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>选择：\u003C\u002Fstrong> 不需要向上游推送改动用 subtree；需要独立开发并推回上游用 submodule。\u003C\u002Fp>\n\u003Ch2 id=\"团队工作流对比\">团队工作流对比\u003C\u002Fh2>\n\u003Cp>\u003Cstrong>GitHub Flow：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>main ---------------------------------------- (永远可部署)\n      └── feature\u002Fxxx -------- PR --------┘\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Git Flow：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>main ---------------------------------------- (版本发布)\ndevelop ------------------------------------- (集成)\n         └── feature\u002Fxxx -----------------┘\nhotfix -- (从 main 分出，修完合回 main 和 develop)\nrelease -- (从 develop 分出，修 bug，合回两者)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>Trunk-based Development：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode>main -------- (直接提交或极短命 feature 分支，&lt;= 1天)\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>需要强大的 CI\u002FCD 和 feature flags，是 Google、Meta 的选择。\u003C\u002Fp>\n\u003Ch2 id=\"hooks-自动化质量保障\">hooks：自动化质量保障\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-bash\"># .git\u002Fhooks\u002Fpre-commit（提交前自动 lint）\n#!\u002Fbin\u002Fbash\nset -e\nnpx eslint --ext .js,.ts src\u002F || exit 1\nnpx tsc --noEmit || exit 1\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-bash\"># .git\u002Fhooks\u002Fcommit-msg（校验提交信息格式）\n#!\u002Fbin\u002Fbash\nCOMMIT_MSG=$(cat &quot;$1&quot;)\nPATTERN=&quot;^(feat|fix|docs|style|refactor|test|chore|perf)(\\(.+\\))?: .{1,72}$&quot;\nif ! echo &quot;$COMMIT_MSG&quot; | grep -qE &quot;$PATTERN&quot;; then\n    echo &quot;提交信息格式错误！正确格式：type(scope): description&quot;\n    exit 1\nfi\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cp>\u003Cstrong>使用 husky 管理 hooks（推荐）：\u003C\u002Fstrong>\u003C\u002Fp>\n\u003Cpre>\u003Ccode class=\"language-bash\">npm install --save-dev husky lint-staged\nnpx husky init\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Cpre>\u003Ccode class=\"language-json\">{\n  &quot;lint-staged&quot;: {\n    &quot;*.{js,ts}&quot;: [&quot;eslint --fix&quot;, &quot;git add&quot;],\n    &quot;*.{json,md}&quot;: [&quot;prettier --write&quot;, &quot;git add&quot;]\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003Ch2 id=\"常用别名和配置\">常用别名和配置\u003C\u002Fh2>\n\u003Cpre>\u003Ccode class=\"language-ini\"># ~\u002F.gitconfig\n[alias]\n    lg = log --oneline --graph --decorate --all\n    st = status -sb\n    unstage = reset HEAD --\n    undo = reset --soft HEAD~1\n    wip = commit -am &quot;WIP&quot;\n\n[pull]\n    rebase = true  # git pull 默认用 rebase\n\n[push]\n    default = current  # 默认推送当前分支\n\u003C\u002Fcode>\u003C\u002Fpre>\n","2026-05-03",[11,12],"git","工程实践",false,[15,28,39,51,59,66,69,76,83,90,100,109,119,128,136,144,153,162,171,181,188,198,204,211,217,226,233,240,248,258,267,276,286,296,306,314,324,335,345,354,362,368,376,384,392,400,408,415],{"slug":16,"title":17,"description":18,"pub_date":19,"tags":20,"draft":13,"word_count":27},"ide-skills-guide","Agent Skills 完全指南：21 款第三方 Skill 深度评测与使用心得","全面评测 21 款第三方 Agent Skills，涵盖 Vue 生态、前端设计、构建工具、实用工具四大分类。从安装配置到实际使用场景，带你了解每个 Skill 的功能特点、最佳实践与使用心得。","2026-06-15",[21,22,23,24,25,26],"agent","skills","AI","效率工具","前端","Vue",4169,{"slug":29,"title":30,"description":31,"pub_date":32,"tags":33,"draft":13,"word_count":38},"linux-kernel-skeleton-struct-funcptr-container_of","Linux 内核骨架：struct、函数指针与 container_of","读懂 Linux 内核源码的三件套：巨大的 struct 组合代替继承、函数指针表实现虚派发、container_of 宏从嵌入成员找回完整对象。","2026-05-09",[34,35,36,37],"linux","kernel","C","container_of",1369,{"slug":40,"title":41,"description":42,"pub_date":43,"tags":44,"draft":13,"word_count":50},"astro-complete-guide-2025","Astro 5 深度剖析：Islands 架构原理、构建优化与 Cloudflare Workers 边缘部署","从编译器视角解析 Astro 5 的 Islands 架构实现原理，Content Layer API 的 Vite 插件机制，Server Islands 的流式渲染，以及如何在 Cloudflare Workers + D1 边缘环境下榨干性能。","2026-05-08",[45,46,47,48,49],"astro","frontend","cloudflare","performance","architecture",3663,{"slug":52,"title":53,"description":54,"pub_date":9,"tags":55,"draft":13,"word_count":58},"llm-prompt-engineering","Prompt Engineering 实战：让 LLM 真正听话的技巧","System prompt 怎么写、Few-shot 怎么设计、Chain-of-Thought 原理，以及常见失败模式和调试方法。",[56,57,12],"ai","llm",1723,{"slug":60,"title":61,"description":62,"pub_date":9,"tags":63,"draft":13,"word_count":65},"rag-system-design","RAG 系统设计：从 naive 到 production-ready","Retrieval-Augmented Generation 不只是「向量数据库 + LLM」，分块策略、召回质量、重排序、缓存才是工程核心。",[56,64,57,12],"rag",1613,{"slug":4,"title":5,"description":6,"pub_date":9,"tags":67,"draft":13,"word_count":68},[11,12],1396,{"slug":70,"title":71,"description":72,"pub_date":9,"tags":73,"draft":13,"word_count":75},"docker-practical-guide","Docker 实战：从会用到用好","会 docker run 不够，Dockerfile 最佳实践、多阶段构建、Compose 编排、镜像瘦身才是日常真正需要的。",[74,34,12],"docker",1268,{"slug":77,"title":78,"description":79,"pub_date":9,"tags":80,"draft":13,"word_count":82},"anthropics-skills-guide","anthropics\u002Fskills：Anthropic 官方 Agent Skills 仓库解析","Anthropic 官方开源的 Agent Skills 标准仓库，127k stars，解析 SKILL.md 规范、17 个示例 skill 的设计模式，以及如何在 Claude Code \u002F Claude.ai \u002F API 中使用",[56,81,21,22],"Claude",2090,{"slug":84,"title":85,"description":86,"pub_date":9,"tags":87,"draft":13,"word_count":89},"karpathy-claude-code-guidelines","Karpathy 的 LLM 编码批评与 CLAUDE.md 最佳实践","基于 Andrej Karpathy 对 LLM 编程助手的观察，forrestchang 提炼出一个 CLAUDE.md 文件，4 条原则解决 AI 编码的典型失控问题：乱猜假设、过度设计、乱改代码、目标不清",[56,81,88,12],"Claude Code",2699,{"slug":91,"title":92,"description":93,"pub_date":9,"tags":94,"draft":13,"word_count":99},"typescript-advanced-patterns","TypeScript 高级模式：让类型系统为你工作","基础 TS 会了但类型总是 any？条件类型、映射类型、模板字面量类型、infer 关键字才是 TS 的真正威力。",[95,96,97,98],"typescript","类型系统","前端工程","高级模式",1419,{"slug":101,"title":102,"description":103,"pub_date":9,"tags":104,"draft":13,"word_count":108},"linux-performance-tuning","Linux 性能调优实战：从 top 到 perf 的完整工具链","遇到性能问题不知道从哪下手？这篇建立系统化的排查思路，从 CPU\u002F内存\u002FIO\u002F网络逐层分析。",[34,105,106,107],"性能","运维","系统编程",1524,{"slug":110,"title":111,"description":112,"pub_date":9,"tags":113,"draft":13,"word_count":118},"python-functional-programming","Python 函数式编程：map\u002Ffilter\u002Freduce 之外","Python 不是纯函数式语言，但 functools、itertools、偏函数、闭包这些工具用好了能让代码简洁一个量级。",[114,115,116,117],"python","函数式","闭包","装饰器",1867,{"slug":120,"title":121,"description":122,"pub_date":9,"tags":123,"draft":13,"word_count":127},"python-oop-guide","Python 面向对象：__init__ 之外你需要知道的","Python OOP 不只是 class + __init__，魔术方法、描述符、元类才是真正的武器。",[114,124,125,126],"OOP","面向对象","魔术方法",1792,{"slug":129,"title":130,"description":131,"pub_date":9,"tags":132,"draft":13,"word_count":135},"python-data-structures","Python 内置数据结构深度解析","list、dict、set、tuple 不只是数据容器，搞懂它们的底层实现和时间复杂度，才能写出高性能 Python。",[114,133,105,134],"数据结构","算法",1517,{"slug":137,"title":138,"description":139,"pub_date":9,"tags":140,"draft":13,"word_count":143},"python-basics-quick-start","Python 快速上手：写给有编程基础的人","已经会其他语言，想快速掌握 Python 的语法特性和思维方式，这篇是捷径。",[114,141,142],"入门","基础",1607,{"slug":145,"title":146,"description":147,"pub_date":9,"tags":148,"draft":13,"word_count":152},"python-dataclass-pydantic","Python dataclass vs Pydantic：数据类选型指南","dataclass 是标准库的轻量选择，Pydantic v2 是带验证的重武器，什么时候用哪个，这篇说清楚。",[114,149,150,151],"dataclass","pydantic","数据验证",1323,{"slug":154,"title":155,"description":156,"pub_date":9,"tags":157,"draft":13,"word_count":161},"python-asyncio-practical","Python asyncio 实战：从回调地狱到协程优雅","asyncio 是 Python 异步编程的核心，搞懂 event loop、Task、gather 这些概念才能写出真正高效的异步代码。",[114,158,159,160],"asyncio","并发","网络编程",1258,{"slug":163,"title":164,"description":165,"pub_date":9,"tags":166,"draft":13,"word_count":170},"python-type-hints-guide","Python 类型注解完全指南：从入门到实践","Python 3.5+ 引入类型注解，配合 mypy\u002Fpyright 让 Python 也能享受静态类型检查的好处。",[114,167,168,169],"typescript-style","type-hints","工具链",1102,{"slug":172,"title":173,"description":174,"pub_date":175,"tags":176,"draft":13,"word_count":180},"pwa-install-update-button","PWA 踩坑：为什么安装按钮从来不出现","从 beforeinstallprompt 到 Service Worker waiting，把 PWA 的安装与更新提示真正做对","2026-05-02",[177,178,179],"pwa","javascript","web",1683,{"slug":182,"title":183,"description":184,"pub_date":185,"tags":186,"draft":13,"word_count":187},"openclaw-vs-hermes-agent","OpenClaw vs Hermes Agent：两个本地优先 Agent 的设计差异","OpenClaw（Novita AI）和 Hermes Agent（Nous Research）都是本地运行的个人 AI Agent，但在记忆系统、技能学习、运行环境和模型生态上走了不同的路。深入对比两种架构的核心差异。","2026-05-01",[56,21,57],1679,{"slug":189,"title":190,"description":191,"pub_date":185,"tags":192,"draft":13,"word_count":197},"cpp-random-design-patterns","C++ 设计模式实战：RAII、观察者、工厂","用现代 C++（C++17\u002F20）实现三种高频设计模式：RAII 资源管理、观察者模式事件系统、工厂模式插件架构。每种模式给出问题场景、实现代码和真实工程案例。",[193,194,195,196],"cpp","设计模式","c++17","工程",2613,{"slug":199,"title":200,"description":201,"pub_date":185,"tags":202,"draft":13,"word_count":203},"data-structures-fundamentals","数据结构基础：从数组到红黑树","系统梳理常用数据结构的核心原理、时间复杂度和适用场景。数组、链表、栈、队列、哈希表、二叉树、堆、图，每种结构附实现要点和 C++ 代码片段。",[133,134,193,142],3004,{"slug":205,"title":206,"description":207,"pub_date":208,"tags":209,"draft":13,"word_count":210},"ai-agent-what-is","什么是 AI Agent？从 LLM 到自主执行","LLM 本身是无状态问答机，Agent 是什么让它’动’起来的？本文深入解析 Agent 的四个核心能力、ReAct 框架、工具调用原理，以及主流框架横向对比。","2026-04-30",[56,21,57],2116,{"slug":212,"title":213,"description":214,"pub_date":208,"tags":215,"draft":13,"word_count":216},"ai-agent-memory","AI Agent 的记忆系统：从上下文窗口到长期记忆","深入拆解 AI Agent 的四种记忆类型、上下文窗口压缩策略、RAG 向量检索原理，以及三种典型失败模式和工程选型建议。",[56,21,64],2052,{"slug":218,"title":219,"description":220,"pub_date":208,"tags":221,"draft":13,"word_count":225},"network-proxy-vpn-guide","代理与翻墙技术原理：从 HTTP 代理到现代协议","深入解析代理与 VPN 的本质区别，梳理从 SOCKS5 到 Shadowsocks、V2Ray\u002FXray、Hysteria2 的协议演进，以及机场订阅的技术本质。",[222,223,224],"网络","代理","协议",2148,{"slug":227,"title":228,"description":229,"pub_date":208,"tags":230,"draft":13,"word_count":143},"algorithm-binary-search","二分查找：永远写不对？记住这个模板","彻底搞清楚二分查找的边界问题：闭区间和左闭右开两套模板、三道经典 LeetCode 题目完整 C++ 实现，以及二分答案的进阶思路。",[134,231,232,193],"二分查找","leetcode",{"slug":234,"title":235,"description":236,"pub_date":208,"tags":237,"draft":13,"word_count":239},"algorithm-sliding-window","滑动窗口算法：从暴力到 O(n) 的思维跃迁","系统讲解滑动窗口算法的核心模板、适用题型，配合三道经典 LeetCode 题目的完整 C++ 实现，彻底理解双指针收缩思路。",[134,238,232,193],"滑动窗口",1943,{"slug":241,"title":242,"description":243,"pub_date":208,"tags":244,"draft":13,"word_count":247},"network-clash-config","Clash \u002F Mihomo 配置详解：规则、策略组与分流","深入解析 Clash\u002FMihomo 的核心配置结构，包括代理节点、策略组类型、规则优先级、DNS fake-ip 模式，以及一份实用的完整配置模板。",[222,245,223,246],"clash","配置",1292,{"slug":249,"title":250,"description":251,"pub_date":252,"tags":253,"draft":13,"word_count":257},"hid-hotplug","HID 设备热插拔检测：从 udev 到 node-hid","在 Linux 上用 node-hid + usb 库实现可靠的 USB HID 设备热插拔检测，踩坑记录","2026-04-28",[193,254,34,255,256],"hid","nodejs","electron",2039,{"slug":259,"title":260,"description":261,"pub_date":262,"tags":263,"draft":13,"word_count":266},"electron-ipc-types","Electron IPC 类型安全：从 any 到完全类型化","用 TypeScript 泛型封装 Electron IPC，彻底消灭 any，preload 契约集中管理","2026-04-25",[256,95,264,265],"ipc","vue",1446,{"slug":268,"title":269,"description":270,"pub_date":271,"tags":272,"draft":13,"word_count":275},"element-plus-popover-hide","手动关闭多个 el-popover（不用 v-model:visible）","通过 ref + Reflect.get 调用 hide() 方法手动关闭 Element Plus Popover，解释 Vue3 Proxy 导致无法直接调用实例方法的原因。","2024-10-25",[265,273,274],"element-plus","vue3",1321,{"slug":277,"title":278,"description":279,"pub_date":280,"tags":281,"draft":13,"word_count":285},"vite-vue3-ts-elementplus-pinia","用 Vite+（vp）从零搭建 Vue3 + TypeScript + Element Plus + Pinia + Vue Router","使用 Vite+ 统一工具链（vp）一条命令搭建 Vue3 全家桶，涵盖按需导入、Pinia store、路由配置，以及常见坑的解决方案。","2024-08-27",[265,282,95,273,283,284],"vite","pinia","vite-plus",1960,{"slug":287,"title":288,"description":289,"pub_date":290,"tags":291,"draft":13,"word_count":295},"cef-lnk2038-iterator-debug-level","CEF LNK2038：解决 _ITERATOR_DEBUG_LEVEL 不匹配错误","分析 CEF（Chromium Embedded Framework）集成时出现的 LNK2038 _ITERATOR_DEBUG_LEVEL 链接错误，从根本原因到解决方案的完整指南。","2024-05-07",[193,292,293,294],"CEF","Visual Studio","链接错误",1509,{"slug":297,"title":298,"description":299,"pub_date":300,"tags":301,"draft":13,"word_count":305},"npm-electron-install-fix","彻底解决 npm 安装 Electron 失败的问题","分析 npm install electron 失败的根本原因（下载二进制超时\u002F被墙），通过国内镜像（npmmirror）彻底解决，并介绍多种备选方案和常见错误排查。","2024-03-01",[256,302,303,304],"npm","前端工具链","国内镜像",1494,{"slug":307,"title":308,"description":309,"pub_date":310,"tags":311,"draft":13,"word_count":313},"git-out-of-memory","解决 git 报错：Fatal: Out of memory, malloc failed","分析 git 大仓库操作时出现 Out of memory malloc failed 的根本原因，通过调整 pack.windowMemory、http.postBuffer 和 git repack 彻底解决。","2024-01-31",[11,34,312],"工具",2244,{"slug":315,"title":316,"description":317,"pub_date":318,"tags":319,"draft":13,"word_count":323},"vmware-tools-install","在 VMware 虚拟机中安装 open-vm-tools 完整指南","详解 VMware Tools 的作用、open-vm-tools 与官方 VMware Tools 的区别，以及在 Ubuntu 虚拟机中安装并生效的完整步骤和常见问题排查。","2023-11-21",[320,34,321,322],"VMware","Ubuntu","虚拟机",2523,{"slug":325,"title":326,"description":327,"pub_date":328,"tags":329,"draft":13,"word_count":334},"load-balancing-algorithms","负载均衡算法完全指南：从轮询到一致性哈希","系统梳理静态与动态负载均衡算法，涵盖轮询、随机、权重、IP Hash、一致性 Hash、最少连接、最快响应等，并对比 Nginx、Dubbo、Spring Cloud LoadBalancer 的实现差异。","2023-11-15",[330,331,332,333],"分布式","负载均衡","Nginx","微服务",1764,{"slug":336,"title":337,"description":338,"pub_date":339,"tags":340,"draft":13,"word_count":344},"win-cw2a-ca2w","ATL 字符串转换：CW2A 与 CA2W 完全指南","详解 ATL 宏 CW2A\u002FCA2W 在 Unicode 与 ANSI 之间的字符串转换用法、头文件依赖、USES_CONVERSION 宏的作用与常见陷阱。","2023-06-09",[193,341,342,343],"windows","ATL","字符串",1665,{"slug":346,"title":347,"description":348,"pub_date":339,"tags":349,"draft":13,"word_count":353},"csharp-sendmessage-cpp","C# 通过 SendMessage 向 C++ 窗口发送消息与字符串","使用 P\u002FInvoke 调用 user32.dll 的 SendMessage，从 C# 发送自定义 WM_USER 消息及字符串指针给 C++ 原生窗口，并在 C++ 侧正确接收和转换。",[350,193,341,351,352],"C#","互操作","PInvoke",1554,{"slug":355,"title":356,"description":357,"pub_date":358,"tags":359,"draft":13,"word_count":361},"win-postmessage-vector","Windows PostMessage 跨线程传递 std::vector 指针","通过 PostMessage 在 Windows 消息队列中传递 std::vector 指针，使用 reinterpret_cast 将指针装入 LPARAM，并在接收方正确释放内存。","2023-05-26",[193,341,360],"WinAPI",1823,{"slug":363,"title":364,"description":365,"pub_date":358,"tags":366,"draft":13,"word_count":367},"exe-dll-single-package","将 EXE 和 DLL 打包成单一可执行文件","介绍两种将 exe 和依赖 dll 打包成单文件的方案：Enigma Virtual Box 和 WinRAR 自解压，适合发布 Windows 桌面程序时简化分发流程。",[341,193,312],1619,{"slug":369,"title":370,"description":371,"pub_date":358,"tags":372,"draft":13,"word_count":375},"cpp-random-mt19937","C++ 现代随机数生成：用 mt19937 彻底告别 rand()","深入讲解为什么 rand() 不够用，以及如何用 C++11 的 \u003Crandom> 库正确生成高质量随机数，涵盖 mt19937、各种分布和线程安全。",[193,373,374],"c++11","random",1549,{"slug":377,"title":378,"description":379,"pub_date":380,"tags":381,"draft":13,"word_count":383},"win-startup-registry","C++ 实现程序开机自启动：注册表方式详解","通过操作 Windows 注册表 Run 键实现程序开机自启动，包括 HKCU 与 HKLM 区别、完整封装代码、工作目录问题和 UAC 权限处理。","2022-12-26",[341,193,382],"registry",1201,{"slug":385,"title":386,"description":387,"pub_date":388,"tags":389,"draft":13,"word_count":391},"mfc-cstring-wparam","MFC 中 CString 与 WPARAM 之间的转换","详解 MFC 消息传递中 CString 无法直接强转为 WPARAM 的原因，以及两种正确的转换方案，并介绍结构体指针传递的正确姿势。","2022-11-25",[390,193,341],"mfc",1546,{"slug":393,"title":394,"description":395,"pub_date":396,"tags":397,"draft":13,"word_count":399},"duilib-static-build","正确编译 Duilib 静态库：避免 ATL 依赖和链接错误","详解如何用 DuiLib_Static.vcxproj 编译 Duilib 静态库，解决 VARIANT 未定义、Unicode 配置不匹配和 ATL 依赖等常见问题。","2022-08-24",[193,398,341,390],"duilib",2639,{"slug":401,"title":402,"description":403,"pub_date":404,"tags":405,"draft":13,"word_count":407},"mfc-dpi-adaptive","MFC 界面自适应不同分辨率","MFC 对话框程序实现控件和字体随分辨率自动缩放的完整方案，附 DPI Awareness 配置说明","2022-08-17",[390,193,341,406],"dpi",1414,{"slug":409,"title":410,"description":411,"pub_date":412,"tags":413,"draft":13,"word_count":414},"mfc-drag-window","MFC 无标题栏窗口客户区拖动：三种方法对比","MFC 对话框去掉标题栏后如何实现拖动移动窗口，三种方案完整实现与适用场景分析","2022-08-16",[390,193,341],1633,{"slug":416,"title":417,"description":418,"pub_date":419,"tags":420,"draft":13,"word_count":422},"algorithm-number-complement","整数的补数：位运算掩码解法","LeetCode 476 题，用掩码 XOR 实现整数补数，附 C++\u002FPython\u002FJava 三种实现及补数与补码的区别","2021-03-08",[134,421,232],"位运算",1374,[]]