Git 分支与合并

作者: 蓝桥云课 | 来源:发表于2016-10-19 17:08 被阅读773次

    前言:以下内容全部截选自实验楼课程【Git 实战教程】,更多Git 使用介绍,可以点击这里,进行查看~


    一、分支与合并

    Git的分支可以让你在主线(master分支)之外进行代码提交,同时又不会影响代码库主线。

    分支的作用体现在多人协作开发中,比如一个团队开发软件,你负责独立的一个功能需要一个月的时间来完成,你就可以创建一个分支,只把该功能的代码提交到这个分支,而其他同事仍然可以继续使用主线开发,你每天的提交不会对他们造成任何影响。当你完成功能后,测试通过再把你的功能分支合并到主线。

    1.分支

    一个Git仓库可以维护很多开发分支。现在我们来创建一个新的叫 experimental的分支:

    $ git branch experimental
    

    运行git branch命令可以查看当前的分支列表,已经目前的开发环境处在哪个分支上:

    $ git branch experimental* master
    

    experimental 分支是你刚才创建的,master分支是Git系统默认创建的主分支。星号标识了你当工作在哪个分支下,输入git checkout 分支名可以切换到其他分支:

    $ git checkout experimentalSwitched to branch 'experimental'
    

    切换到experimental分支,切换完成后,先编辑里面的一个文件,再提交(commit)改动,最后切换回 “master”分支:

    # 修改文件file1
    $ echo "update" >> file1
    # 查看当前状态
    $ git status
    # 添加并提交file1的修改
    $ git add file1
    $ git commit -m "update file1"
    # 查看file1的内容
    $ cat file1
    test
    update
    # 切换到master分支
    $ git checkout master
    

    查看下file1中的内容会发现刚才做的修改已经看不到了。因为刚才的修改时在experimental分支下,现在切换回了master分支,目录下的文件都是master分支上的文件了。

    现在可以在master分支下再作一些不同的修改:

    # 修改文件file2
    $ echo "update again" >> file2
    # 查看当前状态
    $ git status
    # 添加并提交file2的修改
    $ git add file2
    $ git commit -m "update file2 on master"
    # 查看file2的内容
    $ cat file2
    test
    update again
    

    这时,两个分支就有了各自不同的修改,分支的内容都已经不同,如何将多个分支进行合并呢?

    2.合并

    可以通过下面的git merge命令来合并experimental到主线分支master:

    # 切换到master分支
    $ git checkout master
    # 将experimental分支合并到master
    $ git merge -m 'merge experimental branch' experimental
    

    -m参数仍然是需要填写合并的注释信息。

    由于两个branch修改了两个不同的文件,所以合并时不会有冲突,执行上面的命令后合并就完成了。

    如果有冲突,比如两个分支都改了一个文件file3,则合并时会失败。首先我们在master分支上修改file3文件并提交:

    # 切换到master分支
    $ git checkout master
    # 修改file3文件
    $ echo "master: update file3" >> file3
    # 提交到master分支
    $ git commit -a -m 'update file3 on master'
    

    然后切换到experimental,修改file3并提交:

    # 切换到experimental分支
    $ git checkout experimental
    # 修改file3文件
    $ echo "experimental: update file3" >> file3
    # 提交到master分支
    $ git commit -a -m 'update file3 on experimental'
    

    切换到master进行合并:

    $ git checkout master
    $ git merge experimental
    Auto-merging file3
    CONFLICT (content): Merge conflict in file3
    Automatic merge failed; fix conflicts and then commit the result.
    

    合并失败后先用git status查看状态,会发现file3显示为both modified,查看file3内容会发现:

    $ cat file3
    test
    <<<<<<< HEAD
    master: update file3
    =======
    experimental: update file3
    >>>>>>> experimental
    

    上面的内容也可以使用git diff查看,先前已经提到git diff不加参数可以显示未提交到缓存区中的修改内容。

    可以看到冲突的内容都被添加到了file3中,我们使用vim编辑这个文件,去掉git自动产生标志冲突的<<<<<<等符号后,根据需要只保留我们需要的内容后保存,然后使用git add file3git commit命令来提交合并后的file3内容,这个过程是手动解决冲突的流程。

    # 编辑冲突文件
    $ vim file3
    # 提交修改后的文件
    $ git add file3
    $ git commit -m 'merge file3'
    

    当我们完成合并后,不再需要experimental时,可以使用下面的命令删除:

    $ git branch -d experimental
    

    git branch -d只能删除那些已经被当前分支的合并的分支. 如果你要强制删除某个分支的话就用git branch –D

    撒销一个合并

    如果你觉得你合并后的状态是一团乱麻,想把当前的修改都放弃,你可以用下面的命令回到合并之前的状态:

    $ git reset --hard HEAD^
    # 查看file3的内容,已经恢复到合并前的master上的文件内容
    $ cat file3
    

    快速向前合并

    还有一种需要特殊对待的情况,在前面没有提到。通常,一个合并会产生一个合并提交(commit), 把两个父分支里的每一行内容都合并进来。

    但是,如果当前的分支和另一个分支没有内容上的差异,就是说当前分支的每一个提交(commit)都已经存在另一个分支里了,git 就会执行一个“快速向前"(fast forward)操作;git 不创建任何新的提交(commit),只是将当前分支指向合并进来的分支。

    二、高级分支与合并

    1.在合并过程中得到解决冲突的协助

    git会把所有可以自动合并的修改加入到索引中去, 所以git diff只会显示有冲突的部分. 它使用了一种不常见的语法:

    $ git diff
    

    回忆一下, 在我们解决冲突之后, 得到的提交会有两个而不是一个父提交: 一个父提交会成为HEAD, 也就是当前分支的tip; 另外一个父提交会成为另一分支的tip, 被暂时存在MERGE_HEAD. 在合并过程中, 索引中保存着每个文件的三个版本. 三个"文件暂存(file stage)"中的每一个都代表了文件的不同版本:

    $ git show :1:file.txt 
    $ git show :2:file.txt
    $ git show :3:file.txt
    

    当你使用git diff去显示冲突时, 它在工作树(work tree), 暂存2(stage 2)和暂存3(stage 3)之间执行三路diff操作, 只显示那些两方都有的块(换句话说, 当一个块的合并结果只从暂存2中得到时, 是不会被显示出来的; 对于暂存3来说也是一样).

    上面的diff结果显示了file.txt在工作树, 暂存2和暂存3中的差异. git不在每行前面加上单个'+'或者'-', 相反地, 它使用两栏去显示差异: 第一栏用于显示第一个父提交与工作目录文件拷贝的差异, 第二栏用于显示第二个父提交与工作文件拷贝的差异. (参见git diff-files中的"COMBINED DIFF FORMAT"取得此格式详细信息.) 在用直观的方法解决冲突之后(但是在更新索引之前), diff输出会变成下面的样子:

    $ git diff
    diff --cc file.txt
    index 802992c,2b60207..0000000
    --- a/file.txt
    +++ b/file.txt
    @@@ -1,1 -1,1 +1,1 @@@
    - Hello world
    -Goodbye
    ++Goodbye world
    

    上面的输出显示了解决冲突后的版本删除了第一个父版本提供的"Hello world"和第二个父版本提供的"Goodbye", 然后加入了两个父版本中都没有的"Goodbye world". 一些特别diff选项允许你对比工作目录和三个暂存中任何一个的差异:

    $ git diff -1 file.txt 
    $ git diff --base file.txt 
    $ git diff -2 file.txt 
    $ git diff --ours file.txt 
    $ git diff -3 file.txt
    $ git diff --theirs file.txt
    

    git log和gitk命令也为合并操作提供了特别的协助:

    $ git log --merge
    $ gitk --merge
    

    这会显示所有那些只在HEAD或者只在MERGE_HEAD中存在的提交, 还有那些更新(touch)了未合并文件的提交. 你也可以使用git mergetool, 它允许你使用外部工具如emacs或kdiff3去合并文件. 每次你解决冲突之后, 应该更新索引:

    $ git add file.txt
    

    完成索引更新之后, git-diff(缺省地)不再显示那个文件的差异, 所以那个文件的不同暂存版本会被"折叠"起来.

    2.多路合并

    你可以一次合并多个头, 只需简单地把它们作为git merge的参数列出. 例如,

    $ git merge scott/master rick/master tom/master
    

    相当于

    $ git merge scott/master
    $ git merge rick/master
    $ git merge tom/master
    

    3.子树

    有时会出现你想在自己项目中引入其他独立开发项目的内容的情况. 在没有路径冲突的前提下, 你只需要简单地从其他项目拉取内容即可.
    如果有冲突的文件, 那么就会出现问题. 可能的例子包括Makefile和其他一些标准文件名. 你可以选择合并这些冲突的文件, 但是更多的情况是你不愿意把它们合并. 一个更好解决方案是把外部项目作为一个子目录进行合并.

    这种情况不被递归合并策略所支持, 所以简单的拉取是无用的. 在这种情况下, 你需要的是子树合并策略.

    这下面例子中, 我们设定你有一个仓库位于/path/to/B (如果你需要的话, 也可以是一个URL). 你想要合并那个仓库的master分支到你当前仓库的dir-B子目录下. 下面就是你所需要的命令序列:

    $ git remote add -f Bproject /path/to/B (1)
    $ git merge -s ours --no-commit Bproject/master (2)
    $ git read-tree --prefix=dir-B/ -u Bproject/master (3)
    $ git commit -m "Merge B project as our subdirectory" (4)
    $ git pull -s subtree Bproject master (5)
    

    子树合并的好处就是它并没有给你仓库的用户增加太多的管理负担. 它兼容于较老(版本号小于1.5.2)的客户端, 克隆完成之后马上可以得到代码.

    然而, 如果你使用子模块(submodule), 你可以选择不传输这些子模块对象. 这可能在子树合并过程中造成问题.

    另外, 若你需要修改内嵌外部项目的内容, 使用子模块方式可以更容易地提交你的修改.


    以上内容截选自实验楼课程【Git 实战教程】,更多Git 使用介绍,可以点击这里,进行查看,课程是为《Git Community Book 中文版》提供的配套实验,该书籍汇聚了Git社区的很多精华, 帮助你尽快的掌握Git,该课程则通过动手实践的方式带你掌握这些精华。

    相关文章

      网友评论

        本文标题:Git 分支与合并

        本文链接:https://www.haomeiwen.com/subject/ujexuttx.html