这是我参与「第三届青训营 -后端场」笔记创作活动的的第11篇笔记
一、 本堂课重点内容
1. Git是什么
2. Git基本使用方式
3. Git研发流程
二、 详细知识点介绍
2.1 Git是什么
2.1.1 版本控制
Git is a free and open source distributed version control system designed to handle everything from small to very large projects with speed and efficiency.
版本控制是什么?
一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统
为什么需要版本控制?
更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查,预防事故发生;
也能够随时切换到不同的版本,回滚误删误改的问题代码;
2.1.1.1本地版本控制:
最初的方式:
通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本
解决方案:
开发了一些本地的版本控制软件,其中最流行的是下CS
基本原理:
本地保存所有变更的补丁集,可以理解成就是所有的Df,通过这些补丁,我们可以计算出每个版本的实际的文件内容
缺点:
RCS这种本地版本控制存在最致命的缺陷就是只能在本地使用,无法进行团队协作,因此使用的场景非常有限,因 此衍生出了集中式版本控制
2.1.1.2 集中版本控制
代表性工具:SVN
基本原理:
1.提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中
2.增量保存每次提交的Df,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突
优点:
1.学习简单,更容易作
2.支持二进制文件,对大文件支持更友好
缺点:
1.本地不存储版本管理的概念,所有提交都只能联上服务器后才可以提交
2.分支上的支持不够好,对于大型项目团队合作比较因难
3.用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失。
2.1.1.3分布式版本控制
代表性工具:Git
基本原理:
1.每个库都存有完整的提交历史,可以直接在本地进行代码提交
2.每次提交记录的都是完整的文件快照,而不是记录增量
3.通过Push等操作来完成和远端代码的同步
优点:
1.分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
2.分支管理功能强大,方便团队合作,多人协同开发
3.校验和机制保证完整性,一般只添如数据,很少执行删除操作,不容易导致代码丢失
缺点:
1.相对SVN更复杂,学习成本更高
2.对于大文件的支持不是持别好(git-lfs工具可以弥补这个功能)
2.1.2 Git发展历史
作者
Linus Torvalds(就是Linux这个项目的作者,同时也是Git的作者)。
开发原因
怀疑Linux团队对BitKeeper(另一种分布式版本控制条统,专有软件)进行了逆向工程,BitKeeper不允许Linux团队继续无法使用。因此决定自己开发一个分布式版本控制条统。
开发时间
大概花了两周时问,就完成了Git的代码第一个版本,后续Linux项目就开始使用Git进行维护。
平台:
Github:全球最大的代码托管平台,大部分的开源项目都放在这个平台上。
Gitlab:全球最大的开源代码托管平台,项目的所有代码都是开源的,便于在自己的服务器上完成Gitlab的搭建。
Gerrit:由Google开发的一个代码托管平台,Android这个开源项目就托管在Gerrit之上。
2.2 Git的基本使用方式
2.2.1 Git基本命令
2.2.2 Git目录介绍
项目初始化
mkdir study
cd study
git init
其他参数
-initial-branch初始化的分支
-bare创建一个仓库(纯Cit目录,没有工作目录)
-template可以通过模版来创建预先构建好的自定义git目录
2.2.2.1 不同级别的Git配置
每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置
2.2.2.2 常见Git配置
用户名配置
git config --global user.name "liaoxingju"
git config --global user.email liaoxingju@bytedance.com
Instead of配置
git config --global url.git@github.com:.insteadOf github.com/
Gt命令别名配置
git config --global alias.cin "commit --amend --no-edit"
2.2.3 Git Romote
查看Romate:git remove -v
添加Romate:git remote add origin ssh git@github.com:git/git.git
git remote add origin http github.com/git/git.git
同一个Origin设置不同的Push和Fetch URL
一个仓库拥有多个remote,从没有写权限的仓库fetch代码,push到自己有权限的仓库
2.2.3.1 HTTP Romate
我们知道了什么是remote配置后,那我们本地是如何与remote进行通信的呢,一般会通过http和ssh两种协议,这两种协议都需要对身份进行认证,类似go这种语言,依赖库很多,所以我们需要不断的输入认证的账号密码,肯定是一件很麻烦的事情,因此我们需要配置一下免密的认证方式
免密配置
内存:git config-global credential,helper'cache-timeout:=3600'
硬盘:gtonfszglobalcredential.helper"store-file/path/Mo/eredential-file"
不指定目录的情况默认是~/.git-credentials
将密钥信息存在指定文件中
具体格式:{user}:${password}@github.com
SSH可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问
目前的Key的类型四种,分别是dsa、rsa、ecdsa、ed25519
默认使用的是r5a,由于一些安全问题,现在已经不推荐使用dsa和rsa了,优先推荐使用ed25519
ssh-keygen-ted25519-c your email@example.com
密钥默认存在~/.ssh/ided25519.pub
2.2.4 Git Add
2.2.5 Git Commit
2.2.6 Objects
commit/tree/blob在git里面都统一称为Object。除此之外还有个tag的object。
Blob
存储文件的内容
Tree
存储文件的目录信息、
Commit
存储提交信息,一个Commit可以对应唯一版本的代码
如何把这三个信息串联在一起呢?
1.通过Commit寻找到Tree信息,每个Commit都会存储对应的TreeID
2.通过Tree存储的信息,获取到对应的目录树信息。
3.从tree中获得blob的lD,通过Blob ID获取对应的文件内容。
结果展示:
2.2.7 Refs
Ref5文件存储的内容:
refs的内容就是对应的Commit ID
因此把ref当做指针,指向对应的Commit来表示当前Ref对应的版本。
不同种类的ref
refs/heads前缀表示的是分支,除此之外还有其他种类的ref比如refs/tags前缀表示的是标签。
Branch:
git checkout-b可以创建一个新分支
分支一般用于开发阶段,是可以不断添加Commit进行迭代的
Tag:
标签一般表示的是一个稳定版本,指向的Commit一般不会变更
通过git tag命令生成tag
2.2.8 Annotation Tag
什么是附注标参?
一种特殊的Tag,可以给Tag提供一些额外的信息。
如何创建附注标签?
通过git tag-a命令来完成附注标签的创建。
查看该tag object的内容。
2.2.9 追溯历史版本
获取当前版本代码
通过Ref指向的Commit可以获取难一的代码版本o
获取历史版本代码
Commit里面会存有parent commit字段,通过commit的串联获取历史版本代码。
1. 修改文件,并提交,创建新的commit.
- 查看最新的commit,新增了parent信息。
两者不同
1.新增tree object3a
2.新增blob object55
3.新增commit object64
test ref指向新的commit
2.2.10修改历史版本
1.commit amend
通过这个命令可以修改最近的一次commit信息,修改之后commit id会麦
2.rebase
通过git rebase-I HEAD~3可以实现对最近三个commit的修改:
1.合并commit
2.修改具体的commit message
3.删除某个commit
3.filter branch
该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作
2.2.11 Objects
新增的Object
修改Commit后我们可以发现git object又出现了变化
新增commit object7f
但是之前的commit object63并没有被删除
悬空的Object
顾名思义就是没有ref指向的object
2.2.12 Git GC
GC
通过git gc命令,可以删除一些不需要的object,
以及会对object进行一些打包压缩来减少仓库的体积。
Reflog
reflog是用于记录操作日志,防上误操作后数据丢失
通过reflog来找到丢失的数据,手动将日志设置为过期。
指定时间
git gc prune=now指定的是修剪多久之前的对象
默认是两周前
2.2.13完整的Git视图
2.2.14 Git Clone &Pull & Fetch
Clone
拉取完整的仓库到本地目录,可以指定分支,深度。
Fetch
将远端某些分支最新代码拉取到本地,不会执行merge操作,
会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动棕作。
Pull
拉取远端某分支,并和本地代码进行合并,作用等同于git fetch+git merge,
也可以通过git pull--rebase完成git fetch+git rebase操作。
可能存在冲突,需要解决冲突。
2.2.15 Git Push
常用命令
一般使用git push origin master命令即可完成
冲突问题
1.知果本地的commit记录和远端的commit历史不一致,则会产生冲突,比如git commit-amend or git rebase都有可能导致这个问题。
2.如果该分支就有己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin master-f
来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。
推送规则限制
可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失
2.3 Git研发流程
2.3.1不同的工作流
2.3.2 集中式工作流
什么是集中式工作流?
只依托于master分支进行研发话动
工作方式
1.获取远端master代码
2.直接在master分支完成修改
3.提交前拉取最新的master代码和本地代码进行合
并(使用rebase),如果有冲突需要解决冲突
4.提交本地代码到master
2.3.2.1集中式工作流-Gerrit
Gerrit是由Google开发的一款代码托管平台,主要的特点就是能够很好的进行代码评审。
在aosp(android open source project)中使用的很广,Gerrit的开发流程就是一种集中式工作流。
基本原理
1.依托于Change ID概念,每个提交生成一个单独的代码译审。
2.提交上去的代码不会存储在真正的refs/heads,/下的分支中,而是存在一个refs/for/的引用下。
3.通过refs/meta/config下的文件存储代码的配置,包括权限,评审等配置,每个Change都必须要完成Review后才能合入。
优点
1.提供强制的代码评审机制,保证代码的质量
2.提供更丰富的权限功能,可以针对分支做细粒度的权限管控
3.保证master的历史整洁性
4.Aosp多仓的场景支持更好
缺点
1.开发人员较多的情况下,更容易出现冲突
2.对于多分支的支持较差,想要区分多个版本的线上代码时,更容易出现问题
3.一般只有管理员才能创建仓库,比较难以在项目之间形成代码复用,比如类似的fork操作就不支持。
2.3.3 分支管理工作流
2.3.3.1 分支管理工作流-GitFlow
Git Flow是比较早期出现的分支管理策略。
包舍五种类型的分支
Master:主干分支
Develop:开发分支
Feature:特性分支
Release:发布分支
Hotfix:热修复分支
优点:
如果能按照定义的标准严格执行,
代码会很清晰,并且很难出现混乱。
缺点:
流程过于复杂,上线的节奏会比较慢。
由于太复杂,研发容易不按照标准执行,
从而导致代码出现混乱。
2.3.3.2 分支管理工作流-Github Flow
Github的工作流,只有一个主干分支,基于Pull Request往主干分支中提交代码。
选择团队合作的方式
1.owner创建好仓库后,其他用户通过Fork的方式来创建自己的仓库,并在fork的仓库上进行开发
2.owner创建好仓库后,统一给团队内成员分配权限,直接在同一个仓库内进行开发
创建一个Pull Request
l.创建一个main主分支
2.创建一个feature分支
3.创建一个feature到main的Pull Request
可以在Pull Request页面执行CI/CA/CR等操作,都检查通过后,执行合入。
可以通过进行一些保护分支设置,来限制合入的策略,以及限制直接的Push操作。
Gitlab推荐的工作流是在GitFlow和Github Flow上做出优化,既保持了平一主分支的简便,又可以适应不同的开发环坑
原则:upstream first上游优先
只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master.
2.3.4 代码合并
Fast-Forward
不会产生一个merge节点,合并后保持一个线性历史,如果target分支有了更新,则需要通过rebase操作更新source branch后才可以合入。
Three-Way Merge
三方合并,会产生一个新的merge节点
2.3.5 如何选择合适的工作流
选择原则
没有最好的,只有最合适的
针对小型团队合作,推荐使用Github工作流即可
1.尽量保证少量多次,最好不要一次性提交上千行代码
2.提交Pull Request后最少需要保证有CR后再合入
3.主干分支尽量保持整洁,使用fast-forward合入方式,合入前进行rebase
大型团队合作,根据自己的需妻指定不同的工作流,不需要局限在某种流程中
常见问题:
1.在Gerrit平台上使用Merge的方式合入代码。
Gerrit是集中式工作流,不推荐使用Merge方式合入代码,应该是在主干分支开发后,直接Push。
2.不了解保护分支,Code Review,Cl等机念,研发流程不规范。
保护分支:防止用户直接向主干分支提交代码,必须通过PR来进行合入。
Code Review,Cl:都是在合入前的检查策略,Code Review是人工进行检查,Cl则是通过一些定制化
的脚本来进行一些校验。
3.代码历史混乱,代码合并方式不清晰。
不理解Fast Forward和Three Way Merge的区别,本地代码更新频繁的使用Three Way的方式,导致
生成过多的Merge节点,使提交历史变得复杂不清晰。
三、 课后个人总结
本堂课从Git的演变历史,多平台发展历程,常用指令,使用方式,注意事项等多方面介绍了git工具,便于实际工程中的开发。
四、 引用参考
青训营官方课件