解决 Git 合并冲突

内容纲要

查看【Git】专题可浏览更多内容

场景一:不同人修改了不同文件

假设 A 与 B 两人协作开发,A 修改了 A 文件并提交推送,此时当 B 修改了 B 文件(各改各的),在 B 提交后使用 git push 命令时就会遇到:

 ! [rejected]        dev -> dev (fetch first)
error: 无法推送一些引用到 'https://example.com/git-repo.git'
提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外
提示:一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更
提示:(如 'git pull ...')。
提示:详见 'git push --help' 中的 'Note about fast-forwards' 小节。

此时查看状态:

git status
位于分支 dev
您的分支和 'origin/dev' 出现了偏离,
并且分别有 1 和 1 处不同的提交。
  (使用 "git pull" 来合并远程分支)

无文件要提交,干净的工作区

解决方法

此时对于 B 来说,有一个领先于远程仓库的提交(B 文件的修改)和一个落后于远程仓库的提交(A 对 A 文件的修改)。

那么解决方法也很简单,先拉取再推送即可:

git pull

使用 git pull 后会弹出一个交互式界面:

Merge branch 'dev' of https://example.com/git-repo.git dev
# 请输入一个提交信息以解释此合并的必要性,尤其是将一个更新后的上游分支
# 合并到主题分支。
#
# 以 '#' 开始的行将被忽略,而空的提交说明将终止提交。

这里其实就是一个改动的提交信息,使用这默认信息或修改第一行为你需要的提交信息即可。

然后再进行推送:

git push

有些方法会使用 git fetch + git merge,但实际上 git pull 就是两者的组合。

场景二:不同人修改了相同文件的不同部分

假设 A 与 B 两人协作开发,A 修改了文件 A 的第一行后提交并推送,而 B 修改了文件 A 的第二行,在提交后推送时,遇到提示:

 ! [rejected]        dev -> dev (fetch first)
error: 无法推送一些引用到 'https://example.com/git-repo.git'
提示:更新被拒绝,因为远程仓库包含您本地尚不存在的提交。这通常是因为另外
提示:一个仓库已向该引用进行了推送。再次推送前,您可能需要先整合远程变更
提示:(如 'git pull ...')。
提示:详见 'git push --help' 中的 'Note about fast-forwards' 小节。

解决方法

解决方法其实和「场景一」相同,先拉取远程仓库的数据处理好再推送即可:

git pull

# 或
git fetch
git merge origin/dev # 此处示例的分支为 dev

使用 git pullgit merge,Git 会将 A 的修改内容进行合并,成功后会弹出一个交互式界面:

Merge branch 'dev' of https://example.com/git-repo.git dev
# 请输入一个提交信息以解释此合并的必要性,尤其是将一个更新后的上游分支
# 合并到主题分支。
#
# 以 '#' 开始的行将被忽略,而空的提交说明将终止提交。

这里其实就是一个改动的提交信息,使用这默认信息或修改第一行为你需要的提交信息即可。

然后再进行推送:

git push

场景三:不同人修改了相同文件的相同部分

假设目前有个 Markdown 文件是这样的:

# 标题1

## 标题2

标题

#### 标题4

##### 标题5

###### 标题6

出问题的是标题3,它应该是这样的:

### 标题3

仍然假设 A 与 B 两人协作开发,A 修改成了这样:

### 标题

也就是加了井号但是没加上数字 3,然后 A 提交并推送了。

然后 B 修改成了这样:

标题3

加上了数字 3 但没加井号。

此时 B 提交后使用 git pull 会收到这样的提示:

remote: Enumerating objects: 5, done.
remote: Counting objects: 100% (5/5), done.
remote: Compressing objects: 100% (3/3), done.
remote: Total 3 (delta 0), reused 3 (delta 0), pack-reused 0
展开对象中: 100% (3/3), 301 字节 | 100.00 KiB/s, 完成.
来自 https://example.com/git-repo.git
   c26a0c0..2e709cc  dev        -> origin/dev
自动合并 README.md
冲突(内容):合并冲突于 README.md
自动合并失败,修正冲突然后提交修正的结果。

解决方法

打开冲突文件:

# 标题1

## 标题2

<<<<<<< HEAD
标题3
=======
### 标题
>>>>>>> origin/dev

#### 标题4

##### 标题5

###### 标题6

Git 是没办法知道我们想怎么解决冲突问题的,所以它只能给予帮助提示:

  • <<<<<<< HEAD 开头与 >>>>>>> origin/dev 结尾中间的部分是存在冲突的;
  • ======= 以上的部分是自己(B)所做的改动的部分;
  • ======= 以下的部分是来自远程仓库上,也就是 A 推送上去的改动;

最终,B 在与 A 沟通后并结合 B 自身的修改,最终 B 将文件修改如下:

# 标题1

## 标题2

### 标题3

#### 标题4

##### 标题5

###### 标题6

然后使用命令:

# 添加修正的冲突的文件
git add READMD.md
git commit -m '解决了冲突'

git push
枚举对象中: 10, 完成.
对象计数中: 100% (10/10), 完成.
使用 8 个线程进行压缩
压缩对象中: 100% (6/6), 完成.
写入对象中: 100% (6/6), 653 字节 | 653.00 KiB/s, 完成.
总共 6(差异 0),复用 0(差异 0),包复用 0
To https://example.com/git-repo.git
   2e709cc..bc24593  dev -> dev

成功了,来看看提交了几次以及分支情况:

git log --graph --pretty=short
*   commit bc245936278fda2b14c939b20928e7ba4877c8a6 (HEAD -> dev, origin/dev)
|\  Merge: 5aa90e7 2e709cc
| | Author: b <[email protected]>
| |
| |     b:修正冲突
| |
| * commit 2e709ccd4b67b74223b2d78e4f0277d1479c6e46
| | Author: a <[email protected]>
| |
| |     a:加上标题
| |
* | commit 5aa90e7507dbdcfb3931e6c87166627cd3583366
|/  Author: b <[email protected]>
|
|       b:增加了数字3
|
* commit c26a0c03c534d9a277366c7c52292fb24a3d3ea2
| Author: x <[email protected]>
|
|     标题初稿

在发生共同冲突的时候,建议需要与他人协商讨论修改结果,尽可能的不要改动他人的部分,毕竟是团队协作嘛。