git合并时index出现unmerged状态,是因为冲突文件在index中被记录为stage 1(base)、stage 2(head)和stage 3(merge_head)三个版本,形成三槽位结构,仅当所有路径只剩stage 0才视为解决。

合并时index为什么会出现unmerged状态
Git合并分支时,index(暂存区)会记录冲突文件的多个版本,这是它进入unmerged状态的根本原因。不是所有文件都会变,只有那些在两个分支中都被修改、且修改不兼容的文件才会被标记为unmerged。
此时git ls-files -s能看到同一路径对应三行输出:1表示base(共同祖先)、2表示HEAD(当前分支)、3表示MERGE_HEAD(待合并分支)。这三行就是index里“三个槽位”的实际体现。
- 如果某文件只在一个分支改动,Git能自动合并,index里只保留一个条目(stage 0),不会触发unmerged
- 手动执行
git add某个冲突文件后,对应路径的stage 1/2/3条目会被清除,只留下stage 0 —— 这才是真正的“解决冲突”动作 -
git status显示“both modified”本质是读取了index中stage 2和stage 3同时存在这一事实
merge过程index如何从stage 0变成stage 1/2/3
执行git merge前,index全是stage 0(已暂存/已跟踪文件)。一旦检测到冲突,Git会把冲突文件的三个版本写入index,但不会覆盖工作区——工作区仍保留你本地的修改内容。
关键点在于:index此时成了“冲突元数据容器”,它不保存完整文件内容,而是存blob hash + mode + path + stage编号。你可以用git cat-file -p <hash></hash>查看每个stage对应的实际内容。
- stage 1:来自merge base的blob hash,代表“原始版本”
- stage 2:来自HEAD的blob hash,代表“你改了什么”
- stage 3:来自MERGE_HEAD的blob hash,代表“对方改了什么”
- 工作区文件内容默认保持HEAD版本(即你本地的修改),Git不做覆盖,除非你显式
git checkout --ours或--theirs
为什么git commit能直接提交unmerged index
因为Git允许commit包含stage 1/2/3条目的index,这种commit就是所谓的“merge commit”。它本身不包含diff,而是在tree对象里引用多个parent,靠tree结构隐含三方合并结果。
批量替换指定目录下所有 Git 仓库的远程地址(remote URL)。 当用户需要将 Git 仓库从一个服务器迁移到另一个服务器时使用。 触发词:git remote 替换、git url 批量修改、git 仓库迁移、更换 git 地址、批量修改 remote url。
注意:git commit成功不代表冲突已解决,只是把当前index状态固化为一次提交。如果你没清理stage 2/3,这个commit仍然是未解决冲突的状态,后续git log --merges能看到,但git status仍报冲突。
- 只有当所有路径都只剩stage 0条目时,index才被视为“clean”,
git commit才会生成普通commit - 强行
git commit -m "merge"跳过检查,会产生一个含unmerged entry的commit,但别人checkout时会立刻遇到相同冲突 -
git merge --no-commit就是故意停在这一步,让你手动调协index再commit
调试index变化最有效的几个命令
别依赖GUI或IDE的冲突提示,直接看index才能确认真实状态。以下命令组合能快速定位问题:
git ls-files -s —— 查看哪些文件处于stage 1/2/3;git ls-files --unmerged —— 只列出unmerged路径;git diff --cached —— 显示index中stage 2 vs stage 3的差异(即你和对方修改的差别);git show :1:file.txt —— 查看stage 1内容(base版本)。
-
:1、:2、:3是git内部语法,分别对应三个stage,只能在git show或git cat-file中使用 -
git checkout --ours file.txt本质是把stage 2内容写入工作区并git add,清掉stage 2/3 - 误删了工作区文件?别慌,
git checkout :2 -- file.txt能从stage 2恢复——index里一直存着hash,只要blob还在object db里就能找回
index不是临时缓存,它是Git操作的核心状态寄存器。合并时它承载了“三方比较”的全部上下文,理解它怎么存、怎么读、怎么清,比背merge策略更重要。真正卡住的时候,先ls-files -s,再决定下一步。










