这是我参与「第三届青训营 -后端场」笔记创作活动的的第3篇笔记
前言引入
为什么要学习Git
- 在协同工作中要用到Git
- 参与开源社区的贡献要用到Git(Git占大多数)
青训营为什么要设计Git这门课
常见问题
- 入职后只会按照文档去配置Git,然后在拉取代码的过程中出现问题,却不知道该如何去解决。缺少自己排查配置的能力
- 不懂Git的使用规范,在团队合作中会各种各样的问题,会给代码后期维护带来各种问题
课程目标
- 学习Git的基本命令,了解基本原理,在以后遇到类似Git使用问题,能自行去排查
- 了解研发流程中的基本概念和规范,学会 正确 使用Git
Git 是什么
为什么会出现Git
Git是什么?
分布式 版本控制 系统
分布式:与中心化相对,分布式就是全世界都有备份,没有所谓的中心
版本控制:可以帮助记录我们每一次的版本变化
问什么需要版本控制?
- 为了方便对代码进行检查
- 预防事故发生,方便回滚到误删误改之前的代码状态
Git的基本原理
- 每一个库都有完整的提交历史,可以直接在本地进行代码提交
- 每次提交都是完整的文件快照而不是记录增量(与SVN相比)
- 通过Push等操作来完成和远端代码的同步
Git的优却点
优点:
- 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
- 分支管理功能强大,方便团队合作,多人协同开发
- 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失
缺点:
- 相对SVN更复杂,学习成本更高
- 对大文件的支持不是很好(完整文件快照,而不是文件增量)(这个缺点可以通过 git-Ifs工具来解决)
Git 命令的基本使用方式及原理
Git的基本命令介绍
常见问题
-
配置Git配置后,仍然没办法拉取代码
- 没有配置ssh密码
-
Fetch了远端分支,但是查看本地的分支历史还是没有变化
- 不会默认指向marge操作
Git的目录介绍 / 如何使用这些命令 / 命令的原理
通过命令行去初始化一个代码仓库
mkdir demo
cd demo
git init
git init 命令默认的分支是master主分支
可以带参数
git init --initial-branch #用于指定初始化的分支 (本机电脑)
git init --bare #创建一个裸仓库(纯Git目录,没有工作目录)(服务器)
git init --template #通过模板来创建预先构建好的自定义git目录
初始化git仓库之后,执行 tree.git命令
会打印出git目录树(tree是打印指定文件夹(也可以是磁盘)的目录树)
常见的Git配置
-
用户名配置
-
instead of配置
替换push的协议(http or ssh)
-
Git别名配置
git config --global alias.cin "commit --amend --no-edit" -
Git Remote
#查看命令 git remote -v #添加命令 git remote add origin_ssh git@github.com:git/git.git git remote add origin_http https://github.com/git/git.git可以通过配置 remote 信息,将clone 和push设置成两个仓库(场景:从github的仓库clone了代码,然后想push到自己的代码仓库,并且不想设置成两个源)
push 命令中的参数 origin 就代表源
Git命令
-
git add .
git add 后会在object目录下添加一条数据(也就是Blob 用于存储文件的信息)
-
git status
查看文件的状态
-
git commit
git commit 后会在object目录下添加两条数据
一个是用来存储文件目录信息的(Tree)
另一个是用来存储文件提交信息的(Commit)
如何把这三个信息串联到一起
通过Commit寻找Tree信息,每个Commit 都会存储对应的 Tree ID
再通过Tree中存储的Blob ID信息 获取对应的内容
-
git checkout -b test
创建名称为 test 的分支
Refs文件存储的内容是当前提交版本的 Commit ID
因此可以把ref当成指针,指向对应的 Commit
tag 文件内存储的是发行版本的 Commit ID
和 ref不同的是,tag标签存储的是固定版本的 Commit ID 而不是当前选中的
-
获取当前版本的代码
通过ref指针指向 commit 可获取唯一的代码版本信息
-
获取历史版本的代码
-
修改历史版本
-
通过 git commit --amend
修改后commit id 会改变
-
通过 rebase
-
通过filter --branch
-
-
git fsck --lost-found
查找本地是否有悬空的commit
-
git gc
git 的垃圾回收,可以删除一些不需要的object,以及会对 object 进行体积压缩
-
通过下面两个命令来实现GC的目的
git reflog expire --expire=now --all git gc --prune=now在进行gc之前需要将日志过期时间改成现在,然后再gc
-
git clone
克隆远端完整的代码仓库
-
git fetch
将远端某些分支最新的代码拉取到本地,但不会默认指向merge操作,需要手动和本地仓库进行合并
-
git pull
通过 git pull --rebase
拉取远端某些分支的最新的代码,并于本地代码进行合并
-
git push
-
Git 常用的研发流程,我们应该选择怎样的研发流程
常见问题
-
在Gerrit 平台上使用Merge的方式去合入代码
Gerrit使集中式工作流,应该是主干分支开发完后直接push
-
不了解保护分支,Code Review,CI的概念,研发流程不规范
- 保护分支,防止用户直接向主干分支提交代码,必须通过PR的方式来合入(pull request)
- code review 是人工检查
- CI是通过一些定制化的脚本来检查
-
代码历史混乱,代码合并方式不清晰
不清楚fast-forward 和 thre-way merge
集中式工作流
只依托于master分支进行研发活动
工作方式:
- 先获取远端master分支
- 直接在master分支进行修改
- 提交前,拉取最新的master分支与本地代码进行合并,如果有冲突需要本地解决
- 提交本地代码到master分支
Gerrit
特点:提供了强制的代码评审机制,保证了代码质量
分支管理工作流
Git Flow
分支类型丰富,规范严格
包含五种类型的分支:
- master:主干分支
- develop:开发分支
- feature:特性分支
- release:发布分支
- hotfix:热修复分支
Github Flow
只有主干分支和开发分支,规则简单
选择团队合作的方式
- owner创建好仓库之后,其他用户通过Fork的方式来创建自己的仓库,并在fork的仓库上进行开发
- owner创建仓库,统一给团队内的成员分配权限,直接在一个仓库内开发
#先克隆远端的代码
git clone github@github.com
#向远端推送readme文件
git add .
git commit -m"add readme"
git push origin master
#创建新分支,并自动切换到创建的分支
git checkout -b feature
#修改readme文件,并推送到远端
git add .
git commit -m"updat readme"
git push origin feature
#创建一个Pull request
#确认无误,merge该分支
#切回主分支
git checkout main
在Setting中配置规则
- 保护主分支,不允许直接向主分支进行push操作
- 需要第三人来确认merge操作
- ……
代码合并方式
- Fast-Forward
#在新分支更新文件
git checkout -b test
#修改文件
#切回主分支
git checkout main
#快速合并分支
git merge test --ff-only
- Three-Way Merge(三方合并会产生一个新节点)
#在新分支更新文件
git checkout -b test
#修改文件
#切回主分支
git checkout main
#快速合并分支
git merge test --no-ff
Gitlab Flow
在主干分支和开发分支之上构建环境分支,版本分支,满足不同分支 or 环境的需要
如何选择合适的工作流
没有最好只有更好
针对小型团队合作,推荐Github工作流即可
- 尽量保证少量多次
- 提交pull request 后最少需要保证有CR后再合入
- 主干分支尽量保持整洁,使用fast-merge进行合并, 合并前进行rebase