zoukankan      html  css  js  c++  java
  • 从git的问题模型理解git

    对于很多试图说明git工作过程的文章都会出现一个图:

    	  A---B---C topic
    	 /         
        D---E---F---G---H master

    但是并没有告诉你,这些字母代表的commit到底是什么。把topic分支merge到master的时候的conflict为什么不体现出来?这才是初学者迷茫的东西,文章画的图倒是简单明了,然后困惑还是困惑。很多问题其实如果把问题描述清楚了,问题也就解决了。那么git解决的问题是什么,都知道是版本控制,那么具体化之后是什么东西呢?

    1、什么是commit?

    首先git的区分文件变化的最细粒度是line行,不是单词更不是字母。行变化主要是增删改三种。commit可以看成是记录这些增删改操作的数据。
    commit是一个add,del,update的集合,update分解为一个del配合一个add,update n,可以表示为(del n)+(add n.5)或者(del n)+(add (n-1).5) 其中n.5表示在n行后添加一行,所以commit只是一个del和add的集合,只要记录下del的行号,add的gap号的集合,配合待变化的base文件,就可以精确确定修改内容是什么。这些add和del是无序的,只要全部应用到base文件,就会得到相同的结果文件。

    2、合并

    合并两次commit就是,两个ops集合的合并,合并是可以递归的,一系列commit可以合并成一个commit,对应就是一些列ops集合合并为一个大ops集合。

    3、什么是冲突?
    冲突就是指两次commit的执行顺序会影响都最终结果文件内容的不同,这就是通常说的冲突。也就是不能简单机械地合并,否则不符合预期。那么我的预期是什么呢?预期就是颠倒两次commit得到的结果是一样的。这样的合并过程需要人为参与,告诉git合并的时候应该以什么样的结果为准。

    那么什么样commit产生冲突呢?
    两个commit,del同一行,不分操作先后只执行一次,就可以确定不同操作顺序不影响最终结果。
    两个commit,add到同一个gap,那么不同的操作顺序最终结果不同,所以,这两个commit是conflict。
    两个update同一行,翻译为一个del加上一个add,如果add可以是被del行的前或者后的gap,这个由于对同一个gap的add判定conflict。
    甚至update相邻两行。
    实验验证同时修改相邻两行为什么会冲突,思考了一下,原因可能如下。
    如果是update一行,那么相当于在这一行前或者后增加一行,所以,这一行本身和这一行前后的gap都被锁定——可以认为是被污染的范围。
    由于,add的gap仍然是有交叉的,因为一个update等价于del该行然后在after或before的gap增加新行,也判定为conflict。

    所以如果用整数表示行号,用x.5表示x行后的gap,因为修改一行是del line,add before或者add after都行。
    commitA,update line4——修改范围是4,3.5,4.5
    commitB,update line3——修改范围是3,3.5,2.5
    commitC,update line2——修改范围是2,2.5,1.5
    所以A和B是冲突的,A和C不是冲突的。

     4、解决merge conflict

    对于git新手而言冲突很可怕,但是版本控制的日常就是发生冲突。

    当两个ops集合合并的时候,产生一个新的commit,也就是两个commit的和,将所有ops操作应用到文件,git判断有冲突之后标记处冲突部分——哪个commit分别做的修改是什么,此时,文件可以编辑,用户修改后并确认,add,commit为最终的merge结果。

    git的冲突的情况只会被夸大,而不会被忽略,宁枉勿纵,这是安全的策略。git检测文件的改动时,总是尽量精确描述修改的部分——例如在文件最前面增加一行,如果准确描述,就是add line to gap 0.5,如果简单起见也可以认为del all,add new all。没有精确描述缩小改动是问题不大,最多只是影响性能。

    现在再理解ABC代表的commit,就可以想到背后只不过一个增删的操作集合,merge也只是集合的合并。

    5、rebase
    将分支rebase到一个分叉点之后的master提交,实际上导致分支上的commit做了相应的调整,是为了保证从分支merge到master的时候不会出现冲突。否则,需要先从master merge到分支,此时在分支解决冲突,然后将最新分支merge到master,两个分支多了一个历史交汇点。如图,先将master分支merge到topic的M,此时如果有冲突,则在topic分支解决提交到topic,然后从topic merge到master的H。

    	  A---B---C--M  topic
    	 /          / 
        D---E---F-----G-----H master

    而使用rebase则可能在rebase的时候有冲突,此时解决冲突,这时topic分支的commit其实已是A2,B2,C2了,因为他们的base文件变化了,只有操作集合跟随变化,才能保证最终的结果文件不变。

    	  A2---B2---C2 topic
    	 /         
        D---E---F---G---H master

    由于,topic是rebase到G的,所以此时merge到master,一定没有conflict,这样合并的master分支就更好看。

    最后,相信一点,git说没有冲突就真的没有冲突,真的有冲突不会被git所遗漏。


    ==========================================
    但是,其实没有冲突不代表代码不会有bug。举个极端的例子:
    一个outputstream,忘记了写bye这个单词,还忘记了close。如果

    ……
    OutputStream os= ...…
    other code
    ……

    一个人增加

    ……
    OutputStream os= ...…
    other code
    os.write("bye")
    ……

    另一个人增加

    ……
    OutputStream os= ...…
    ……
    os.close()
    other code
    ……

    merge是无冲突的,结果如下

    ……
    OutputStream os= ...…
    ……
    os.close()
    other code
    os.write("bye")
    ……

    显然这段代码运行时就会出错。

  • 相关阅读:
    潜水一年,然后回来
    【搬运】Visual Studio vs2017 vs2019 中文离线安装包下载,替代ISO镜像
    Re0:在 .NetCore中 EF的基本使用
    Re0:在.NetCore 中Dapper的基本用法
    jdadjkgh.txt
    Android Studio打包出来的安装包是非正式发布版本
    Android Studio生成开发调试版(Debug)和正式发布版(Release)的安装包
    【unity-2】coroutine
    【ugui-1】RectTransformUtility
    [ps笔记]快捷键、快捷方式
  • 原文地址:https://www.cnblogs.com/linlei2099/p/10016355.html
Copyright © 2011-2022 走看看