黑帽联盟

 找回密码
 会员注册
查看: 1579|回复: 0
打印 上一主题 下一主题

[基础服务] Git之旅(12):解决冲突

[复制链接]

895

主题

38

听众

3323

积分

管理员

Rank: 9Rank: 9Rank: 9

  • TA的每日心情
    无聊
    5 天前
  • 签到天数: 1644 天

    [LV.Master]伴坛终老

    前文中介绍了合并分支的方法以及常见场景,但是并没有模拟合并分支时遇到冲突的场景,这篇文章就站在前文的基础上,来总结一下怎样解决冲突。

    如果两个分支中的同一个文件中的同一行的内容不一样,当我们合并这两个分支时,就会出现冲突,因为git无法判断我们想要以哪个内容为准,所以需要我们人为介入去确认,人为介入确认内容的过程,就是解决冲突的过程。

    为了方便演示,我们先来创建一个测试仓库,并且创建两个分支出来,然后分别修改这两个分支中的同一个文件中的同一行,以便之后合并时能够出现冲突,过程如下:
    $ git init testgit
    Initialized empty Git repository in D:/workspace/git/testgit/.git/

    /d/workspace/git
    $ cd testgit/

    /d/workspace/git/testgit (master)
    $ echo 1 > testfile

    /d/workspace/git/testgit (master)
    $ git add testfile

    /d/workspace/git/testgit (master)
    $ git commit -m "add test file"
    [master (root-commit) d808b6e] add test file
    1 file changed, 1 insertion(+)
    create mode 100644 testfile

    /d/workspace/git/testgit (master)
    $ echo 2 >> testfile

    /d/workspace/git/testgit (master)
    $ git add testfile

    /d/workspace/git/testgit (master)
    $ git commit -m "add 2"
    [master 6b5149f] add 2
    1 file changed, 1 insertion(+)

    /d/workspace/git/testgit (master)
    $ cat testfile
    1
    2

    /d/workspace/git/testgit (master)
    $ git checkout -b new
    Switched to a new branch 'new'

    /d/workspace/git/testgit (new)
    $ cat testfile
    1
    2
    通过上述步骤,我们创建了一个测试文件testfile,在testfile里面写了两行内容,并且创建了两个初始提交,然后,我们基于master分支创建了new分支,现在,new分支和master分支是完全相同的,我准备在两个分支中分别修改testfile的第二行,让new分支和master分支中的testfile的第二行的内容变的不同。
    先来操作new分支,操作如下:
    /d/workspace/git/testgit (new)
    $ cat testfile
    1
    2

    /d/workspace/git/testgit (new)
    $ vim testfile

    /d/workspace/git/testgit (new)
    $ cat testfile
    1
    2new

    /d/workspace/git/testgit (new)
    $ git add testfile

    /d/workspace/git/testgit (new)
    $ git commit -m "2 new"
    [new 7d188a4] 2 new
    1 file changed, 1 insertion(+), 1 deletion(-)
    如上所示,我将new分支中的testfile文件中的第二行从"2"改成了"2new",并且基于这个修改,在new分支中创建了新提交。
    new分支操作完了,现在来操作master分支。
    /d/workspace/git/testgit (new)
    $ git checkout master
    Switched to branch 'master'

    /d/workspace/git/testgit (master)
    $ cat testfile
    1
    2

    /d/workspace/git/testgit (master)
    $ vim testfile

    /d/workspace/git/testgit (master)
    $ cat testfile
    1
    2master

    /d/workspace/git/testgit (master)
    $ git add testfile

    /d/workspace/git/testgit (master)
    $ git commit -m "2 master"
    [master 3c5ea03] 2 master
    1 file changed, 1 insertion(+), 1 deletion(-)
    如上所示,我将master分支中的testfile文件中的第二行从"2"改成了"2master",并且基于这个修改,在master分支中创建了新提交。
    现在,在master分支和new分中,都有了属于了自己的提交,如下图所示,而且,这些提交都是针对testfile文件的第二行所做的修改。
    1.png
    假设,此时我们想要合并两个分支,就会出现所谓的冲突,我们来试试。
    当前我们处于master分支中,现在尝试直接把new分支合并到当前分支,命令如下:
    git merge new
    当我们执行上述命令后,会看到如下返回信息
    Auto-merging testfile
    CONFLICT (content): Merge conflict in testfile
    Automatic merge failed; fix conflicts and then commit the result.

    /d/workspace/git/testgit (master|MERGING)
    $
    那么上述返回信息是什么意思呢?大概意思就是,合并时出现冲突啦,冲突在testfile中,自动合并失败啦,快来人解决冲突啊。
    而且,我们能够发现,在git bash中,如果合并时遇到冲突,在git bash所显示的路径中就会添加上如下字样
    "(分支名|MERGING)"
    就像上例中的 "/d/workspace/git/testgit (master|MERGING)" 一样, "(分支名|MERGING)"表示当前分支还处于"合并中"的状态,也就是说,合并操作并没有完成,还在进行中。
    此时,如果我们执行git status命令,能够看到如下信息
    /d/workspace/git/testgit (master|MERGING)
    $ git status
    On branch master
    You have unmerged paths.
    (fix conflicts and run "git commit")
    (use "git merge --abort" to abort the merge)

    Unmerged paths:
    (use "git add <file>..." to mark resolution)

    both modified:   testfile

    no changes added to commit (use "git add" and/or "git commit -a")
    从上述信息可以看出,我们当前处于master分支,存在没有合并完成的路径(存在没有合并完成的文件)。
    而且git提示我们,现在我们有两种选择,这两种选择分别如下:
    (fix conflicts and run "git commit")
    (use "git merge --abort" to abort the merge)
    第一种选择是:修复冲突,然后将确定后的内容创建提交。
    第二种选择是:使用"git merge --abort"命令放弃合并。
    看来git还是很人性化的,在合并时如果遇到冲突,我们可以选择放弃合并,就像从来都没有发生过任何事情一样,也可以选择解决冲突,完成合并,后文会演示这两种操作,此处不用纠结。
    从返回信息的"Unmerged paths"中,我们能够找到所有未完成合并的文件(存在冲突的文件),而上例中,合并时出现冲突的文件就是testfile文件,那么,我们来看看,在产生冲突以后,testfile文件变成了什么样子。
    /d/workspace/git/testgit (master|MERGING)
    $ cat testfile
    1
    <<<<<<< HEAD
    2master
    =======
    2new
    >>>>>>> new
    如上所示,当冲突发生以后,git会自动将冲突的部分标注出来。
    git会使用如下结构,将冲突的内容标注起来
    <<<<<<< HEAD
    =======
    >>>>>>> BranchName
    git会将当前分支中的内容放在 "<<<<<<< HEAD" 与 "=======" 之间。
    git会将new分支中的内容放在 "======="  与 "<<<<<<< new" 之间。
    也就是说,git并不能确定,是使用"2master"作为最终的内容,还是使用"2new"作为最终的内容,所以,需要我们人为的进行裁决,决定最终的内容。

    到目前为止,我们已经在合并时遇到了冲突,并且查看了冲突所在的testfile文件,我们可以选择放弃合并,也可以选择解决冲突,我们先来演示一下,怎样放弃合并,正如提示信息中所示,我们只要执行"git merge --abort"命令即可放弃合并,我们来试试,操作如下:
    /d/workspace/git/testgit (master|MERGING)
    $ git merge --abort

    /d/workspace/git/testgit (master)
    $ git status
    On branch master
    nothing to commit, working tree clean

    /d/workspace/git/testgit (master)
    $ cat testfile
    1
    2master
    如上例所示,当我们执行git merge --abort命令以后,git就取消了本次合并操作,此时,再次执行"git status"命令,可以看到master分支中没有任何需要修改和提交的内容,查看testfile的内容,会发现testfile中的内容与执行合并操作之前相同,就好像没有执行过任何合并操作一样。这就是在冲突时放弃合并的方法,很容易吧。

    我们已经掌握了怎样在合并产生冲突时放弃合并,现在我们来看看怎样在发生冲突的情况下完成合并。
    再次执行如下合并命令,以便合并时产生冲突。
    /d/workspace/git/testgit (master)
    $ git merge new
    Auto-merging testfile
    CONFLICT (content): Merge conflict in testfile
    Automatic merge failed; fix conflicts and then commit the result.
    如你所见,自动合并失败了,因为有冲突在testfile文件中,git提示我们,可以修复冲突,然后将修复后的结果提交。
    查看testfile文件的内容,发现冲突的部分已经被git标注好了,如下:
    /d/workspace/git/testgit (master|MERGING)
    $ cat testfile
    1
    <<<<<<< HEAD
    2master
    =======
    2new
    >>>>>>> new
    我们可以按照自己的需求,修改冲突的部分,此处假设,我想要使用"2 master 2 new"作为最终的内容,我只需要通过编辑器,将冲突标注的部分改为"2 master 2 new"即可,操作如下(使用vim编辑器编辑文本):
    /d/workspace/git/testgit (master|MERGING)
    $ vim testfile

    /d/workspace/git/testgit (master|MERGING)
    $ cat testfile
    1
    2master 2new
    编辑后的testfile内容如上所示
    好了,冲突的部分已经被人为干预解决了,不过,这并不代表整个合并操作完成了,在解决所有冲突文件以后,我们还需要将最终的状态创建为提交,才算完成了整个合并操作,整个流程操作如下:
    /d/workspace/git/testgit (master|MERGING)
    $ git status
    On branch master
    You have unmerged paths.
    (fix conflicts and run "git commit")
    (use "git merge --abort" to abort the merge)

    Unmerged paths:
    (use "git add <file>..." to mark resolution)

    both modified:   testfile

    no changes added to commit (use "git add" and/or "git commit -a")

    /d/workspace/git/testgit (master|MERGING)
    $ git add testfile

    /d/workspace/git/testgit (master|MERGING)
    $ git status
    On branch master
    All conflicts fixed but you are still merging.
    (use "git commit" to conclude merge)

    Changes to be committed:

    modified:   testfile

    /d/workspace/git/testgit (master|MERGING)
    $ git commit -m "merge new branch into master branch"
    [master 67f725c] merge new branch into master branch

    /d/workspace/git/testgit (master)
    $
    如上述操作所示,我们先执行了git status命令,如果你在git bash中也执行了git status命令,会看到返回信息中testfile是红色的,也就是说,testfile目前的状态还未添加到暂存区,于是,为了方便提交,我们执行了git add命令,将testfile添加到了暂存区,再次执行git status命令,可以看到testfile已经变为绿色,最后,我们使用git commit命令创建了提交,将解决冲突后的合并状态永久的保存了在了新提交中。
    此时,使用gitk --all命令查看图形化界面,如下:
    2.png
    可以看到,合并完成了,在合并过程中虽然遇到了冲突,但是我们人为介入,解决了冲突,并且在最后创建了提交,将合并后的状态保存在了新的合并提交中。
    细心如你肯定已经发现了,上图所表达的状态其实与前文中正常合并后的状态一样(前一篇文章中我们演示了在没有冲突的情况下顺利完成合并的操作,其合并后的状态与上图一样),只不过,当遇到冲突时,合并提交不会自动创建,而是会给我们解决冲突的机会,当我们将所有冲突解决以后,再手动的创建合并提交,也就是刚才演示的解决冲突、创建提交的过程。总结成一句话就是,在合并时,如果没有冲突,就自动创建合并提交,如果存在冲突,需解决冲突后手动创建提交。

    其实,在没有冲突能够正常合并的情况下,我们也可以明确指定不自动创建提交,而是手动的创建提交,我们只需要借助"--no-commit"参数即可,示例如下
    git merge --no-commit new
    上述命令表示,将new分支合并到当前分支,在没有冲突的情况下,也不自动创建提交,而是给我们一个修改的机会,我们可以将内容进行进一步修改后,以最后敲定的结果创建提交。快来自己创建一个测试场景,试试上面的"--no-commit"参数吧,此处就不进行演示了。

    当分支合并完成后,我们就可以将不需要的分支删除了,比如上例中的new分支,我们已经将new分支的内容合并到了master分支中,所以,如果不再需要在new分支上进行操作,即可删除new分支,示例命令如下:
    /d/workspace/git/testgit (master)
    $ git branch -d new
    Deleted branch new (was 7d188a4).

    /d/workspace/git/testgit (master)
    $ git branch -a
    * master
    如上述操作所示,我们想要删除new分支,所以执行了"git branch -d new"命令,"-d"参数为删除之意,需要注意,你如果想要删除new分支,就不能处于new分支中,必须先切换到其他分支中,而上例中,我们删除new分支时,处于master分支中,所以可以正常删除new分支,删除分支后,使用"git branch -a"命令查看所有分支,已经看不到new分支了,可以确认,new分支已经被删除了。
    此时,如果使用gitk --all命令查看图形化界面,会看到如下界面:
    3.png
    从上图中可以看出,new分支的分支标签已经被删除了,目前只有master分支。

    在有些情况下,我们使用"-d"参数,是无法删除对应分支的,比如,当git检测到,你要删除的分支还没有合并到其他分支中,git会出现类似如下提示:
    error: The branch 'new' is not fully merged.
    If you are sure you want to delete it, run 'git branch -D new'.
    在new分支没有完全合并到其他分支中时,如果执行"git branch -d new"命令,就会出现上述提示,这是git为了保险起见而进行的提示,如果你无论如何就是想要删除new分支,无论它是否被完全合并都想要删除它,可以使用"-D"选项(大写D),即可强制删除对应的分支,示例如下:
    git branch -D new
    好啦,到目前为止,我们已经了解了怎样合并分支、解决冲突、删除分支,希望这篇文章能够帮助到你,加油~

    帖子永久地址: 

    黑帽联盟 - 论坛版权1、本主题所有言论和图片纯属会员个人意见,与本论坛立场无关
    2、本站所有主题由该帖子作者发表,该帖子作者与黑帽联盟享有帖子相关版权
    3、其他单位或个人使用、转载或引用本文时必须同时征得该帖子作者和黑帽联盟的同意
    4、帖子作者须承担一切因本文发表而直接或间接导致的民事或刑事法律责任
    5、本帖部分内容转载自其它媒体,但并不代表本站赞同其观点和对其真实性负责
    6、如本帖侵犯到任何版权问题,请立即告知本站,本站将及时予与删除并致以最深的歉意
    7、黑帽联盟管理员和版主有权不事先通知发贴者而删除本文

    勿忘初心,方得始终!
    您需要登录后才可以回帖 登录 | 会员注册

    发布主题 !fastreply! 收藏帖子 返回列表 搜索
    回顶部