<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>Monorepo on heyaohua's Blog</title><link>https://blog.heyaohua.com/tags/monorepo/</link><description>Recent content in Monorepo on heyaohua's Blog</description><image><title>heyaohua's Blog</title><url>https://blog.heyaohua.com/og-image.png</url><link>https://blog.heyaohua.com/og-image.png</link></image><generator>Hugo</generator><language>zh-cn</language><lastBuildDate>Sat, 22 Aug 2026 23:24:00 +0800</lastBuildDate><atom:link href="https://blog.heyaohua.com/tags/monorepo/index.xml" rel="self" type="application/rss+xml"/><item><title>Git 使用手册 · 终极篇：工程化、安全与团队规范</title><link>https://blog.heyaohua.com/posts/2026/08/git-handbook-ultimate/</link><pubDate>Sat, 22 Aug 2026 23:24:00 +0800</pubDate><guid>https://blog.heyaohua.com/posts/2026/08/git-handbook-ultimate/</guid><description>Git 终极手册，覆盖 monorepo 策略、签名提交与密钥扫描等安全实践、CI/CD 中的 Git 技巧、灾难恢复 SOP，以及一套可直接落地的团队 Git 规范。</description><content:encoded><![CDATA[<p>这是 Git 手册系列的最后一篇。前三篇解决了&quot;会用 → 用得对 → 懂原理&quot;，本篇解决最后一件事：<strong>让 Git 在团队和工程体系里成为可靠的基础设施</strong>，并在灾难发生时救得回来。</p>
<hr>
<h2 id="目录">目录</h2>
<ul>
<li>Monorepo vs Polyrepo：仓库拓扑决策</li>
<li>大型仓库的性能优化</li>
<li>提交规范：Conventional Commits</li>
<li>签名提交与提交者验证</li>
<li>密钥防护：从预防到泄漏处置</li>
<li>CI/CD 中的 Git 技巧</li>
<li>灾难恢复 SOP</li>
<li>一套可直接落地的团队 Git 规范</li>
<li>终极速查表</li>
</ul>
<hr>
<h2 id="monorepo-vs-polyrepo仓库拓扑决策">Monorepo vs Polyrepo：仓库拓扑决策</h2>
<table>
	<thead>
			<tr>
					<th></th>
					<th>Monorepo（一个仓库存所有项目）</th>
					<th>Polyrepo（每项目一个仓库）</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>跨项目改动</td>
					<td>一次提交原子完成</td>
					<td>要发版、升依赖、多 PR 联动</td>
			</tr>
			<tr>
					<td>代码共享</td>
					<td>直接 import</td>
					<td>发包 / submodule</td>
			</tr>
			<tr>
					<td>CI</td>
					<td>需要增量构建，配置复杂</td>
					<td>简单直接</td>
			</tr>
			<tr>
					<td>权限边界</td>
					<td>靠 CODEOWNERS，粒度粗</td>
					<td>天然隔离</td>
			</tr>
			<tr>
					<td>克隆体积</td>
					<td>大（可用 sparse checkout 缓解）</td>
					<td>小</td>
			</tr>
			<tr>
					<td>代表</td>
					<td>Google、Meta、Vue、React 生态</td>
					<td>多数中小团队</td>
			</tr>
	</tbody>
</table>
<p><strong>决策建议</strong>：</p>
<ul>
<li>项目间强耦合、频繁联动发布（如前端组件库 + 应用）→ monorepo</li>
<li>团队/项目独立、发布节奏不同、需要强权限边界 → polyrepo</li>
<li>不确定就先 polyrepo；拆容易，合回去很难</li>
</ul>
<p>monorepo 配套工具：Nx、Turborepo、Bazel（构建缓存与增量）、changesets（多包版本管理）。</p>
<hr>
<h2 id="大型仓库的性能优化">大型仓库的性能优化</h2>
<p>仓库几万提交、几百 MB 之后，这些手段能救体验：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 浅克隆：只拿最近的历史（CI 必备）</span>
</span></span><span style="display:flex;"><span>git clone --depth <span style="color:#bd93f9">1</span> &lt;url&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 部分克隆：不下载全部 blob，按需懒加载（本地开发大仓库神器）</span>
</span></span><span style="display:flex;"><span>git clone --filter<span style="color:#ff79c6">=</span>blob:none &lt;url&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 稀疏检出：只 checkout 需要的目录</span>
</span></span><span style="display:flex;"><span>git sparse-checkout init --cone
</span></span><span style="display:flex;"><span>git sparse-checkout <span style="color:#8be9fd;font-style:italic">set</span> apps/web packages/ui
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 让 status / diff 在大仓库里更快</span>
</span></span><span style="display:flex;"><span>git config core.fsmonitor <span style="color:#8be9fd;font-style:italic">true</span>
</span></span><span style="display:flex;"><span>git config core.untrackedCache <span style="color:#8be9fd;font-style:italic">true</span>
</span></span><span style="display:flex;"><span>git maintenance start        <span style="color:#6272a4"># 后台自动做 gc、打包、提交图等维护</span>
</span></span></code></pre></div><p>提交图（commit-graph）单独提一句：它让 <code>git log --graph</code>、合并基计算在大仓库里快一个数量级，<code>git maintenance start</code> 会自动开启。</p>
<hr>
<h2 id="提交规范conventional-commits">提交规范：Conventional Commits</h2>
<p>格式：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>&lt;type&gt;(&lt;scope&gt;): &lt;subject&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;body&gt;
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>&lt;footer&gt;
</span></span></code></pre></div><p>常用 type：</p>
<table>
	<thead>
			<tr>
					<th>type</th>
					<th>含义</th>
					<th>影响版本</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td><code>feat</code></td>
					<td>新功能</td>
					<td>次版本 +1</td>
			</tr>
			<tr>
					<td><code>fix</code></td>
					<td>修 bug</td>
					<td>修订号 +1</td>
			</tr>
			<tr>
					<td><code>perf</code></td>
					<td>性能优化</td>
					<td>修订号 +1</td>
			</tr>
			<tr>
					<td><code>refactor</code></td>
					<td>重构（不改行为）</td>
					<td>不升</td>
			</tr>
			<tr>
					<td><code>docs</code> / <code>style</code> / <code>test</code> / <code>chore</code> / <code>ci</code> / <code>build</code></td>
					<td>文档 / 格式 / 测试 / 杂务</td>
					<td>不升</td>
			</tr>
			<tr>
					<td>带 <code>!</code> 或 footer 写 <code>BREAKING CHANGE:</code></td>
					<td>破坏性变更</td>
					<td>主版本 +1</td>
			</tr>
	</tbody>
</table>
<p>示例：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-text" data-lang="text"><span style="display:flex;"><span>feat(auth): 支持手机号验证码登录
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>- 新增 /auth/sms/send 与 /auth/sms/verify 接口
</span></span><span style="display:flex;"><span>- 验证码 5 分钟有效，限频 1 条/分钟
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>Closes #128
</span></span></code></pre></div><p>为什么值得推行：<strong>版本号和 CHANGELOG 可以全自动生成</strong>。配套工具链：</p>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># commitizen：交互式生成规范提交信息</span>
</span></span><span style="display:flex;"><span>pnpm add -D commitizen cz-conventional-changelog
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># commitlint + husky：不合规的提交直接拒绝</span>
</span></span><span style="display:flex;"><span>pnpm add -D @commitlint/cli @commitlint/config-conventional
</span></span><span style="display:flex;"><span><span style="color:#8be9fd;font-style:italic">echo</span> <span style="color:#f1fa8c">&#34;npx commitlint --edit \$1&#34;</span> &gt; .husky/commit-msg
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># semantic-release / release-please：按提交记录自动升版本、发 CHANGELOG</span>
</span></span></code></pre></div><hr>
<h2 id="签名提交与提交者验证">签名提交与提交者验证</h2>
<p>Git 的 <code>user.name</code> / <code>user.email</code> 可以随便填——<code>git commit --author=&quot;Linus &lt;linus@kernel.org&gt;&quot;</code> 就能伪装任何人。要证明&quot;这个提交真的是我提交的&quot;，用签名：</p>
<h3 id="ssh-签名推荐零门槛复用现有密钥">SSH 签名（推荐，零门槛复用现有密钥）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git config --global gpg.format ssh
</span></span><span style="display:flex;"><span>git config --global user.signingkey ~/.ssh/id_ed25519.pub
</span></span><span style="display:flex;"><span>git config --global commit.gpgsign <span style="color:#8be9fd;font-style:italic">true</span>      <span style="color:#6272a4"># 之后每次提交自动签名</span>
</span></span></code></pre></div><p>GitHub 设置里把同一个公钥添加为 <strong>Signing Key</strong>，你的提交就会显示 <code>Verified</code> 徽章。</p>
<h3 id="验证别人的提交">验证别人的提交</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git log --show-signature         <span style="color:#6272a4"># 查看签名验证结果</span>
</span></span><span style="display:flex;"><span>git merge --verify-signatures main
</span></span></code></pre></div><h3 id="为什么这很重要">为什么这很重要</h3>
<ul>
<li>供应链安全：防止有人伪装核心维护者提交恶意代码</li>
<li>审计合规：金融、政企场景要求提交可追溯真人</li>
<li>配合分支保护规则&quot;要求签名提交&quot;，从平台侧强制</li>
</ul>
<hr>
<h2 id="密钥防护从预防到泄漏处置">密钥防护：从预防到泄漏处置</h2>
<p>密钥进仓库是最高频的安全事故。三层防御：</p>
<h3 id="第一层本地预防">第一层：本地预防</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># gitleaks：提交/推送前扫描</span>
</span></span><span style="display:flex;"><span>brew install gitleaks
</span></span><span style="display:flex;"><span>gitleaks git --pre-commit --staged --verbose   <span style="color:#6272a4"># 接入 husky pre-commit</span>
</span></span></code></pre></div><p><code>.gitignore</code> 里永远有：<code>.env</code>、<code>*.pem</code>、<code>*.key</code>、<code>credentials*</code>。</p>
<h3 id="第二层服务端拦截">第二层：服务端拦截</h3>
<ul>
<li>GitHub：开启 <strong>Push Protection</strong>（推送时扫描，疑似密钥直接拒绝）</li>
<li>GitLab：开启 Secret Detection</li>
<li>自建：pre-receive hook 跑 gitleaks</li>
</ul>
<h3 id="第三层泄漏后的处置-sop">第三层：泄漏后的处置 SOP</h3>
<p>密钥一旦进过任何一次提交，就<strong>视为已泄漏</strong>，即使后来的提交删了它：</p>
<ol>
<li><strong>立即吊销 / 轮换密钥</strong>（这是唯一有效的补救，改历史只是清理痕迹）</li>
<li>用 <code>git filter-repo</code> 从全历史删除该文件（见高级篇）</li>
<li>全员重新克隆</li>
<li>排查密钥使用日志，评估影响面</li>
<li>复盘：为什么绕过了第一层和第二层</li>
</ol>
<hr>
<h2 id="cicd-中的-git-技巧">CI/CD 中的 Git 技巧</h2>
<h3 id="浅克隆--按需加深">浅克隆 + 按需加深</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#6272a4"># GitHub Actions</span>
</span></span><span style="display:flex;"><span>- <span style="color:#ff79c6">uses</span>: actions/checkout@v4
</span></span><span style="display:flex;"><span>  <span style="color:#ff79c6">with</span>:
</span></span><span style="display:flex;"><span>    <span style="color:#ff79c6">fetch-depth</span>: <span style="color:#bd93f9">0</span>        <span style="color:#6272a4"># 需要 tag / 全历史（如算版本号）时才用 0，否则保持默认 1</span>
</span></span></code></pre></div><h3 id="只跑受影响范围的测试">只跑受影响范围的测试</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 与合并基比较，得到改动文件列表</span>
</span></span><span style="display:flex;"><span>git diff --name-only origin/main...HEAD | grep -q <span style="color:#f1fa8c">&#39;^apps/web/&#39;</span> <span style="color:#ff79c6">&amp;&amp;</span> run_web_tests
</span></span></code></pre></div><h3 id="版本号来自-git">版本号来自 Git</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git describe --tags --always --dirty
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># v1.2.0-5-ga1b2c3d  → 距 v1.2.0 之后第 5 个提交，哈希 a1b2c3d</span>
</span></span></code></pre></div><p>这是给构建产物打版本标识的零维护方案，前端塞进 <code>__VERSION__</code>、后端塞进 <code>/healthz</code> 返回值。</p>
<h3 id="自动打-tag--生成-changelog">自动打 tag / 生成 CHANGELOG</h3>
<p>release-please 或 semantic-release 会读取 Conventional Commits，自动算出版本号、生成 CHANGELOG、创建 release PR 和 tag。从此&quot;发版&quot;不是一个手工动作。</p>
<h3 id="保护分支--必需检查">保护分支 + 必需检查</h3>
<p>平台侧配置（GitHub Branch Protection / GitLab Protected Branches）：</p>
<ul>
<li>禁止直接 push main，必须走 PR</li>
<li>必须 ≥1 人 approve</li>
<li>必须通过 CI 检查</li>
<li>禁止 force push 和删除</li>
</ul>
<hr>
<h2 id="灾难恢复-sop">灾难恢复 SOP</h2>
<h3 id="场景-1误删了远程分支">场景 1：误删了远程分支</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 任何拉过该分支的人本地都有完整历史</span>
</span></span><span style="display:flex;"><span>git switch -c feature/xxx &lt;已知的最新提交id 或 reflog 里找&gt;
</span></span><span style="display:flex;"><span>git push -u origin feature/xxx
</span></span></code></pre></div><h3 id="场景-2对-main-执行了-force-push覆盖掉别人的提交">场景 2：对 main 执行了 force push，覆盖掉别人的提交</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span><span style="color:#6272a4"># 找到被覆盖前的 main 位置（任何同事本地、CI 缓存、或平台 Events API）</span>
</span></span><span style="display:flex;"><span>git reflog show main           <span style="color:#6272a4"># 在还没 pull 的机器上</span>
</span></span><span style="display:flex;"><span>git push --force origin &lt;旧提交id&gt;:main
</span></span></code></pre></div><p>教训：<strong>在平台设置里禁掉 main 的 force push</strong>，从制度上消灭这个场景。</p>
<h3 id="场景-3仓库本地损坏对象丢失索引损坏">场景 3：仓库本地损坏（对象丢失、索引损坏）</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git fsck --full                <span style="color:#6272a4"># 诊断</span>
</span></span><span style="display:flex;"><span>git clone &lt;remote-url&gt; rescue  <span style="color:#6272a4"># 最省事：远程还在就直接重克隆</span>
</span></span><span style="display:flex;"><span><span style="color:#6272a4"># 远程也没了但同事机器上有：任何人的 clone 都是完整备份</span>
</span></span></code></pre></div><h3 id="场景-4rebase--amend-错了想回到操作前">场景 4：rebase / amend 错了，想回到操作前</h3>
<div class="highlight"><pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-bash" data-lang="bash"><span style="display:flex;"><span>git reflog                     <span style="color:#6272a4"># 找到 rebase 之前的 HEAD 位置</span>
</span></span><span style="display:flex;"><span>git reset --hard HEAD@<span style="color:#ff79c6">{</span>5<span style="color:#ff79c6">}</span>
</span></span></code></pre></div><h3 id="场景-5整个-git-目录被删了">场景 5：整个 .git 目录被删了</h3>
<p>未推送的提交彻底丢失——<strong>这就是为什么&quot;每天至少 push 一次&quot;写进了初级篇</strong>。能做的只有从远程重新克隆，工作区文件还在的话手动对比恢复。</p>
<p><strong>通用原则</strong>：Git 里 99% 的&quot;丢失&quot;都能用 <code>git reflog</code> + <code>git fsck</code> 找回；剩下 1% 是&quot;从未 commit / 从未 push&quot;，只能靠备份习惯预防。</p>
<hr>
<h2 id="一套可直接落地的团队-git-规范">一套可直接落地的团队 Git 规范</h2>
<p>以下是本篇的浓缩，可以直接贴进团队 Wiki：</p>
<h3 id="分支">分支</h3>
<ol>
<li><code>main</code> 永远可发布，受保护，禁止直推、禁止 force push</li>
<li>功能分支命名：<code>&lt;type&gt;/&lt;简述&gt;</code>，如 <code>feat/sms-login</code>、<code>fix/pay-timeout</code></li>
<li>功能分支活不过 3 天，合并后删除</li>
</ol>
<h3 id="提交">提交</h3>
<ol start="4">
<li>遵循 Conventional Commits，commitlint 强制</li>
<li>一次提交做一件事；<code>wip</code>、<code>tmp</code> 类提交合并前必须 squash</li>
<li>开启 SSH 提交签名</li>
</ol>
<h3 id="同步">同步</h3>
<ol start="7">
<li>每天开工先 <code>git rebase origin/main</code>（功能分支上）</li>
<li>每天至少 push 一次</li>
<li>公共分支只用 merge，功能分支同步用 rebase，永不 rebase 公共历史</li>
</ol>
<h3 id="评审">评审</h3>
<ol start="10">
<li>一切代码经 PR 合入 main，CI 通过 + 至少 1 人 approve</li>
<li>PR 保持小：≤400 行变更为佳，大了拆</li>
</ol>
<h3 id="发布">发布</h3>
<ol start="12">
<li>tag 遵循 semver，由 release-please 自动生成</li>
<li>线上 bug 走 <code>hotfix/*</code> 分支，修完同时回合 main</li>
</ol>
<h3 id="安全">安全</h3>
<ol start="14">
<li>pre-commit 跑 gitleaks，服务端开 Push Protection</li>
<li>密钥泄漏 = 立即轮换，其次才是清历史</li>
</ol>
<hr>
<h2 id="终极速查表">终极速查表</h2>
<table>
	<thead>
			<tr>
					<th>场景</th>
					<th>方案</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>大仓库克隆</td>
					<td><code>--filter=blob:none</code> + sparse-checkout</td>
			</tr>
			<tr>
					<td>提交规范</td>
					<td>Conventional Commits + commitlint</td>
			</tr>
			<tr>
					<td>自动发版</td>
					<td>release-please / semantic-release</td>
			</tr>
			<tr>
					<td>构建版本号</td>
					<td><code>git describe --tags --always --dirty</code></td>
			</tr>
			<tr>
					<td>提交防伪</td>
					<td>SSH 签名 + 平台 Verified</td>
			</tr>
			<tr>
					<td>密钥扫描</td>
					<td>gitleaks（本地）+ Push Protection（服务端）</td>
			</tr>
			<tr>
					<td>密钥泄漏</td>
					<td>轮换密钥 → filter-repo 清历史 → 全员重克隆</td>
			</tr>
			<tr>
					<td>误删/覆盖找回</td>
					<td><code>git reflog</code> → <code>reset --hard</code> / 强推恢复</td>
			</tr>
			<tr>
					<td>仓库损坏</td>
					<td><code>git fsck</code> 诊断，重新克隆</td>
			</tr>
			<tr>
					<td>协作红线</td>
					<td>保护 main、禁 force push、一切走 PR</td>
			</tr>
	</tbody>
</table>
<hr>
<h2 id="系列回顾">系列回顾</h2>
<ul>
<li><strong>初级篇</strong>：四个区域、三板斧、远程同步——能用</li>
<li><strong>中级篇</strong>：分支模型、merge/rebase、reflog——用得对</li>
<li><strong>高级篇</strong>：对象模型、bisect、worktree、submodule、LFS——懂原理</li>
<li><strong>终极篇</strong>：工程化、安全、SOP、团队规范——成体系</li>
</ul>
<p>Git 的学习曲线不在命令数量，而在心智模型。四篇读完，剩下的就是每天在真实项目里踩坑、翻 reflog、读 graph——祝你的每一次 <code>git push</code> 都心安理得。</p>
]]></content:encoded></item></channel></rss>