Git冲突解决

Table of Contents

1 自动合并

1.1 修改不同的文件

参考博文git用户协作

1.2 修改相同文件的不同部分

1.3 同时更改文件名及其内容

如果一个用户将某文件改名或移动到其他目录,另外一个用户针对被重命名前的文件进行了修改,git还是能自动解决冲突、并实现合并的。

2 冲突合并

若两个用户修改了同一文件的同一区域,则git不能完成自动合并。这种情况下,git pull在完成git fetch的操作后,也会在执行git merge时失败中断的。 最好的解决办法是使用图形工具解决冲突。这里使用了kdiff3。 安装配置过程如下,

  • 下载kdiff3
  • 安装:一路回车
  • 为git指定kdiff3作为merge工具,在全局配置文件(git config -e –global)中,添加
[merge]
        tool = kdiff3
[mergetool "kdiff3"]
        path = C:/Program Files/KDiff3/kdiff3.exe
        keepBackup = false
        trustExitCode = false
  • 使用kdiff3进行merge的命令是git mergetool,之后,需要执行git commit和git push操作。
  • 也可将kdiff3作为比较工具使用,需要在全局配置文件中,添加
[diff]
        tool = kdiff3
[difftool "kdiff3"]
        path = C:/Program Files/KDiff3/kdiff3.exe
        keepBackup = false
        trustExitCode = false
  • 使用kdiff3作比较的命令,如git difftool -y, git difftool -v HEAD

3 树合并

当两个用户为同一个文件更名时,git是无法对这两个用户的提交进行合并的,这种冲突称为树冲突。

树冲突是不能使用图形化的工具解决的,可以通过git mergetool以命令行交互式的方法解决。下面介绍个例子。

先进行一些准备工作,初始化一个裸版本库(git-share.git),及两个用户工作目录(git-user1,git-user2),并通过git-user1向其中添加了一个文件(README),供我们随后使用。

$ git init --bare git-share.git
Initialized empty Git repository in c:/Documents and Settings/lhou/git-share.git/
$ git clone ./git-share.git git-user1
Cloning into 'git-user1'...
warning: You appear to have cloned an empty repository.
done.
$ git clone ./git-share.git git-user2
Cloning into 'git-user2'...
warning: You appear to have cloned an empty repository.
done.
$ cd ./git-user1/
$ echo hello > README
$ git add .
$ git commit -m "initialize"
[master (root-commit) 4942175] initialize
 1 file changed, 1 insertion(+)
 create mode 100644 README
$ git push origin master
Counting objects: 3, done.
Writing objects: 100% (3/3), 219 bytes, done.
Total 3 (delta 0), reused 0 (delta 0)
To c:/Documents and Settings/lhou/./git-share.git
 * [new branch]      master -> master
$ cd ../git-user2/
$ git pull
remote: Counting objects: 3, done.
remote: Total 3 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (3/3), done.
From c:/Documents and Settings/lhou/./git-share
 * [new branch]      master     -> origin/master

先由git-user2进行改名

$ pwd
/c/Documents and Settings/lhou/git-user2
$ git mv README user2
$ git commit -m "README->user2"
[master 7bd2b3c] README->user2
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename README => user2 (100%)
$ git push
warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

Counting objects: 3, done.
Writing objects: 100% (2/2), 240 bytes, done.
Total 2 (delta 0), reused 0 (delta 0)
To c:/Documents and Settings/lhou/./git-share.git
   4942175..7bd2b3c  master -> master

改名成功,下面git-user1开始改名

$ cd ../git-user1/
$ git ls-tree HEAD
100644 blob ce013625030ba8dba906f756967f9e9ca394464a    README
$ git mv README user1
$ git commit -m "README->user1"
[master b35daac] README->user1
 1 file changed, 0 insertions(+), 0 deletions(-)
 rename README => user1 (100%)
$ git push
warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

To c:/Documents and Settings/lhou/./git-share.git
 ! [rejected]        master -> master (non-fast-forward)
error: failed to push some refs to 'c:/Documents and Settings/lhou/./git-share.g
it'
hint: Updates were rejected because the tip of your current branch is behind
hint: its remote counterpart. Merge the remote changes (e.g. 'git pull')
hint: before pushing again.
hint: See the 'Note about fast-forwards' in 'git push --help' for details.

改名失败了! 提示说,这里的git push是非快进式推送, 应该先执行git pull。好,那就先执行git pull。

$ git pull
remote: Counting objects: 3, done.
remote: Total 2 (delta 0), reused 0 (delta 0)
Unpacking objects: 100% (2/2), done.
From c:/Documents and Settings/lhou/./git-share
   4942175..7bd2b3c  master     -> origin/master
CONFLICT (rename/rename): Rename "README"->"user1" in branch "HEAD" rename "README"->"user2" in "7bd2b3c3ca452085aefe8702e4bfb398711aba79"
Automatic merge failed; fix conflicts and then commit the result.

可见,git pull已完成git fetch的工作,但是执行git merge工作时失败了!直到这时就可以开始使用git mergetool交互式的解决树冲突了!

$ git mergetool
Merging:
README
user1
user2

mv: cannot stat `README': No such file or directory
cp: cannot stat `./README.BACKUP.8036': No such file or directory
Deleted merge conflict for 'README':
  {local}: deleted
  {remote}: deleted
Use (m)odified or (d)eleted file, or (a)bort? d

这里,选择d回车,表示删除文件README。

Deleted merge conflict for 'user1':
  {local}: created file
  {remote}: deleted
Use (c)reated or (d)eleted file, or (a)bort? c

这里,选择c回车,表示创建文件user1,让我们的这次改名成功,覆盖之前的git-user2改名。

Deleted merge conflict for 'user2':
  {local}: deleted
  {remote}: created file
Use (c)reated or (d)eleted file, or (a)bort? d

这里,选择d回车,表示删除文件user2。

整个交互过程可见

$ git mergetool
Merging:
README
user1
user2

mv: cannot stat `README': No such file or directory
cp: cannot stat `./README.BACKUP.8036': No such file or directory
Deleted merge conflict for 'README':
  {local}: deleted
  {remote}: deleted
Use (m)odified or (d)eleted file, or (a)bort? d

Deleted merge conflict for 'user1':
  {local}: created file
  {remote}: deleted
Use (c)reated or (d)eleted file, or (a)bort? c

Deleted merge conflict for 'user2':
  {local}: deleted
  {remote}: created file
Use (c)reated or (d)eleted file, or (a)bort? d

查看状态,发现除了尚未清理的临时文件,已没冲突了!

$ git status -s
?? user1.orig

下面提交,并推送到共享的裸版本库。

$ git commit -m "fix tree conflict"
[master c5b59d4] fix tree conflict
$ git push
warning: push.default is unset; its implicit value is changing in
Git 2.0 from 'matching' to 'simple'. To squelch this message
and maintain the current behavior after the default changes, use:

  git config --global push.default matching

To squelch this message and adopt the new behavior now, use:

  git config --global push.default simple

See 'git help config' and search for 'push.default' for further information.
(the 'simple' mode was introduced in Git 1.7.11. Use the similar mode
'current' instead of 'simple' if you sometimes use older versions of Git)

Counting objects: 5, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (2/2), done.
Writing objects: 100% (3/3), 362 bytes, done.
Total 3 (delta 1), reused 0 (delta 0)
To c:/Documents and Settings/lhou/./git-share.git
   7bd2b3c..c5b59d4  master -> master

Last Updated 2015-11-15 Sun 15:49.

Created by Howard Hou with Emacs 24.5.1 (Org mode 8.2.10)