这是我参与「第五届青训营 」伴学笔记创作活动的第 5 天
名词解释:
Git是什么
版本控制:一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统
版本控制工具举例:
本地版本控制
基本原理:本地保存所有变更的补丁集,可以理解成就是所有的Diff,通过这些补丁,可以计算出每个版本的文件内容
缺点:只能本地使用,不能团队协作
集中式版本控制
基本原理:
- 提供一个远程服务保存文件,所有用户的提交到该服务器中
- 增量保存每次提交的Diff,如果提交的增量中和远端的现存的文件存在冲突,则需要本地提前解决冲突
优点:学习简单,容易操作。二进制文件,对大文件支持更友好
缺点:
- 本地不存储版本控制,所有提交都只能联上服务器后才能提交
- 分支的支持不友好,对于大型项目团队合作比较困难
- 用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失
分布式版本控制
基本原理:
- 每个库都有完整的提交历史,可以直接在本地进行代码提交
- 每次提交记录的都是完整的文件快照,而不是记录增量
- 通过Push等操作完成和远端代码的同步
优点:
- 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体
- 分支管理功能强大,方便团队合作,多人协同开发
- 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失
缺点:命令多,学习成本高。对大文件支持不是特别好(git-lfs工具可以弥补)
Git的基本使用方式
配置
项目初始化
mkdir git-demo
cd git demo
git init
其他参数:
--initial-branch 初始化的分支
--bare 创建一个裸仓库(纯Git目录,没有工作目录)
--template 可以通过模板来创建预先构建好的自定义git目录
.git目录:
工作区:本地
暂存区:git add、commit上去的
Git Config
不同级别的配置:全局(--global) > 系统(system) > 本地(local)
查询全局配置:
git config --global
每个级别的配置可能会重复,但是低级别的配置会覆盖高级别的配置
常见的Git配置
用户名配置:
git config --global user.name "username"
git config --global user.email "email"
Instead of 配置
git config --global url.git@github.com:.insteadOf https://github.com/
Git 命令别名配置
git config --global alias.cin "commit --amend --no-edit"
Git Remote
Remote 是本地和远端的关联信息
查看Remote:
git remote -v
添加Remote
git remote add origin_ssh git@github.com:git/git.git
git remote add origin_http https://github.com/git/git.git
再次查看:
同一个Origin设置不同的Push和Fetch URL,比如从开源仓库拉取代码,再Push到自己的仓库
git remote add origin git@github.com:git/git.git
git remote set-url --add --push origin git@github.com:my_repo/git.git
HTTP Remote
免密配置:
不推荐HTTP方式连接,不安全
SSH Remote
URL:git@github.com:git/git.git
免密配置:
提交代码
Git add
git add .
在objects目录下存放着提交的记录,可以使用命令查看
git cat-file -p 95d09f2b10159347eece71399a7e2e907ea3df4f
Git Commit
git commit -m "readme.md"
查询,objects目录多了两个文件:
Objects
commit / tree / blob 在git里面统一称为Object,除此之外还有个tag类型的object
Blob : 存储文件的内容
Tree:存储文件的目录信息
Commit:存储提交信息,一个Commit可以对应唯一版本的代码
Tag:附注tag,可以给Tag提供一些额外的信息
三个信息的串联方式:
- 通过Commit寻找到Tree信息,每个Commit都会存储对应的Tree ID
- 通过Tree存储的信息,找到对应的文件目录树的信息
- 从Tree中获得blob的ID,通过Blob ID获取对应的文件内容
Refs
Refs文件存储对应的Commit ID,因此把ref当作指针,指向对应的Commit来表示当前Ref对应的版本
Branch:
git checkout -b 创建分支
一般用于开发阶段,是可以不断添加commit进行迭代的
Tag:
一般表示一个稳定版本,指向的Commit一般不会变更
git tag
创建附注标签:
git tag -a v0.0.2 -m "add feature"
查看:
cat .git/refs/tags/v0.0.2
真正指向的commit Object是什么,然后还会带一些附注信息,谁打的标签,内容是什么
历史版本
追溯历史版本:
git log
修改历史版本:
修改一次最近的commit信息,修改之后commit id 会变
但是修改之后会新增Commit Object,之前的Commit Object并没有删除,这个Commit Object没有指针指向它,称为悬空Commit
git commit --amend
通过git rebase -i HEAD~3可以实现对最近三个commit的修改
- 合并commit
- 修改具体的commit message
- 删除某个commit
删除所有提交中的某个文件或者全局修改邮箱地址等操作:
filter --branch
Git GC
通过git gc 命令删除一些不需要的Object
Git视图
远端同步
Clone: 拉取完整的仓库到本地目录,可以指定分支、深度
Fetch: 将远端某些分支最新代码拉渠道本地,不会执行merge操作,会修改refs/remote 内的分支信息,如果需要和本地代码合并需要手动操作
Pull: 拉取远端某分支,并和本地代码进行合并,操作等同于 git fetch + git merge,也可以git pull --rebase完成git fetch + git rebase操作
Git Push
git push origin master
Git研发流程
不同的工作流:
集中式工作流:
- 获取远端master分支
- 直接在master分支完成修改
- 提交前拉取最新的master代码和本地进行代码合并(使用rebase),如果有冲突需要解决冲突
- 提交本地代码到master
分支管理工作流
GitHub工作流: 只有一个主干分支,基于Pull Request往主干分支中提交代码
团队合作方式:
- owner创建好仓库以后,其他用户通过Fork的方式来创建仓库,并在Fork的仓库上进行开发(开源项目)
- owner创建好仓库以后,统一给团队内成员分配权限,直接在一个仓库内进行开发
GitLab工作流:
原则:upstream first上游优先,只有上有分支采纳的代码才可以进入到下游分支,一般上游分支就是master
代码合并:
Fast-Forward:不会产生一个merge节点,合并后会保持一个线性历史,如果target分支有了更新,则需要通过rebase操作更新source branch后才可以进入
git checkout -b dev
git add .
git commit -m "test"
git checkout main
git merge -dev -ff-only
git log 以后查看没有merge节点
Three-Way Merge 三方合并,会产生新的merge节点
get merge dev --no-ff
选择合适的工作流: 小型团队,Github工作流就行
- 少量多次
- 提交Pull Request后最少需要保证有CR后再合入
- 主干分支尽量保持整洁,使用first-forward合入方式,合入前 rebase