首页  /  云原生  /  正文

Git 分支与合并冲突

云原生 2026-10-02📖 13 分钟👁 —
🌿 云原生 · Git第 3 / 5 页12345📖 完整导航 →

一、分支是干什么的

一句话:并行开发而不影响正常使用,而且随时能回滚。几条分支各自往前走,需要的时候再合回来:

hot-fix、master、feature-blue、feature-game 几条分支各自往前跑,最后再合回来

二、查看、创建、切换

# 查看所有分支(* 表示当前所在分支)
git branch

# 查看(-v 全称:--verbose,显示每个分支的最新提交信息)
git branch -v # -v 可选

# 创建分支(从当前位置创建)
git branch 分支名

# 切换分支(checkout 全称:检出/切换到指定分支)
git checkout 分支名

# 创建并切换分支(一步到位)
git checkout -b 分支名

# 删除分支
git branch -d 分支名

创建一个热修分支:

$ git branch hot-fix

$ git branch -v
  hot-fix 4e1a936 first commit
* master  4e1a936 first commit

# 通过命令提示符可以查看当前所在分支
圣果@shengguo MINGW64 /demo (master)

分支在磁盘上就是一个文件,.git/refs/heads/ 下面能直接看到:

.git 里的 refs/heads 目录,master 和 hot-fix 各是一个文件

切过去:

圣果@shengguo MINGW64 /demo (master)
$ git checkout hot-fix
Switched to branch 'hot-fix'

圣果@shengguo MINGW64 /demo (hot-fix)
# 命令提示符也变了

三、在分支上改东西

$ vim hello.txt
$ git add hello.txt
# 因为之前执行了 git add,Git 已经知道你这次要提交哪些文件了
# 所以不用加文件名也可以
$ git commit -m "fix-01"

$ git reflog
873e568 (HEAD -> hot-fix) HEAD@{0}: commit: fix-01
4e1a936 (master) HEAD@{1}: checkout: moving from master to hot-fix
4e1a936 (master) HEAD@{2}: reset: moving to 4e1a936
4e1a936 (master) HEAD@{3}: reset: moving to 4e1a936
5972f9e HEAD@{4}: reset: moving to 5972f9e
ed2db57 HEAD@{5}: commit: third commit
5972f9e HEAD@{6}: commit: second commit
4e1a936 (master) HEAD@{7}: commit (initial): first commit

reflog 每一行的四个位置分别是这个意思:

873e568 (HEAD -> hot-fix) HEAD@{0}: commit: fix-01
  ①           ②             ③         ④

              分支指针     操作记录    提交内容

四、合并 git merge 和快进

# 合并(把指定分支合并到当前分支上)
git merge 分支名

切回 master,把 hot-fix 合进来。这次 master 上没有任何新提交,所以是一次快进合并,合完分支指针同时指向两个分支:

合并之后 HEAD 同时指向 master 和 hot-fix,reflog 里记着 merge hot-fix: Fast-forward

reflog 里关键的三行:

873e568 (HEAD -> master, hot-fix) HEAD@{0}: merge hot-fix: Fast-forward
4e1a936 HEAD@{1}: checkout: moving from hot-fix to master
873e568 (HEAD -> master, hot-fix) HEAD@{2}: commit: fix-01

Fast-forward 就是“快进”:当前分支没有新提交,git 直接把指针往前挪一格就完事,不会产生新的提交节点。所以 git 定位版本很简单,找分支指针和版本指针就行。

五、合并冲突

什么时候会冲突?两个分支在同一个文件的同一个位置,有两套完全不同的修改,Git 没法全合并,就得自己选一个。

先在 master 上改文件并提交,再到 hot-fix 上改文件并提交,然后回 master 合并:

# 在 master 分支修改文件并commit
$ git commit -m "master test" hello.txt

# 再修改 hot-fix 分支的文件
$ git commit -m "hot-fix test"

# 到 master 分支融合
圣果@shengguo MINGW64 /demo (master)
$ git merge hot-fix
Auto-merging hello.txt
CONFLICT (content): Merge conflict in hello.txt
Automatic merge failed; fix conflicts and then commit the result.

命令提示符会变成合并中状态,git status 也会把冲突文件标出来:

# status 标红,分支显示变为合并中
圣果@shengguo MINGW64 /demo (master|MERGING)
$ git status
On branch master
You have unmerged paths.
  (fix conflicts and run "git commit")
  (use "git merge --abort" to abort the merge)

Unmerged paths:
  (use "git add <file>..." to mark resolution)
        both modified:   hello.txt

no changes added to commit (use "git add" and/or "git commit -a")

打开文件,git 用三个符号把两边的代码标出来了:

# 查看文件(git通过三个符号 < > = 标注)
$ vim hello.txt

hengguo niubi

hengguo niubidwadsadwa
henggu
henggudwaghjko niubi
hengguo niubiwafdasd
<<<<<<< HEAD
======= # 这俩之间是当前分支代码
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hotfix-addaddaddadd
>>>>>>> hot-fix # 这之间是要合并的代码

假设只想保留 hot-fix 分支的 hotfix-addaddaddadd 这部分代码,那就分两步手改:先把不要的那半删掉,再把 <<<<<<<、=======、>>>>>>> 这三行标注删掉,wq 退出。改完是这样:

$ vim hello.txt

hengguo niubi

hengguo niubidwadsadwa
henggu
henggudwaghjko niubi
hengguo niubiwafdasd
hotfix-addaddaddadd

也就是只保留 git 标注之间想要的部分,不要的删掉。

现在的文件是人工合并的结果,还没被追踪,要先 add 到暂存区。但这时候不能带文件名直接 commit:

圣果@shengguo MINGW64 /demo (master|MERGING)
$ git add hello.txt

圣果@shengguo MINGW64 /demo (master|MERGING)
$ git commit -m "finish test" hello.txt
fatal: cannot do a partial commit during a merge.
# 翻译:致命错误:在合并期间无法进行部分提交

提交冲突代码要不带文件名:

Git 的逻辑是:合并是一个“原子操作”,要么整个合并完成,要么整个不完成,不允许“合并到一半,只提交其中一个文件”。现在处于合并中状态,下一步该做的是完成合并,而不是提交某个具体文件。

圣果@shengguo MINGW64 /demo (master|MERGING)
$ git commit -m "finish test"
[master 37588e4] finish test

# 此时用户提示符也变了
圣果@shengguo MINGW64 /demo (master)
$ git status
On branch master
nothing to commit, working tree clean

master 上 cat 出来的就是刚才那份文件。再切到另一个分支看看:

# 进到另一分支查看文件
圣果@shengguo MINGW64 /demo (hot-fix)
$ cat hello.txt
hengguo niubi

hengguo niubidwadsadwa
henggu
henggudwaghjko niubi
hengguo niubiwafdasd
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hengguo niubi
hotfix-addaddaddadd
# 这里还是合并之前的分支

另一分支还是合并之前的,说明 git 只在所在的分支做合并操作,不影响其他分支,这样回退起来也干净。

六、顺手记两个命令

分支用多了,tag 和 stash 迟早碰上:

# 打标签(常用于标记版本,比如 v1.0)
git tag v1.0

# 查看所有标签
git tag

# 推送标签到远程库(默认 push 不带标签)
git push origin v1.0

# 临时保存当前没提交的修改
git stash

# 查看存起来的列表
git stash list

# 恢复最近一次 stash 并把它从列表里删掉
git stash pop

stash 在“手头改到一半、突然要切分支去救火”的时候特别有用:先存起来,切回来再 pop 出来接着改。