要想不产生冲突的习惯是:修改文件之前先git pull,获取远程最新的代码,同步完了之后再对代码进行修改;亦或者事先和同时操作代码的同事沟通,这样可以大概率的避免冲突的产生。
fatal: refusing to merge unrelated histories
加如下这句解决:
git pull origin master --allow-unrelated-histories
在真实的Git运行环境中,往往涉及多个用户对版本仓库的协作,而每个用户都有一个完整的Git版本仓库副本,所以在把各自的操作结果推送到远程仓库的时候出现冲突的可能性就非常高。
在Git中解决冲突的一个优雅方式是:首先通过命令git fetch获取远程仓库最新的修改,然后执行命令git merge将本地的操作结果(实际上就是一个commit)与远程仓库的修改(远程仓库最新的commit)进行合并,如果在合并的过程没有发生冲突,那么Git会生成一个新的commit,并自动提交。但是,合并并非总是成功的,因为合并的不同提交可能是同时修改了同一个文件相同区域的内容,这样就会导致冲突。冲突发生后合并操作会终止,在本地解决冲突后,除非放弃此次合并,需要更新暂存区,再提交,最终完成合并过程。
合并示意图
git fetch和git merge操作可以使用一个命令git pull独立完成
下面的简图描绘了git pull操作的过程。
在初始状态,本地仓库和远端仓库可能是这样的:
从远端仓库执行git fetch命令后:
执行git merge操作后是这样的:
最后推送到远程仓库是这样的:
模拟冲突操作
为了模拟多用户操作一个共享仓库并产生冲突的情况,需要有一个共享仓库,为了简便,可以直接在本地使用git init命令初始化一个共享仓库。
首先执行如果命令初始化一个共享仓库:
git init --bare /home/rhwayfun/java/notes/repos/share.git
• 1
这样就在响应目录下生成了一个裸仓库(没有工作区的仓库称为裸仓库)。然后在目录/home/rhwayfun/java/notes/to下面创建两个文件夹user1和user2,模拟两个用户。并在各自的目录下克隆上面创建的share仓库,以上操作命令如下:
mkdir user1
cd /home/rhwayfun/java/notes/to/user1
git clone file:///home/rhwayfun/java/notes/repos/share.git project
cd ..
mkdir user2
cd user2/
git clone file:///home/rhwayfun/java/notes/repos/share.git project
以上命令执行完毕后就分别在user1和user2两个用户目录下有了最新的远端仓库。
在user1目录执行如下操作:
cd user1/project
git config user.name user1
git config user.email [email protected]
mkdir team
echo "I'm user1." > team/user1.txt
git add user1.txt
git commit -m "create team/user1.txt"
git push
在user2目录执行如下操作:
cd user2/project
git config user.name user2
git config user.email [email protected]
mkdir team
echo "I'm user2." > team/user2.txt
git add user2.txt
git commit -m "create team/user2.txt"
git push
在user2执行git push操作的时候出现了异常信息:
To file:///home/rhwayfun/java/notes/repos2/share.git
! [rejected] master -> master (fetch first)
可知,user2的push操作被拒绝,而被拒绝的原因在于本地的版本库不是最新的,需要首先获取最新的远程库的提交才能继续后面的执行。因为如果没有基于最新的版本库仓库操作可以降低冲突发生的可能性。在更新本地仓库之前,可以先分析一下,user1在team目录增加了user1.txt,而user2在team目录增加了user2.txt,所以从逻辑上两者的操作是没有冲突的。再根据对合并的说明,user2执行git pull应该会自动合并。
执行git pull命令后,继续执行如下的命令,查看提交日志:
git log --oneline --decorate --graph --all
• 1
日志如下:
* 0432ca1 (HEAD -> master) Merge branch ‘master’ of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 5f642a9 (origin/master) create team/user2.txt
* 14cc834 create team/user1.txt
*
可以看到user1和user2的提交合并到一个新的提交0432ca1。所以与之前的分析是吻合的。
但是,可以发现origin/master(这是远端仓库的引用)的提交滞后与user2的提交,因此只需要执行git push就可以将user2的提交更新到共享仓库。
执行后日志输出如下:
* 0432ca1 (HEAD -> master, origin/master) Merge branch ‘master’ of file:///home/rhwayfun/java/notes/repos2/share
|\
| * 5f642a9 create team/user2.txt
* 14cc834 create team/user1.txt
由于user2又进行了一次更新,所以需要切换到user1目录执行如下操作:
cd user1/project
//获取最新的提交
git pull
现在user1和user2的本地仓库都是最新的了,为了制造冲突,可以在user1目录执行如下操作:
cd user1/project
touch README.txt
echo "Hello, user1." > README.txt
git add README.txt
git commit -m "create README.txt"
git push
切换到user2目录,然后执行如下操作:
cd user2/project
touch README.txt
echo "Hello, user2." > README.txt
git add README.txt
git commit -m "create README.txt"
git push
• 发现在执行git push的时候失败,于是执行git pull获取最新的提交,但是好像仍然失败了,日志如下:
U README.txt
Pull is not possible because you have unmerged files.
Please, fix them up in the work tree, and then use ‘git add/rm ’
as appropriate to mark resolution and make a commit.
git 提示我们README.txt在拉取最新的提交的时候发生了冲突,需要在工作区先解决冲突,然后重新将冲突文件添加到暂存区,再执行提交。
为了查看发生冲突的文件,可以执行如下名命令:
// 查看暂存区中记录的冲突文件
• 1 git ls-files -s
• 2 日志如下:
100644 ea9df2ef42c073de18bde4ebdf50e0ac6b1cdd2d 2 README.txt
100644 633d2ed9d0ae01d0d07136c5b5bd857e4d945c14 3 README.txt
100644 17874eaa4a398cc94ed294c93fdbf50f7f843d88 0 team/user1.txt
100644 2dcb7b6ac06d93ea8e6af21ded690f5e171a407c 0 team/user2.txt
编号为2表示暂存区用于保存冲突文件在当前分支中修改的副本,查看该文件的内容执行如下命令:
git show :2:README.txt
• 1
输出结果为
Hello, user2.
编号为3的为暂存区用于保存当前冲突文件在合并版本中修改的副本
git show :3:README.txt
• 1
输出结果为:
Hello, user1.
最后看看工作区的README.txt文件的内容:
cat README.txt
• 1
输出结果:
"<<<<<<< HEAD"
Hello, user2.
"======="
"Hello, user1."
“>>>>>>> 04eed972e27e23a9874f984f08d6567e565d3436”
其中特殊标识<<<<<<<和=之间的内容是当前分支所更新的内容,特殊标识=和>>>>>>>之间的内容是所合并的版本更改的内容。所以只需要手动解决这个冲突就可以,将README.txt文件修改如下:
Hello, user2 and user1.
在user2目录下执行如下操作就可以解决冲突了。
//加上-u参数表示把工作区被跟踪的文件添加到暂存区
git add -u
git commit -m "Merge README.txt: Hello, user2 and user1."
git push
重新运行命令:
git ls-files -s
输出:
100644 2ba0d56dda48b03ef57e5fbcd793f7de1103aa0e 0 README.txt
100644 17874eaa4a398cc94ed294c93fdbf50f7f843d88 0 team/user1.txt
100644 2dcb7b6ac06d93ea8e6af21ded690f5e171a407c 0 team/user2.txt
可以看到所有的编号都变成了0,这样就说明已经成功解决了冲突。
小结
通过以上实际操作认识了冲产生了原因和具体解决冲突的方法,以及在解决冲突过程中使用到一些有用的命令。需要提的一点是,在工作区修改冲突文件时可以使用图形工作完成,不过楼主觉得Linux下的vim编辑器就挺好用的,所有没有介绍使用图形工作修改冲突文件的内容,但本质都是对冲突文件进行编译从而解决冲突的过程。