从本质彻底精通Git——4个模型1个周期1个史观1个工作流
一、什么是Git?
Git是一个开源的分布式版本控制系统,用于敏捷高效地处理任何或小或大的项目。
Git是Linus Torvalds为了帮助管理 Linux 内核开发而开发的一个开放源码的版本控制软件。
二、分布式VS集中式
VisualSVN、TortoiseSVN、Bazzar为集中式版本控制系统,而Mercurial、Git、Bitkeeper为分布式版本控制系统。
1. 集中式版本控制
优点:可以对具体的文件或目录进行权限控制,有全局的版本号。
缺点:所有操作都需要联网中心服务器。
2. 分布式版本控制
优点:
i. 分支管理。
ii. 完整性和安全性更高,各客户端保留有完整的版本库。
iii. 绝大部分操作都是本地化的,支持离线。
缺点:
i. 对版本库的目录和文件无法做到精细化的权限控制。
ii. 无全局性的版本号。
通过以上分析的集中式和分布式版本控制的优缺点,我们就能总结出以Git为代表的分布式版本控制和以SVN为代表的集中式版本控制之间的区别。
3. Git和SVN的区别
- 最核心的区别当然是分布式和集中式。
- 处理数据的方式不同,Git以元数据方式存储数据,SVN按照文件存储,对应的取出数据方式也不一样。
- 分支管理或分支模型(下文会详解)。
- 全局性的版本号。
- 数据的完整性和安全性,分布式的更好。
三、常用的Git解决方案和代码托管平台
1.开源软件解决方案:Gitea、GitLab
2.代码托管平台:码云(Gitee)、码市(Coding)、GitHub、GitLab、Bitbucket
四、Git的基石SHA-1
In cryptography, SHA-1 (Secure Hash Algorithm 1) is a cryptographichash function which takes an input and produces a 160-bit (20-byte) hash value known as a message digest – typically rendered as ahexadecimal number, 40 digits long. It was designed by the UnitedStates National Security Agency, and is a U.S. Federal InformationProcessing Standard.
以上摘自维基百科,主要说明了SHA是一个密码散列函数家族,是FIPS所认证的安全散列算法。能计算出一个数字消息所对应到的,长度固定的字符串(又称消息摘要)的算法。且若输入的消息不同,它们对应到不同字符串的机率很高。SHA-1长度为20字节,160位,熟悉网络编程的都知道,4位可以转换成一个十六进制的数字,所以一个SHA-1散列可以展示为40长度的十六进制的数字。
SHA-1的计算方法伪代码:
1 //Note: All variables are unsigned 32 bits and wrap modulo 232 when calculating
2
3 //Initial variables:
4 h0 := 0x67452301
5 h1 := 0xEFCDAB89
6 h2 := 0x98BADCFE
7 h3 := 0x10325476
8 h4 := 0xC3D2E1F0
9
10 //Pre-processing:
11 append the bit '1' to the message
12 append k bits '0', where k is the minimum number >= 0 such that the resulting message
13 length (in bits) is congruent to 448(mod 512)
14 append length of message (before pre-processing), in bits, as 64-bit big-endian integer
15
16 //Process the message in successive 512-bit chunks:
17 break message into 512-bit chunks
18 for each chunk
19 break chunk into sixteen 32-bit big-endian words w[i], 0 ≤ i ≤ 15
20
21 //Extend the sixteen 32-bit words into eighty 32-bit words:
22 for i from 16 to 79
23 w[i] := (w[i-3] xor w[i-8] xor w[i-14] xor w[i-16]) leftrotate 1
24
25 //Initialize hash value for this chunk:
26 a := h0
27 b := h1
28 c := h2
29 d := h3
30 e := h4
31
32 //Main loop:
33 for i from 0 to 79
34 if 0 ≤ i ≤ 19 then
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
虽然SHA-1作为数字签名的算法不安全,但是作为日常项目代码管理来说却足够能够保证其唯一性。
我们已经了解到SHA-1摘要的长度是20字节,也就是160位。要确保有50%的概率出现一次冲突,需要2^80个随机散列的对象(计算冲突概率的公式是p=(n(n-1)/2)*(1/2^160))。2^80=1.2*10^24,也就是一亿亿亿,这是地球上沙粒总数的1200倍。即使按照目前公开的2005年CRYPTO会议中由王小云提出的更具效率的SHA-1攻击法,也需要2^63=9.2*10^18,也就是九十二亿亿,虽然不及地球上的沙粒数也是一个很大的数字。
超大型项目Linux内核有超过45万次提交,包含360万个对象,也至多需要前11个字符就能够保证SHA-1的唯一性。
Git根据文件内容或目录结构计算出SHA-1散列值,然后通过散列值存储、检索和处理信息。
五、Git模型
1. 区域模型
Git项目中的主要区域:Git目录(仓库)、工作目录和暂存区(索引)
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来实现你的一个不确定的想法。
最后你觉得sub-recordv1方案效率比较高,而在dumb-idea上的工作同事们都觉得很有意义,那么把主题分支上的提交合并合并入长期分支master,舍弃掉sub-record上的C5和C6提交。
根据项目的需要,为实现一个需求或一个子需求甚至一个想法创建一个分支。合并代码的时候只需要合并需要的,那些暂时没能合并入的代码也许以后要么直接或间接就可以用上,如果没能用上也可以借鉴和参考。毕竟创建和使用Git分支的成本很低而且方便有效,这样才是Git分支模型的正确打开方式。
3. 对象模型
对象模型分为:主要对象和标签对象。主要对象又分为blob对象、树对象和提交对象。
blob对象是保存到Git仓库的文件当前版本或者称为元数据。可以理解为文件内容。
树对象解决的是文件名的存储问题。可以认为是目录,对应为Unix目录项。单个树对象包含一个或多个树条目,每个条目包含一个指向blob对象或子树的指针以及相关模式、类型和文件名。
提交对象指定了此刻项目快照的顶层树对象、作者/提交者信息、提交时间戳、一个空行以及提交消息。指向的是树对象。
标签对象与提交对象非常相似,包含了标签的创建者、日期、标签消息和一个指针。通常指向提交对象也可以指向blob对象。是不可变的分支引用,总是指向相同的提交对象或blob对象。
4. “三棵树”模型
将Git类比为三棵树的内容管理器。“树”实际上指的是“文件的集合”,并非特定的数据结构。
三棵树 | HEAD | 最近提交的快照,下次提交的父提交 |
索引 | 预计的下一次提交的快照 | |
工作目录 | 沙盒 |
HEAD和索引着两棵树把数据以一种高效但不够直观的方式保存在.git目录中。而工作目录则将其提取成实际的文件,以便于编辑。可以把工作目录当作沙盒,在将内容提交到暂存区(索引)并写入历史记录之前,你可以随意修改。
三棵树之间的切换
$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)
其中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仓库提交历史就是实际发生过的事件的记录 | merge |
“故事” | Git提交历史是关于项目如何构建的故事 | rebase |
变基操作是把某条分支线上的工作在另一个分支线上按顺序重现。而合并操作则是找出两个分支的末端,并把它们合并到一起。
最好的操作方式是,在本地尚未推送的更改进行变基操作,从而简化提交历史,单决不能对任何已经推送到服务器的更改进行变基操作。
八、分布式工作流
1. 集中式工作流
一个中枢(或是仓库)接受代码,所有人以此同步各自的工作。
2. 集成管理者工作流
(1)项目维护人员推送到公开仓库。
(2)贡献者克隆该仓库,作出自己的修改。
(3)贡献者推送到自己的公开仓库副本。
(4)贡献者向维护人员发送电子邮件,要求合并变更。
(5)维护人员将贡献者的仓库添加为远程仓库并在本地进行合并。
(6)维护人员将合并后的变更推送到主仓库。
3. 司令官与副官工作流
(1)普通开发人员使用自己的主题分支,根据参考仓库(reference repository)拉取项目或进行变基。
(2)副官将开发人员的主题分支合并入master分支。
(3)司令官将副官的master分支合并入自己的master分支。
(4)司令官将其master分支推送到参考仓库,同时其他开发人员以此为基础进行变基操作。
参考资料
[1] Scott Chacon,Ben Straub《精通Git》(第2版)
[2] 维基百科
[3] https://www.sohu.com/a/234659269_575744
[4] https://www.runoob.com/git/git-tutorial.html
从本质彻底精通Git——4个模型1个周期1个史观1个工作流的更多相关文章
- GIT分支管理模型
GIT分支管理模型 link: git-branching-model 主分支(Main branches) 项目两个常驻分支: master 主干分支(锁定),仅用于发布新版本,平时不能在上面干活, ...
- Git Flow 工作模型与使用
一. Git Flow 工作模型的原理 无规矩不成方圆,但是规矩太多了,则感觉到束缚.我们一个人工作的时候喜欢无拘无束,想怎么干就怎么干,没有人评判,没有人检验.时间久了就会盲目自大,以为增删改查熟悉 ...
- Git使用总结 Asp.net生命周期与Http协议 托管代码与非托管代码的区别 通过IEnumerable接口遍历数据 依赖注入与控制反转 C#多线程——优先级 AutoFac容器初步 C#特性详解 C#特性详解 WPF 可触摸移动的ScrollViewer控件 .NET(C#)能开发出什么样的APP?盘点那些通过Smobiler开发的移动应用
一,原理 首先,我们要明白Git是什么,它是一个管理工具或软件,用来管理什么的呢?当然是在软件开发过程中管理软件或者文件的不同版本的工具,一些作家也可以用这个管理自己创作的文本文件,由Linus开发的 ...
- 成功的GIT开发分支模型和策略
详细图文并茂以及git flow工具解释参考: http://danielkummer.github.io/git-flow-cheatsheet/index.zh_CN.html 原文地址:http ...
- CoreData 从入门到精通(六)模型版本和数据迁移
前面几篇文章中讲的所有内容,都是在同一个模型版本上进行操作的.但在真实开发中,基本上不会一直停留在一个版本上,因为需求是不断变化的,说不定什么时候就需要往模型里添加新的字段,添加新的模型,甚至是大规模 ...
- 5分钟精通git教程
git是一个版本控制工具,就要先弄清楚什么是版本 版本: 对外发布的版本如v1.0.0,v1.1.0 叫version 内部代码的版本叫commit,如:修改了一个错别字 顾名思义一个version就 ...
- 30 天精通 Git 版本控管
https://github.com/doggy8088/Learn-Git-in-30-days
- 微信小程序之结构目录、视图层、双线程模型、生命周期、事件传递冒泡、组件、request、登录授权及支付
结构目录与配置介绍 视图层与基础语法 双线程模型 生命周期 事件.传递和冒泡 组件.自定义组件.组件事件传递页面 Request.路由跳转.本地存储 登录(后端实现) | 授权(后端实现) 支付(后端 ...
- asp.net 管道模型+生命处理周期
http://www.cnblogs.com/qianlifeng/archive/2010/12/16/1908568.html https://msdn.microsoft.com/zh-cn/l ...
随机推荐
- 一文搞懂 js 中的各种 for 循环的不同之处
一文搞懂 js 中的各种 for 循环的不同之处 See the Pen for...in vs for...of by xgqfrms (@xgqfrms) on CodePen. for &quo ...
- IndexedDB All In One
IndexedDB All In One https://developer.mozilla.org/en-US/docs/Web/API/IndexedDB_API https://develope ...
- Nestjs 上传文件到七牛云
$ npm install qiniu import * as url from 'url'; import * as qiniu from 'qiniu'; @Post('upload') @Use ...
- Dart 中断Future
更多 中断future 方法1) import 'package:async/async.dart'; void main() { var get = CancelableOperation.from ...
- Flutter ReorderableListView 可拖拽的列表
import 'package:flutter/material.dart'; import 'dart:math' as math; void main() => runApp(MyApp() ...
- CPU飙升的问题
本文转载自CPU飙升的问题 问题发现 事情是这样的,最近小码仔负责的项目预定今天凌晨2点上进行版本更新.前几天测试小姐姐对网站进行压力测试,观察服务的CPU.内存.load.RT.QPS等各种指标. ...
- 使用 Castle 实现 AOP,以及 Autofac 集成 Castle
Castle 是 2003 年诞生于 Apache Avalon 项目,目的是为了创建一个IOC 框架.发展到现在已经有四个组件: ORM组件:ActiveRecord IOC组件:Windsor 动 ...
- Python学习相关链接
感觉挺全的: http://www.cnblogs.com/xinshiye/p/9015187.html 也挺全的:http://www.cnblogs.com/toutou/category/72 ...
- SpringBoot文件上传与POI的使用
1.使用springboot上传文件 本文所要源码在一个项目中,源码:https://github.com/zhongyushi-git/springboot-upload-download.git. ...
- Tango with django 1.9 中文——2.准备工作
在正式开始写代码之前,设置好开发环境是非常重要的.你要确保所有必须的组件都已安装好.本章将概述五个你需要了解的关键组件的设置和使用.清单如下: 使用命令行 Python Python包管理器pip和虚 ...