<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Worktree on 巴巴变的博客</title><link>https://blog.bugxch.top/tags/worktree/</link><description>Recent content in Worktree on 巴巴变的博客</description><generator>Hugo -- gohugo.io</generator><language>zh-cn</language><lastBuildDate>Fri, 11 Sep 2026 00:00:00 +0000</lastBuildDate><atom:link href="https://blog.bugxch.top/tags/worktree/index.xml" rel="self" type="application/rss+xml"/><item><title>根据agent学习git实录</title><link>https://blog.bugxch.top/p/%E6%A0%B9%E6%8D%AEagent%E5%AD%A6%E4%B9%A0git%E5%AE%9E%E5%BD%95/</link><pubDate>Fri, 11 Sep 2026 00:00:00 +0000</pubDate><guid>https://blog.bugxch.top/p/%E6%A0%B9%E6%8D%AEagent%E5%AD%A6%E4%B9%A0git%E5%AE%9E%E5%BD%95/</guid><description>&lt;img src="https://image-1258996033.cos.ap-shanghai.myqcloud.com/westlake" alt="Featured image of post 根据agent学习git实录" /&gt;&lt;p&gt;过去学 Git，模式总是&amp;quot;看教程 → 记命令 → 用时想不起来&amp;quot;。这次换了个方式：让 AI Agent 当陪练，我负责提问和质疑，它负责做实验，我们一起看真实输出。整个过程只遵循一条原则——&lt;strong&gt;任何结论都要用命令跑出来，不接受&amp;quot;应该是这样&amp;quot;&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;p&gt;这篇实录记录了一天内从图形界面到 &lt;code&gt;git worktree&lt;/code&gt; 多 agent 并行协作的完整过程，以及一个让我印象深刻的结论：&lt;strong&gt;当我可以随时打断、质疑、要求验证时，知识吸收率远超单向听讲&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;h1 id="学习方式问题驱动--实验验证"&gt;学习方式：问题驱动 + 实验验证&#10;&lt;/h1&gt;&lt;p&gt;这套方式有三个特点：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;问题驱动&lt;/strong&gt;：我的疑惑就是课程，没有固定大纲。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;实验验证&lt;/strong&gt;：每个结论都在独立的练习仓库里跑一遍，用真实输出说话。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;实时记录&lt;/strong&gt;：每一步问答、每一次纠错都写进笔记，形成可复习的轨迹。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;最有意思的是&amp;quot;质疑&amp;quot;环节。当我发现 Agent 的说法和我的推理对不上时，它不会用&amp;quot;你应该相信我&amp;quot;来回应，而是直接建仓库、敲命令，让事实裁决。下面就有两次它当场被我说服、修正结论的例子。&lt;/p&gt;&#10;&lt;h1 id="第一站trae-源代码管理面板"&gt;第一站：Trae 源代码管理面板&#10;&lt;/h1&gt;&lt;h2 id="三区模型"&gt;三区模型&#10;&lt;/h2&gt;&lt;p&gt;Git 的工作流可以概括为三个区（加上远程共四个）：&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;区域&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;含义&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;快递类比&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Trae 面板&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;工作区&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;你正在编辑的文件&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;桌面上摊开的东西&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;更改&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;暂存区（index）&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;准备提交的清单&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;购物车&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;暂存的更改&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;本地仓库&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;已提交的历史&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;已发货订单&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Git Graph 圆点&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;远程仓库&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;服务器上的副本&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;快递公司仓库&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;h2 id="u--a--m-是什么"&gt;U / A / M 是什么&#10;&lt;/h2&gt;&lt;p&gt;面板里每个文件旁边有一个字母，起初我以为它表示&amp;quot;文件在哪个区&amp;quot;，其实它表示&lt;strong&gt;文件的改动类型&lt;/strong&gt;：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;U&lt;/code&gt;（Untracked）：新文件，从未被 &lt;code&gt;git add&lt;/code&gt; 过&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;A&lt;/code&gt;（Added）：新文件已经 &lt;code&gt;git add&lt;/code&gt;，进入暂存区&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;M&lt;/code&gt;（Modified）：已跟踪文件的内容被修改&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="一次关键修正字母其实是两列"&gt;一次关键修正：字母其实是&amp;quot;两列&amp;quot;&#10;&lt;/h2&gt;&lt;p&gt;我提出一个疑问：&lt;strong&gt;新建文件 &lt;code&gt;git add&lt;/code&gt; 后再修改，命令行显示 &lt;code&gt;AM&lt;/code&gt;，但相对 HEAD 它仍然是新增文件，为什么不是 &lt;code&gt;A&lt;/code&gt;？&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Agent 当场跑实验：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git add c.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;A c.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ &lt;span class="nb"&gt;echo&lt;/span&gt; b &amp;gt;&amp;gt; c.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;AM c.txt&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;结论：&lt;code&gt;git status --short&lt;/code&gt; 的输出是&lt;strong&gt;两列 XY&lt;/strong&gt;，分别对应两个不同的比较：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;第一列 X = index 与 HEAD 比较&lt;/strong&gt; → 决定「暂存的更改」的字母&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;第二列 Y = 工作区与 index 比较&lt;/strong&gt; → 决定「更改」的字母&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;操作&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;输出&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;暂存列表&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;更改列表&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;新文件 add&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;A &lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;新文件 add 后又改&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;AM&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;已跟踪文件修改&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt; M&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;已跟踪文件修改后 add&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;M &lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;—&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;已跟踪 add 后又改&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;MM&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;M&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;原来同一个文件可以&lt;strong&gt;同时&lt;/strong&gt;拥有 A 和 M 两个字母，它们分属两个不同的比较，互不覆盖。Agent 之前&amp;quot;字母只由 index 与 HEAD 比较决定&amp;quot;的说法，正是在我追问下被实验推翻的。&lt;/p&gt;&#10;&lt;h2 id="index-与-head-到底是什么"&gt;index 与 HEAD 到底是什么&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;HEAD&lt;/strong&gt; 是一个指针，文件 &lt;code&gt;.git/HEAD&lt;/code&gt; 里只有一行 &lt;code&gt;ref: refs/heads/main&lt;/code&gt;，指向当前分支最近一次提交。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;index（暂存区）&lt;/strong&gt; 是一个二进制文件 &lt;code&gt;.git/index&lt;/code&gt;，记录&amp;quot;路径 → 内容指纹&amp;quot;的清单。&lt;code&gt;git add&lt;/code&gt; 往清单里写，&lt;code&gt;git commit&lt;/code&gt; 把清单固化成新快照。&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;于是有两次比较：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;工作区 worktree ──比较①──&amp;gt; index ──比较②──&amp;gt; HEAD&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 「更改」 「暂存的更改」&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h1 id="第二站git-graph"&gt;第二站：Git Graph&#10;&lt;/h1&gt;&lt;p&gt;Git Graph 是 VSCode 生态的插件，本质上是 &lt;code&gt;git log --oneline --graph --decorate --all&lt;/code&gt; 的可视化。看懂它，要抓住两个元素：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;圆点 = 一个 commit&lt;/strong&gt;（不可变快照，包含整棵树 + 父指针 + 作者 + 消息）&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;线 = parent 指针&lt;/strong&gt;，表示提交之间的父子关系&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="merge-commit-的双-parent"&gt;merge commit 的双 parent&#10;&lt;/h2&gt;&lt;p&gt;我一度以为 merge commit 的直接父是&amp;quot;历史里更早的那个提交&amp;quot;，实测纠正了这个误解：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git show daa14b7 --format&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="s1"&gt;&amp;#39;%h parents=%P&amp;#39;&lt;/span&gt; -s&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;daa14b7 &lt;span class="nv"&gt;parents&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;fa3a2f5 aaddc62&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;merge commit 有 &lt;strong&gt;2 个直接 parent&lt;/strong&gt;（main 侧和 feature 侧各一个），而 root commit 有 &lt;strong&gt;0 个 parent&lt;/strong&gt;（历史的起点）。&lt;code&gt;parent&lt;/code&gt; 只看&amp;quot;直接父&amp;quot;，不跨层。&lt;/p&gt;&#10;&lt;h2 id="head---main-是什么意思"&gt;HEAD -&amp;gt; main 是什么意思&#10;&lt;/h2&gt;&lt;p&gt;图中某次提交旁标注 &lt;code&gt;HEAD -&amp;gt; main&lt;/code&gt;，表示：当前检出的分支是 &lt;code&gt;main&lt;/code&gt;，HEAD 指针经由 main 指向该分支的 tip。执行 &lt;code&gt;git checkout feat&lt;/code&gt; 后，标记会变成 &lt;code&gt;HEAD -&amp;gt; feat&lt;/code&gt;。&lt;/p&gt;&#10;&lt;h1 id="第三站git-worktree-多-agent-并行"&gt;第三站：git worktree 多 agent 并行&#10;&lt;/h1&gt;&lt;p&gt;这是本次学习的重头戏。普通 Git 仓库一次只能在一个目录检出一个分支；多个 agent 同时工作时共用目录会互相覆盖文件。&lt;code&gt;git worktree&lt;/code&gt; 为同一个仓库创建多个工作目录：&lt;strong&gt;每个目录有独立的工作区、index 和 HEAD，但共享对象数据库&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 共享 .git 对象库&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; / | \&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; 主工作区 worktree-a worktree-b&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; main feature/a feature/b&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;h2 id="共享-vs-复制"&gt;共享 vs 复制&#10;&lt;/h2&gt;&lt;p&gt;我提出了一个自认为很关键的问题：&lt;strong&gt;&amp;ldquo;新建两个文件夹分别检出分支&amp;quot;是不是等价于 &lt;code&gt;git worktree&lt;/code&gt;？&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Agent 用实验区分了两种做法。先建一个裸仓库当&amp;quot;远端&amp;rdquo;，在本地建一个&lt;strong&gt;没 push&lt;/strong&gt; 的分支 &lt;code&gt;feat/a0&lt;/code&gt;：&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;做法 A：新建独立文件夹（clone）&lt;/strong&gt;&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git clone remote.git work2 &amp;amp;&amp;amp; cd work2 &amp;amp;&amp;amp; git branch -a&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* main&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; remotes/origin/main&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git checkout feat/a0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;error: pathspec &amp;#39;feat/a0&amp;#39; did not match any file(s) known to git&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;未 push 的分支，新 clone 的文件夹根本看不到。&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;做法 B：&lt;code&gt;git worktree add&lt;/code&gt;&lt;/strong&gt;&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git worktree add ../work1-b feat/a0&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;Preparing worktree (checking out &amp;#39;feat/a0&amp;#39;)&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;HEAD is now at e3f692e feat/a0&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;零 push、零 clone，直接检出。&lt;/p&gt;&#10;&#10; &lt;blockquote&gt;&#10; &lt;p&gt;结论：&lt;strong&gt;clone = 复制仓库（独立对象库），worktree = 共享仓库（同一对象库多个视图）&lt;/strong&gt;。worktree 因为共享本地对象库，本地私有分支在所有 worktree 里天然可见——这正是它能支撑多 agent 本地协作的本质。&lt;/p&gt;&#10;&#10; &lt;/blockquote&gt;&#10;&lt;h2 id="一个分支只能被一个-worktree-检出"&gt;一个分支只能被一个 worktree 检出&#10;&lt;/h2&gt;&lt;p&gt;实测报错最能说明问题：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-text" data-lang="text"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git checkout main&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fatal: &amp;#39;main&amp;#39; is already used by worktree at &amp;#39;/private/tmp/git-work&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git worktree add ../x feature/a&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;fatal: &amp;#39;feature/a&amp;#39; is already used by worktree at &amp;#39;/private/tmp/git-work-a&amp;#39;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;p&gt;原因是分支是一个&amp;quot;会移动的指针&amp;quot;，如果两个工作区同时检出同一分支，两边 commit 时指针该听谁的？Git 用&amp;quot;一分支一工作区&amp;quot;保证指针移动的唯一性。&lt;/p&gt;&#10;&lt;h2 id="git-branch--avv-里的--和-"&gt;&lt;code&gt;git branch -avv&lt;/code&gt; 里的 &lt;code&gt;+&lt;/code&gt; 和 &lt;code&gt;*&lt;/code&gt;&#10;&lt;/h2&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;*&lt;/code&gt; = 当前工作区检出的分支&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;+&lt;/code&gt; = 被&lt;strong&gt;其他&lt;/strong&gt; linked worktree 检出的分支（括号里是占用它的路径）&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;它和&amp;quot;分支从哪来&amp;quot;无关，只和&amp;quot;此刻被哪个工作区占用&amp;quot;有关。&lt;/p&gt;&#10;&lt;h2 id="一次事故复盘"&gt;一次事故复盘&#10;&lt;/h2&gt;&lt;p&gt;练习中途，我为了清理分支，直接 &lt;code&gt;rm -rf&lt;/code&gt; 删掉了练习仓库再重建，结果把前面积累的完整历史（merge 结构、双 worktree）全丢了。Agent 用 &lt;code&gt;git reflog&lt;/code&gt; 只剩一条初始提交的证据确认了这次&amp;quot;推倒重来&amp;quot;，并强调了操作边界：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;git branch -d&lt;/code&gt; 只删分支指针&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;git worktree remove&lt;/code&gt; 只删工作区&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;rm -rf&lt;/code&gt; + &lt;code&gt;git init&lt;/code&gt; 是整个仓库推倒重来&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;这次事故本身也成了很好的教材——&lt;strong&gt;练习环境也要当成真实环境对待，删除前先确认&lt;/strong&gt;。&lt;/p&gt;&#10;&lt;h1 id="第四站merge-的三种结局"&gt;第四站：merge 的三种结局&#10;&lt;/h1&gt;&lt;p&gt;最后用两个对比实验搞清了 &lt;code&gt;git merge&lt;/code&gt;：&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;div class="chroma"&gt;&#10;&lt;table class="lntable"&gt;&lt;tr&gt;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code&gt;&lt;span class="lnt"&gt;1&#10;&lt;/span&gt;&lt;span class="lnt"&gt;2&#10;&lt;/span&gt;&lt;span class="lnt"&gt;3&#10;&lt;/span&gt;&lt;span class="lnt"&gt;4&#10;&lt;/span&gt;&lt;span class="lnt"&gt;5&#10;&lt;/span&gt;&lt;span class="lnt"&gt;6&#10;&lt;/span&gt;&lt;span class="lnt"&gt;7&#10;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&#10;&lt;td class="lntd"&gt;&#10;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-bash" data-lang="bash"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 情形一：有分叉 → 生成 merge commit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git merge feature/a&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;* 1a24e48 Merge branch &lt;span class="s1"&gt;&amp;#39;feature/a&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# 双 parent&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="c1"&gt;# 情形二：当前分支是目标祖先 → fast-forward&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;$ git merge ff-test&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;6aac506 ff-test: commit &lt;span class="m"&gt;2&lt;/span&gt; &lt;span class="c1"&gt;# 指针平移，无新 commit&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&#10;&lt;/div&gt;&#10;&lt;/div&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;情形&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;结果&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;两个分支指向同一个 commit&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;Already up to date&lt;/code&gt;（无操作）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;当前分支是目标分支的祖先&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;fast-forward&lt;/strong&gt;，指针平移，无新 commit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;两条线有分叉&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;merge commit&lt;/strong&gt;，生成双 parent 新提交&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;&lt;code&gt;git merge --no-ff&lt;/code&gt; 则强制生成 merge commit，即使本可以快进——这样可以&lt;strong&gt;保留分支拓扑&lt;/strong&gt;，方便日后回溯&amp;quot;这批改动来自哪个分支&amp;quot;。&lt;/p&gt;&#10;&lt;h1 id="这套方法为什么有效"&gt;这套方法为什么有效&#10;&lt;/h1&gt;&lt;p&gt;一天下来，我最大的感受是：&lt;strong&gt;主动提问 + 实验验证 + 当场纠错&lt;/strong&gt;，比任何单向讲解都牢固。几个具体的收获：&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;疑问必须落到命令上&lt;/strong&gt;。&lt;code&gt;AM&lt;/code&gt; 到底该显示 A 还是 M、worktree 和 clone 的差别，这些光靠讲容易含糊，跑一遍就再也不会忘。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;允许并欢迎质疑导师&lt;/strong&gt;。这次 Agent 有两处说法被我的追问推翻（字母的两列模型、worktree 路径规则），修正后的结论反而更清晰。&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;共享 vs 复制&lt;/strong&gt;是理解 worktree 的钥匙。抓住&amp;quot;共享对象库、独立 HEAD/index&amp;quot;这一条，剩下的行为都能推导出来。&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;如果把 worktree 用到日常多 agent 协作，记住这条最小协议：&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;一个 agent 一个 worktree + 一个独立分支，绝不共用目录或分支&lt;/li&gt;&#10;&lt;li&gt;worktree 路径放在仓库外&lt;/li&gt;&#10;&lt;li&gt;交付时报 commit hash + 测试结果&lt;/li&gt;&#10;&lt;li&gt;主工作区统一 merge（需要保留拓扑就用 &lt;code&gt;--no-ff&lt;/code&gt;）&lt;/li&gt;&#10;&lt;li&gt;回收前先 &lt;code&gt;git status&lt;/code&gt; 确认干净，再 &lt;code&gt;git worktree remove&lt;/code&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h1 id="附录worktree-常用命令速查"&gt;附录：worktree 常用命令速查&#10;&lt;/h1&gt;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;命令&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;作用&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git worktree list&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;列出所有 worktree&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git worktree add &amp;lt;path&amp;gt; &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;新建工作区并检出已存在分支&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git worktree add -b &amp;lt;new&amp;gt; &amp;lt;path&amp;gt; &amp;lt;start&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;一步建分支并检出&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git switch &amp;lt;branch&amp;gt;&lt;/code&gt; / &lt;code&gt;git switch -c &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;切换 / 新建并切换分支&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git merge &amp;lt;branch&amp;gt;&lt;/code&gt; / &lt;code&gt;git merge --no-ff &amp;lt;branch&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;合并 / 强制生成 merge commit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git worktree remove &amp;lt;path&amp;gt;&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;删除工作区（会删目录内文件）&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git worktree prune&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;清理失效的 worktree 元数据&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;git branch -avv&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;查看分支及 &lt;code&gt;+&lt;/code&gt;/&lt;code&gt;*&lt;/code&gt; 占用状态&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;官方文档：&lt;a class="link" href="https://git-scm.com/docs/git-worktree" target="_blank" rel="noopener"&#10; &gt;https://git-scm.com/docs/git-worktree&lt;/a&gt;&lt;/p&gt;&#10;&lt;h1 id="参考文献"&gt;参考文献&#10;&lt;/h1&gt;&lt;hr&gt;&#10;&lt;p&gt;本文原载于 &lt;a class="link" href="http://blog.bugxch.top" target="_blank" rel="noopener"&#10; &gt;巴巴变的博客&lt;/a&gt;，遵循CC BY-NC-SA 4.0协议，复制请保留原文出处。&lt;/p&gt;&#10;</description></item></channel></rss>