Git学习6 Git冲突模拟与解决

时间:2024-03-13 08:12:06

分享一下我老师大神的人工智能教程!零基础,通俗易懂!http://blog.csdn.net/jiangjunshow

也欢迎大家转载本篇文章。分享知识,造福人民,实现我们中华民族伟大复兴!

                     

在真实的Git运行环境中,往往涉及多个用户对版本仓库的协作,而每个用户都有一个完整的Git版本仓库副本,所以在把各自的操作结果推送到远程仓库的时候出现冲突的可能性就非常高。

在Git中解决冲突的一个优雅方式是:首先通过命令git fetch获取远程仓库最新的修改,然后执行命令git merge将本地的操作结果(实际上就是一个commit)与远程仓库的修改(远程仓库最新的commit)进行合并,如果在合并的过程没有发生冲突,那么Git会生成一个新的commit,并自动提交。但是,合并并非总是成功的,因为合并的不同提交可能是同时修改了同一个文件相同区域的内容,这样就会导致冲突。冲突发生后合并操作会终止,在本地解决冲突后,除非放弃此次合并,需要更新暂存区,再提交,最终完成合并过程。

合并示意图

 

git fetchgit merge操作可以使用一个命令git pull独立完成

下面的简图描绘了git pull操作的过程。

在初始状态,本地仓库和远端仓库可能是这样的:

Git学习6 Git冲突模拟与解决

从远端仓库执行git fetch命令后:

Git学习6 Git冲突模拟与解决

执行git merge操作后是这样的:

Git学习6 Git冲突模拟与解决

最后推送到远程仓库是这样的:

Git学习6 Git冲突模拟与解决


模拟冲突操作

为了模拟多用户操作一个共享仓库并产生冲突的情况,需要有一个共享仓库,为了简便,可以直接在本地使用git init命令初始化一个共享仓库。

首先执行如果命令初始化一个共享仓库:

git init --bare /home/rhwayfun/java/notes/repos/share.git
  • 1

这样就在响应目录下生成了一个裸仓库(没有工作区的仓库称为裸仓库)。然后在目录/home/rhwayfun/java/notes/to下面创建两个文件夹user1user2,模拟两个用户。并在各自的目录下克隆上面创建的share仓库,以上操作命令如下:

mkdir user1cd /home/rhwayfun/java/notes/to/user1git clone file:///home/rhwayfun/java/notes/repos/share.git projectcd ..mkdir user2cd user2/git clone file:///home/rhwayfun/java/notes/repos/share.git project
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

以上命令执行完毕后就分别在user1和user2两个用户目录下有了最新的远端仓库。

在user1目录执行如下操作:

cd user1/projectgit config user.name user1git config user.email user1@163.commkdir teamecho "I'm user1." > team/user1.txtgit add user1.txtgit commit -m "create team/user1.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在user2目录执行如下操作:

cd user2/projectgit config user.name user2git config user.email user2@163.commkdir teamecho "I'm user2." > team/user2.txtgit add user2.txtgit commit -m "create team/user2.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在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
  • 1
  • 2
  • 3

冲突解决

现在user1和user2的本地仓库都是最新的了,为了制造冲突,可以在user1目录执行如下操作:

cd user1/projecttouch README.txtecho "Hello, user1." > README.txtgit add README.txtgit commit -m "create README.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

切换到user2目录,然后执行如下操作:

cd user2/projecttouch README.txtecho "Hello, user2." > README.txtgit add README.txtgit commit -m "create README.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

发现在执行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在拉取最新的提交的时候发生了冲突,需要在工作区先解决冲突,然后重新将冲突文件添加到暂存区,再执行提交。

为了查看发生冲突的文件,可以执行如下名命令:

// 查看暂存区中记录的冲突文件git ls-files -s
  • 1
  • 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

输出结果:

<<<<<<< HEADHello, user2.=======Hello, user1.>>>>>>> 04eed972e27e23a9874f984f08d6567e565d3436
  • 1
  • 2
  • 3
  • 4
  • 5

其中特殊标识<<<<<<<和=======之间的内容是当前分支所更新的内容,特殊标识=======和>>>>>>>之间的内容是所合并的版本更改的内容。所以只需要手动解决这个冲突就可以,将README.txt文件修改如下:

Hello, user2 and user1.
  • 1

在user2目录下执行如下操作就可以解决冲突了。

//加上-u参数表示把工作区被跟踪的文件添加到暂存区git add -ugit commit -m "Merge README.txt: Hello, user2 and user1."git push
  • 1
  • 2
  • 3
  • 4

重新运行命令:

git ls-files -s
  • 1

输出:

 

100644 2ba0d56dda48b03ef57e5fbcd793f7de1103aa0e 0 README.txt
  100644 17874eaa4a398cc94ed294c93fdbf50f7f843d88 0   team/user1.txt
  100644 2dcb7b6ac06d93ea8e6af21ded690f5e171a407c 0   team/user2.txt

可以看到所有的编号都变成了0,这样就说明已经成功解决了冲突。

小结

通过以上实际操作认识了冲产生了原因和具体解决冲突的方法,以及在解决冲突过程中使用到一些有用的命令。需要提的一点是,在工作区修改冲突文件时可以使用图形工作完成,不过楼主觉得Linux下的vim编辑器就挺好用的,所有没有介绍使用图形工作修改冲突文件的内容,但本质都是对冲突文件进行编译从而解决冲突的过程。

           

给我老师的人工智能教程打call!http://blog.csdn.net/jiangjunshow

Git学习6 Git冲突模拟与解决
                     

在真实的Git运行环境中,往往涉及多个用户对版本仓库的协作,而每个用户都有一个完整的Git版本仓库副本,所以在把各自的操作结果推送到远程仓库的时候出现冲突的可能性就非常高。

在Git中解决冲突的一个优雅方式是:首先通过命令git fetch获取远程仓库最新的修改,然后执行命令git merge将本地的操作结果(实际上就是一个commit)与远程仓库的修改(远程仓库最新的commit)进行合并,如果在合并的过程没有发生冲突,那么Git会生成一个新的commit,并自动提交。但是,合并并非总是成功的,因为合并的不同提交可能是同时修改了同一个文件相同区域的内容,这样就会导致冲突。冲突发生后合并操作会终止,在本地解决冲突后,除非放弃此次合并,需要更新暂存区,再提交,最终完成合并过程。

合并示意图

 

git fetchgit merge操作可以使用一个命令git pull独立完成

下面的简图描绘了git pull操作的过程。

在初始状态,本地仓库和远端仓库可能是这样的:

Git学习6 Git冲突模拟与解决

从远端仓库执行git fetch命令后:

Git学习6 Git冲突模拟与解决

执行git merge操作后是这样的:

Git学习6 Git冲突模拟与解决

最后推送到远程仓库是这样的:

Git学习6 Git冲突模拟与解决


模拟冲突操作

为了模拟多用户操作一个共享仓库并产生冲突的情况,需要有一个共享仓库,为了简便,可以直接在本地使用git init命令初始化一个共享仓库。

首先执行如果命令初始化一个共享仓库:

git init --bare /home/rhwayfun/java/notes/repos/share.git
  • 1

这样就在响应目录下生成了一个裸仓库(没有工作区的仓库称为裸仓库)。然后在目录/home/rhwayfun/java/notes/to下面创建两个文件夹user1user2,模拟两个用户。并在各自的目录下克隆上面创建的share仓库,以上操作命令如下:

mkdir user1cd /home/rhwayfun/java/notes/to/user1git clone file:///home/rhwayfun/java/notes/repos/share.git projectcd ..mkdir user2cd user2/git clone file:///home/rhwayfun/java/notes/repos/share.git project
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

以上命令执行完毕后就分别在user1和user2两个用户目录下有了最新的远端仓库。

在user1目录执行如下操作:

cd user1/projectgit config user.name user1git config user.email user1@163.commkdir teamecho "I'm user1." > team/user1.txtgit add user1.txtgit commit -m "create team/user1.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在user2目录执行如下操作:

cd user2/projectgit config user.name user2git config user.email user2@163.commkdir teamecho "I'm user2." > team/user2.txtgit add user2.txtgit commit -m "create team/user2.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

在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
  • 1
  • 2
  • 3

冲突解决

现在user1和user2的本地仓库都是最新的了,为了制造冲突,可以在user1目录执行如下操作:

cd user1/projecttouch README.txtecho "Hello, user1." > README.txtgit add README.txtgit commit -m "create README.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

切换到user2目录,然后执行如下操作:

cd user2/projecttouch README.txtecho "Hello, user2." > README.txtgit add README.txtgit commit -m "create README.txt"git push
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

发现在执行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在拉取最新的提交的时候发生了冲突,需要在工作区先解决冲突,然后重新将冲突文件添加到暂存区,再执行提交。

为了查看发生冲突的文件,可以执行如下名命令:

// 查看暂存区中记录的冲突文件git ls-files -s
  • 1
  • 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

输出结果:

<<<<<<< HEADHello, user2.=======Hello, user1.>>>>>>> 04eed972e27e23a9874f984f08d6567e565d3436
  • 1
  • 2
  • 3
  • 4
  • 5

其中特殊标识<<<<<<<和=======之间的内容是当前分支所更新的内容,特殊标识=======和>>>>>>>之间的内容是所合并的版本更改的内容。所以只需要手动解决这个冲突就可以,将README.txt文件修改如下:

Hello, user2 and user1.
  • 1

在user2目录下执行如下操作就可以解决冲突了。

//加上-u参数表示把工作区被跟踪的文件添加到暂存区git add -ugit commit -m "Merge README.txt: Hello, user2 and user1."git push
  • 1
  • 2
  • 3
  • 4

重新运行命令:

git ls-files -s
  • 1

输出:

 

100644 2ba0d56dda48b03ef57e5fbcd793f7de1103aa0e 0 README.txt
  100644 17874eaa4a398cc94ed294c93fdbf50f7f843d88 0   team/user1.txt
  100644 2dcb7b6ac06d93ea8e6af21ded690f5e171a407c 0   team/user2.txt

可以看到所有的编号都变成了0,这样就说明已经成功解决了冲突。

小结

通过以上实际操作认识了冲产生了原因和具体解决冲突的方法,以及在解决冲突过程中使用到一些有用的命令。需要提的一点是,在工作区修改冲突文件时可以使用图形工作完成,不过楼主觉得Linux下的vim编辑器就挺好用的,所有没有介绍使用图形工作修改冲突文件的内容,但本质都是对冲突文件进行编译从而解决冲突的过程。