这是我参与「第三届青训营-后端场」笔记创作活动的的第10篇笔记。
Git 是什么
一个免费的、开源的分布式版本控制系统。
版本控制:一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。
版本控制:
| 类型 | 代表性工具 | 特点 |
|---|---|---|
| 本地版本控制 | RCS | 本地保存所有变更的补丁集;只能在本地使用,无法进行团队协作 |
| 集中式版本控制 | SVN | 提供远端服务保存文件,增量保存每次提交的diff;操作简单,对大文件支持更友好;所有提交必须连接服务器,分支支持不够好;本地不保存所有版本代码,服务器故障会导致历史版本丢失 |
| 分布式版本控制 | Git | 可以直接在本地进行代码提交,每个库都有完整提交历史;每次提交记录都是完整的文件快照而不是记录增量;通过push等操作与远端代码同步;分支管理功能强大,方便多人协作;校验和机制保证完整性;对大文件支持不是特别好 |
Git 衍生:
- Github:全球最大的代码托管平台
- Gitlab:全球最大的开源代码托管平台,此项目的所有代码开源,可以在自己服务器上完成搭建
- Gerrit:Android项目的托管平台
Git 命令的基本使用方式及原理
git init--initial-branch初始化分支--bare创建一个裸仓库(没有工作目录)--template通过模板创建预先构建好的自定义git目录
注:--init-branch选项是在git 2.28版本之后增加的,之前的版本无法使用,附上Ubuntu更新git的方法:
sudo add-apt-repository ppa:git-core/ppa\
sudo apt-get update\
sudo apt-get install git
Git 配置
每个级别的配置可能重复,低级别覆盖高级别
--local配置保存在.git/config--global配置保存在~/.gitconfig--system配置保存在$(prefix)/etc/gitconfig
基本配置:
- 用户名配置:
user.name,user.email - Instead of配置:可以用https替换ssh,如
url.git@github.com:.insteadOf https://github.com/ - 别名配置:
alias.xxx "xxxxx"
Remote配置:本质修改config文件,可以直接修改文件实现
- 查看配置:
git remote -v - 添加远程源,如:
git remote add origin_ssh git@github.com:git/git.gitgit remote add origin_http https://github.com/git/git.git
- 同一个origin设置不同的push和fetch URL:
git remote set-url --add --push origin [new_url]
免密配置:
- HTTP Remote(通常不安全):
https://github.com/git/git.git- 内存:
git config --global credential.helper 'cache --timeout=3600' - 硬盘:
git config --global credential.helper "store --file /path/to/credential-file",默认是~/.git-credentials
- 内存:
- SSH Remote:
git@github.com:git/git.git- 目前Key的类型:dsa, rsa, ecdsa, ed25519。默认使用的是rsa,现在不推荐使用dsa和rsa,优先推荐使用 ed25519
ssh-keygen -t ed25519 -C "email@example.com",默认存在~/.ssh/id_ed25519.pub- 将公钥拷贝至github
代码提交
Git Add
git add .- 在
.git/objects/目录下增加了一个新的文件(blob object),使用命令git cat-file -p ddxxxxxx可以查看对应的内容
Git Commit
git commit -m "add file"- 在
.git/objects/目录下增加了两个新的文件(commit object和tree object)
关于Objects
- commit, tree, blob 在 git 中统一称为 object,此外还有一种 tag object
- blob:存储文件的内容
- tree:存储文件的目录信息
- commit:存储提交信息,一个commit对应唯一版本的代码
- 每个 commit 都会存储对应的 tree ID --> tree 存储对应的目录树信息以及 blob 的ID --> 通过 blob ID 获取对应文件信息。
关于Refs
- 存储在
.git/refs目录下 - refs文件的内容是对应的 commit ID,把ref当作指针,指向对应版本
refs/heads表示的是分支git checkout -b创建分支,同时在.git/refs/heads目录下增加新的文件- 分支用于开发阶段,不断随 commit 迭代
refs/tags表示的标签git tag生成标签,- 标签一般表示稳定版本,指向的 commit 一般不会变更
- 附注标签使用
git tag -a命令,这是一种特殊的标签,可以为 tag 提供一些额外的信息,此外,辅助标签会在.git/objects/目录下生成新的 tag object
追溯历史版本
- commit 中会存储 parent commit 字段,通过串联获取历史版本代码
修改历史版本
commit --amend:可以修改最近一次 commit 信息,修改之后 commit ID 会变- 会产生新的 commit,不会产生新的 tree, blob
- 会产生悬空的object,通过
git fsck --lost-found查找
rebasefilter --branch:可以指定删除所有提交中的某个文件或全局修改邮箱地址等
Git GC
- 删除一些不需要的 object,以及对 object 进行一些打包压缩减小仓库体积
- 手动将日志设置为过期
git reflog expire --expire=now --all- reflog 记录操作日志,用来防止误操作数据丢失的
- 如果不手动设置为过期,reflog中还会有旧的commit的引用,因而不能通过gc命令真实的删除
- 指定修建多久之前的对象
git gc --prune=now,默认是两周前
代码同步
Clone
- 拉取完整的仓库到本地目录,可以指定分支、深度
Fetch
- 将远端某些分支最新代码拉取到本地,不会执行merge操作,会修改refs/remote内的分支信息,如果需要和本地代码合并需要手动操作
Pull
- 拉取远端某分支,并和本地代码进行合并,等同于
git fetch+git merge git pull --rebase等同于git fetch+git rebase操作- 可能存在冲突,需要解决冲突
Push
- 一般使用
git push origin main - 如果本地的 commit 记录和远端的 commit 历史不一致,则会产生冲突
- 如果自己一个人使用,或者团队内确认过可以修改历史则可以通过
git push origin main -f强制推送,不推荐主干分支进行该操作
Git 常用的研发流程
集中式工作流
只依托主干分支进行开发,不存在其它分支,代表平台 Gerrit
分支管理工作流
Git Flow
- 分支类型丰富,规范严格
- 比较早期的分支管理策略:master, develop, feature, release, hotfix
Github Flow
- 只有主干分支和开发分支,基于 pull request(PR) 往主干分支中提交代码,规则简单
- 团队合作方式:
- owner创建好仓库后,其他用户通过fork方式创建自己的仓库,并在fork的仓库上进行开发
- owner创建好仓库后,统一给团队内成员分配权限,直接在同一个仓库内进行开发
- PR流程
git checkout -b featuregit add .git commit -m "xxx"git push origin feature获取 pull request 链接- "Create pull request"
- "Merge pull request"
- 利用分支保护设置限制合入的策略或直接的push操作
- 在仓库的 Settings 选项中 Branches 进行 Add rule
- "Require a pull request before merging"
- "Require linear history" 使用 fast-forward
- "Include administrators" 管理员也不例外
- 在仓库的 Settings 选项中 Branches 进行 Add rule
Gitlab Flow
- 在主干分支和开发分支之上构建环境分支、版本分支,满足不同环境或发布的需要
- 原则:上游优先。只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master
代码合并的方式
- fast-forward
git merge test --ff-only- 不会产生merge节点,合并后保持一个线性历史
- 如果target分支有了更新,需要通过rebase操作更新source分支后才可以合入
- three-way merge
git merge test --no-ff- 会产生一个新的merge节点
合适的工作流:小型团队合作推荐使用github工作流即可
- 尽量保证少量多次
- 提交pull request后最少需要保证有CR(code review)后再合入
- 主干分支尽量保持整洁,使用fast-forward合入方式,合入前进行rebase