从本质彻底精通Git——4个模型1个周期1个史观1个工作流

35             f := (b and c) or ((not b) and d)
36             k := 0x5A827999
37         else if 20 ≤ i ≤ 39
38             f := b xor c xor d
39             k := 0x6ED9EBA1
40         else if 40 ≤ i ≤ 59
41             f := (b and c) or (b and d) or(c and d)
42             k := 0x8F1BBCDC
43         else if 60 ≤ i ≤ 79
44             f := b xor c xor d
45             k := 0xCA62C1D6
46         temp := (a leftrotate 5) + f + e + k + w[i]
47         e := d
48         d := c
49         c := b leftrotate 30
50         b := a
51         a := temp
52 
53     //Add this chunk's hash to result so far:
54     h0 := h0 + a
55     h1 := h1 + b
56     h2 := h2 + c
57     h3 := h3 + d
58     h4 := h4 + e
59 
60 //Produce the final hash value (big-endian):
61 digest = hash = h0 append h1 append h2 append h3 append h4
从本质彻底精通Git——4个模型1个周期1个史观1个工作流

虽然SHA-1作为数字签名的算法不安全,但是作为日常项目代码管理来说却足够能够保证其唯一性。

我们已经了解到SHA-1摘要的长度是20字节,也就是160位。要确保有50%的概率出现一次冲突,需要280个随机散列的对象(计算冲突概率的公式是p=(n(n-1)/2)*(1/2160))。280=1.2*1024,也就是一亿亿亿,这是地球上沙粒总数的1200倍。即使按照目前公开的2005年CRYPTO会议中由王小云提出的更具效率的SHA-1攻击法,也需要263=9.2*1018,也就是九十二亿亿,虽然不及地球上的沙粒数也是一个很大的数字。

超大型项目Linux内核有超过45万次提交,包含360万个对象,也至多需要前11个字符就能够保证SHA-1的唯一性。

Git根据文件内容或目录结构计算出SHA-1散列值,然后通过散列值存储、检索和处理信息。

五、Git模型

1. 区域模型

Git项目中的主要区域:Git目录(仓库)、工作目录和暂存区(索引)

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

Git目录也称为Git仓库或Git数据库,是保存Git项目元数据和对象数据库的地方。是Git最重要的部分,当从其它计算机中克隆项目时需要复制的内容。

工作目录是项目某个版本的单次检出。这些文件从Git仓库中提取出来,放置在磁盘上使用和修改。我们平时码代码时的区域就是在工作目录中,因为这里是唯一提供了对文件进行编辑的地方。

暂存区也称为索引,是一个文件,一般位于Git目录中。保存了下次所要提交内容的相关信息。Git的add命令就是将工作目录中的内容添加到暂存区中。

2. 分支模型

分支模型是Git的精髓,被称为Git的“杀手锏特性”。

分支意味着偏离开发主线并继续你自己的工作而不影响主线开发。在其它很多版本控制工具中,有较昂贵的成本,因为常常需要去对整个源代码目录进行一次复制,特别对于大型项目,这样的复制时间成本是很高的。

Git的分支与众不同的地方在于,极致的轻量,几乎即时就可以完成分支操作,分支间的切换操作也很方便。

Git以快照的方式存储数据。

当发起提交时,Git存储的是提交对象(commit object),其中包含了指向暂存区快照的指针。提交对象也包含作者的姓名和邮箱地址、已输入的提交信息以及指向其父提交的指针。初始提交没有父提交,而一般的提交会有一个父提交;对于两个或更多分支的合并提交,存在多个父提交。

当执行git commit进行提交时,Git会先为每个子目录计算校验和,然后再把这些树对象(tree object)保存到Git仓库中,Git随后会创建提交对象,其中包括元数据以及指向项目根目录的树对象的指针,以便有需要的时候重新创建这次快照。

Git分支只不过是一个指向某次提交的轻量级的可移动指针。Git默认的分支名称是master。当你发起提交时,你的当前分支比如master分支就会移动指向你刚刚的提交。

git init命令默认创建的就是master分支。

Git的分支实际上就是一个简单的文件,其中只包含了该分支所指向提交的长度为40个字符的SHA-1校验和。正因如此,Git分支的创建和删除成本就很低。创建新分支就如同向文件写入40个字符外加一个换行符一件简单方便。

提交时Git保存了父对象的指针,当进行合并操作时Git会自动寻找适当的合并基础,创建新分支并在其上coding,然后把多个分支间的代码进行合并很方便,所以Git鼓励开发人员创建和使用分支。

接下来我们来了解几个分支概念

长期分支VS主题分支

主题分支是指短期的、用于实现某一特定功能及其相关工作的分支。与之相对的就是长期分支,长期分支是在整个项目中会一直保持,用于合并主题分支或版本控制和代码发布的分支。比如在master分支上存放稳定版的代码,develop上进行开发,test分支上进行测试,在iss-email上进行email的主题开发。

远程分支VS跟踪分支

远程分支是指向远程仓库的分支的指针,这些指针存在于本地且无法被移动。基于远程分支创建的本地分支就是其远程分支的跟踪分支(tracking branch),有时也叫做上游分支(upstream branch)。远程分支我们能够理解是在服务器上的分支,那跟踪分支呢?我随便创建的本地分支都是跟踪分支吗?本地非跟踪分支和跟踪分支又有什么区别呢?

当你克隆一个远程仓库时,Git默认情况下会自动创建跟踪着远程origin/master分支的本地master分支。当你试图执行分支切换操作时,如果该分支尚未被创建,并且该分支名称和某个远程分支名称一致,那么Git会帮你创建跟踪分支。当设置成为跟踪分支后,使用Git命令时可以简化操作,比如在master分支上push代码到远程仓库上,可以直接使用git push,如果没设置跟踪分支需要使用git push origin/master。

$git checkout --track origin/serverfix
Branch serverfix set up to track remote branch serverfix from origin.
Switched to new Branch 'serverfix' 
$git checkout -b sf origin/serverfix
Branch sf set up to track remote branch serverfix from origin.
Switch to a new branch 'sf'

当我们了解了Git的分支模型后,分支模型正确的打开方式是什么样的呢?

假设你在master分支上做了一些项目起始的工作,之后为了实现某个需求,创建并切换到主题分支sub-record,并在其上做了一些开发工作。之后,你又尝试另一种能实现需求的方式,创建并切换到新的分支sub-recordv1。接着你又切换回master分支并继续工作了一段时间,最后你创建了新的分支dumb-idea来实现你的一个不确定的想法。

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

最后你觉得sub-recordv1方案效率比较高,而在dumb-idea上的工作同事们都觉得很有意义,那么把主题分支上的提交合并合并入长期分支master,舍弃掉sub-record上的C5和C6提交。

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

根据项目的需要,为实现一个需求或一个子需求甚至一个想法创建一个分支。合并代码的时候只需要合并需要的,那些暂时没能合并入的代码也许以后要么直接或间接就可以用上,如果没能用上也可以借鉴和参考。毕竟创建和使用Git分支的成本很低而且方便有效,这样才是Git分支模型的正确打开方式。

3. 对象模型

对象模型分为:主要对象和标签对象。主要对象又分为blob对象、树对象和提交对象。

blob对象是保存到Git仓库的文件当前版本或者称为元数据。可以理解为文件内容。

树对象解决的是文件名的存储问题。可以认为是目录,对应为Unix目录项。单个树对象包含一个或多个树条目,每个条目包含一个指向blob对象或子树的指针以及相关模式、类型和文件名。

提交对象指定了此刻项目快照的顶层树对象、作者/提交者信息、提交时间戳、一个空行以及提交消息。指向的是树对象。

标签对象与提交对象非常相似,包含了标签的创建者、日期、标签消息和一个指针。通常指向提交对象也可以指向blob对象。是不可变的分支引用,总是指向相同的提交对象或blob对象。

4. “三棵树”模型

将Git类比为三棵树的内容管理器。“树”实际上指的是“文件的集合”,并非特定的数据结构。

三棵树HEAD最近提交的快照,下次提交的父提交

索引预计的下一次提交的快照

工作目录沙盒

HEAD和索引这两棵树把数据以一种高效但不够直观的方式保存在.git目录中。而工作目录则将其提取成实际的文件,以便于编辑。可以把工作目录当作沙盒,在将内容提交到暂存区(索引)并写入历史记录之前,你可以随意修改。

三棵树之间的切换

从本质彻底精通Git——4个模型1个周期1个史观1个工作流
从本质彻底精通Git——4个模型1个周期1个史观1个工作流
$git status

Changes to be committed:
    (use "git restore --staged <file>..."to unstage)

Changes not staged for commit:
    (use "git add <file>..." to update what will be committed)
    (use "git restore <file>..." to discard changes in working directory)
从本质彻底精通Git——4个模型1个周期1个史观1个工作流

其中Changes not staged for commit提示的是索引和工作目录之间的差异。

其中Changes to be committed提示的是HEAD和索引之间的差异。

通过以上我们了解到,Git通过操作三棵树的状态来记录项目的快照。

通过学习了三棵树模型,我们将运用所学来理解Git的重置,包括两个命令:reset和checkout。

reset命令会以特定的次序重写这三棵树,操作方式如下:

(1)移动HEAD分支的指向(指定了--soft选项,则在此停止)。

(2)使索引看起来像HEAD(默认行为,或指定了--mixed选项,则在此停止)。

(3)使工作目录看起来像索引(指定了--hard选项,则在此停止)。

checkout命令操作三棵树有两种方式:

方式一(不使用路径):

(1)与reset --hard不同,checkout不会影响工作目录。他会确保不会破坏已更改的文件。

(2)更新HEAD的方式。reset移动的是HEAD指向的分支,而checkout移动的是HEAD,使其指向其他分支。

假设我们有两个分支:master和develop,分别指向不同的提交。我们当前处在develop分支(因为HEAD也指向该分支)。如果执行git reset master,那么develop会与master一样,指向同一提交。如果执行的是git checkout master,那么发生移动的会是HEAD,而不是develop。HEAD将会指向master。

方式二(使用路径):

加上文件路径,与reset一样,不会移动HEAD。会使用提交中的文件来更新索引,但是也会覆盖工作目录中对应的文件。

reset和checkout命令速查表

操作HEAD索引工作目录工作目录是否安全?

提交级别reset --soft [commit]REF否否是

reset [commit]REF是否是

reset --hard [commit]REF是是否

checkout [commit]HEAD是是是

文件级别reset (commit) [file]否是否是

checkout (commit) [file]否是是否

HEAD一列中的REF表示该命令移动了HEAD指向的引用(分支),HEAD表示移动了HEAD自身。

注意:“工作目录是否安全”一列,如果显示是否,应当慎重,执行前要考虑清楚,否则可能丢失工作成果。

六、文件状态的生命周期

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

七、提交史观

代表观点提交史观代表命令

“史书”、“记录”Git仓库提交历史就是实际发生过的事件的记录merge

“故事”Git提交历史是关于项目如何构建的故事rebase

变基操作是把某条分支线上的工作在另一个分支线上按顺序重现。而合并操作则是找出两个分支的末端,并把它们合并到一起。

最好的操作方式是,在本地尚未推送的更改进行变基操作,从而简化提交历史,但决不能对任何已经推送到服务器的更改进行变基操作。

八、分布式工作流

1. 集中式工作流

一个中枢(或是仓库)接受代码,所有人以此同步各自的工作。

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

2. 集成管理者工作流

(1)项目维护人员推送到公开仓库。

(2)贡献者克隆该仓库,作出自己的修改。

(3)贡献者推送到自己的公开仓库副本。

(4)贡献者向维护人员发送电子邮件,要求合并变更。

(5)维护人员将贡献者的仓库添加为远程仓库并在本地进行合并。

(6)维护人员将合并后的变更推送到主仓库。

从本质彻底精通Git——4个模型1个周期1个史观1个工作流

3. 司令官与副官工作流

(1)普通开发人员使用自己的主题分支,根据参考仓库(reference repository)拉取项目或进行变基。

(2)副官将开发人员的主题分支合并入master分支。

(3)司令官将副官的master分支合并入自己的master分支。

(4)司令官将其master分支推送到参考仓库,同时其他开发人员以此为基础进行变基操作。

从本质彻底精通Git——4个模型1个周期1个史观1个工作流
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 214,658评论 6 496
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,482评论 3 389
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 160,213评论 0 350
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,395评论 1 288
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,487评论 6 386
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,523评论 1 293
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,525评论 3 414
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,300评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,753评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,048评论 2 330
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,223评论 1 343
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,905评论 5 338
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,541评论 3 322
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,168评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,417评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,094评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,088评论 2 352

推荐阅读更多精彩内容