这是我参与「第三届青训营 -后端场」笔记创作活动的的第五篇笔记。笔记的内容总结并归纳了廖兴桔老师讲述的git的正确使用姿势和最佳实践。学习如何正确使用git进行代码托管,学习如何正确地使用Git。
为什么要学习Git
协同工作、开源社区
简单来说,Git是程序员的必备技能。
1.Git是什么
1.1 版本控制
Git:分布式版本控制系统
何为版本控制?
一种记录版本变化,以便将来查阅特定版本修订情况的系统
为什么需要版本控制?
方便对改动代码进行检查,也能够随时切换
1.1.1 本地版本控制
最初通过本地复制文件夹,来完成版本控制
解决方案: 开发一些本地的版本控制软件,例如RCS:保存每个版本的Diff
缺陷:只能本地修改,不存在协同性
1.1.2 集中版本控制
代表性工具:SVN
基本原理:
- 提供一个远端服务来保存文件,所有用户的提交保存到该服务器中
- 增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲突,则需要本地提前解决冲突
缺陷:本地不保存版本管理、分支支持不友好
1.1.3 分布式版本控制
代表性工具:Git
基本原理:
- 每个库都存有完整的提交历史,可以直接在本地进行代码提交
- 每次提交记录的都是完整的文件快照,而不是记录增量
- 通过Push等操作来完成和远端代码的同步
缺陷:使用起来较为复杂、不支持大文件
1.2 Git发展历史
作者:Linus Torvalds(Linux项目作者)
Github、Gitlab、Gerrit(Android)
2.Git的基本使用方式
常见问题
1、为什么我明明配置了Git配置,但是仍然没有办法拉取代码?
没有配置免密认证
Instead Of配置没有配,配的SSH免密配置,但是使用的还是HTTP协议访问
2、为什么我Fetch了远程分支,但是我看本地当前的分支历史还是没有变化?
Fetch会把代码拉取到本地的远端分支,但是不会合并到当前分支,所以当前分支历史没有变化
2.1 Git目录介绍
项目初始化:git init
其他参数:
--initial-branch 初始化的分支
--bare 创建一个裸仓库(纯git目录,没有工作目录)
--template 可以通过模板来创建预先构建好的自定义git目录
输入tree .git,得到如下结果:
git仓库管理:
2.1.1 Git Config
不同级别的Git配置:低级别会覆盖高级别,级别高低:system > global > local
2.1.2 常见Git配置
用户名配置:
git config --global user.name "xxx"
git config --global user.email xxx@xxx.com
Instead of配置
git config --globol url.git@github.com:.insteadOf https://github.com/
Git命令别名配置
git config --global alias.cin "commit --amend --no-edit"
2.2 Git Remote
同一个Origin设置不同的Push和Fetch URL
2.2.1 HTTP Remote
免密配置:
2.3 Git Add
git add .
git add命令将文件加入到暂存区
2.4 Git Commit
git commit -m "xxx"
git commit命令用于将更改记录(提交)到存储库
2.5 Objects
commit/tree/blob 在git里面都统一称为Object,除此之外还有个tag的object
Blob:存储文件的内容
Tree:存储文件的目录信息
Commit:存储提交信息,一个Commit可以对应版本的代码
如何把这三个信息串联到一起?
2.6 Refs
Refs的内容就是对应的Commit ID
因此可以把ref当做指针,指向对应的Commit来表示当前Ref对应的版本
不同种类的ref
refs/heads前缀表示的是分支,除此之外还有其他种类的ref,比如refs/tags前缀表示的是标签
Branch git checkout -b 可以创建一个新分支
分支一般用于开发阶段,是可以不断添加Commit进行迭代的
Tag 标签一般表示的是一个稳定版本,指向的Commit一般不会变更
通过git tag命令生成tag
2.7 Annotation Tag
什么是附注标签?
一种特殊的Tag,可以给Tag提供一些额外的信息
如何创建附注标签? 通过git tag -a 命令来完成附注标签的创建
查看该tag object的内容
2.8 追溯历史版本
获取当前版本代码
通过Ref指向的commit可以获取唯一的代码版本
获取历史版本代码
Commit里面会存有parent commit字段,通过commit的串联获取历史版本代码。
1、修改文件,并提交,创建新的commit。
2、查看最新的commit,新增了parent信息。
2.9 修改历史版本
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.10 Objects
新增的Object:但是之前的object并没有被删除
悬空的Object:通过git fsck找到
2.11 Git GC
GC:
- 通过git gc命令,可以删除一些不需要的obeject,以及会对object进行一些打包压缩来减少仓库的体积
Reflog:
- reflog是用于记录操作日志,防止误操作后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期
指定时间:
- git gc prune=now制定的是修建多久之前的对象,默认是2周之前
2.12 完整的Git视图
2.13 Git Clone&Pull&Fetch
Clone 拉取完整的仓库到指定目录,可以制定分支,深度
Fetch:将远端某些分支最新代码拉取到本地,不会执行merge操作,会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作
pull:拉取远端某分支,并和本地代码进行合并,操作等同于git fetch+git merge,
也可以通过git pull --rebase 完成git fetch+git rebase操作
可能会存在冲突,需要解决冲突
2.14 Git Push
常用命令:
一般使用git push origin master命令即可完成
冲突问题:
1、如果本地的commit记录和远端的commit历史不一致,则会产生冲突,比如git commit --amend or git rebase都有可能导致这个问题
2、如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin master -f来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突之后再进行推送
推送规则限制:
可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失
3. Git研发流程
常见问题
1、在Gerrit平台上使用Merge的方式合入代码
Gerrit平台是集中式工作流,不推荐使用Merge方式合入代码,应该是在主干分支开发后,直接Push
2、不了解保护分支,Code Review,CI等概念,研发流程不规范
保护分支:防止用户直接向主干分支提交代码,必须通过PR进行合入
3、代码历史混乱,代码合并方式不清晰
3.1 不同的工作流
3.2 集中式工作流
什么是集中式工作流
只依托于master分支进行研发活动
工作方式:
- 获取远端master代码
- 直接在master分支完成修改
- 提交前拉取最新的master代码和本地代码进行合并(使用rebase),如果有冲突则需要解决冲突
- 提交本地代码到master
3.2.1 集中式工作流-Gerrit
Gerrit,一种免费、开放源代码的代码审查软件,使用网页界面。利用网页浏览器,同一个团队的软件程序员,可以相互审阅彼此修改后的程序代码,决定是否能够提交,退回或者继续修改。它使用Git作为底层版本控制系统。它分支自Rietveld,作者为Google公司的Shawn Pearce,原先是为了管理Android计划而产生。
基本原理:
1、依托于Change ID概念,每个提交生成一个单独的代码评审
2、提交上去的代码不会存储在真正的refs/heads/下的分支中,而是存在一个refs/for/的引用下。
3、通过refs/meta/config下的文件存储代码的配置,包括权限,评审等配置,每个Change都必须要完成Review之后才能合入
优点:保证master分支的整洁性
缺点:开发人员较多的情况下,容易出现冲突;对多分支的支持较差。
3.3 分支管理工作流
3.3.1 分支管理工作流-GitFlow
Git Flow是比较早期出现的分支管理策略
包含五种类型的分支:
- Master:主干分支
- DEevelop:开发分支
- Feature:特性分支
- Release:发布分支
- Hotfix: 热修复分支
优点:如果能够完全按照定义的标准严格执行,代码会很清晰,很难出现混乱
缺点:流程过于复杂,上线的节奏会较慢。且研发不容易按照标准执行,容易出现代码混乱。
3.3.2 分支管理工作流-Github Flow
Github的工作流,只有一个主干分支,基于Pull Request往主干分支中提交代码。
可以通过进行一些保护分支设置,来限制合入的策略,以及限制直接的push操作。
3.3.3 分支管理工作流-Gitlab Flow
原则:upstream first上游优先
只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master
3.4 代码合并
Fast-Forward
不会产生一个merge节点,合并后保持一个线性历史
Three-Way Merge
三方合并,会产生一个新的merge节点
3.5 如何选择合适的工作流
选择原则:没有最好的,只有最合适的
针对小型团队合作,推荐使用Github工作流即可
大型团队合作,根据自己的需要指定不同的工作流,不需要局限在某种流程中