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