01 Git是什么?
版本控制
- Git是什么?
Git是一个免费开源的分布式版本控制系统,旨在快速高效地处理从小型到超大型的项目。
- 版本控制是什么?
一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。
- 为什么需要版本控制?
更好的关注变更,了解到每个版本的改动是什么,方便对改动的代码进行检查,预防事故发生也能够随时切换到不同的版本,回滚误删误改的问题代码。
本地版本控制
最初的方式 通过本地复制文件夹,来完成版本控制,一般可以通过不同的文件名来区分版本。
解决方案 开发了一些本地的版本控制软件,其中最流行的是 RCS。
基本原理 本地保存所有变更的补丁集,可以理解成就是所有的 Diff,通过这些补丁,我们可以计算出每个版本的实际的文件内容
缺点 RCS这种本地版本控制存在最致命的缺陷就是只能在本地使用,无法进行团队协作,因此使用的场景非常有限,因此衍生出了集中式版本控制。
集中版本控制
代表性工具: SVN
基本原理:
- 提供一个远端服务来保存文件,所有用户的提交都提交到该服务器中。
- 增量保存每次提交的Diff,如果提交的增量中和远端现存的文件存在冲灾,则需要本地提前解决冲突。
优点:
- 学习商单,更容易操作。
- 支持二进制文件,对大文件支持更友好。
缺点
- 本地不存储版本管理的概念,所有提交都只能联上服务器后才可以提交。
- 分支上的支持不够好,对于大型项目团队合作比较困难。
- 用户本地不保存所有版本的代码,如果服务端故障容易导致历史版本的丢失。
分布版本控制
代表性工具: Git
基本原理
- 每个库都存有完整的提交历史,可以直接在本地进行代码提交。
- 每次提交记录的都是完整的文件快照,而不是记录增量。
- 过 Push 等操作来完成和远端代码的同步。
优点
- 分布式开发,每个库都是完整的提交历史,支持本地提交,强调个体。
- 分支管理功能强大,方便团队合作,多人协同开发。
- 校验和机制保证完整性,一般只添加数据,很少执行删除操作,不容易导致代码丢失。
缺点
- 相对 SVN 更复杂,学习成本更高
- 对于大文件的交持不是特别好 (git-lfs 工具可以弥补这个功能)
Git发展历史
Github 全球最大的代码托管平台,大部分的开源项目都放在这个平台上。
Gitlab 全球最大的开源代码托管平台,项目的所有代码都是开源的,便于在自己的服务器上完成 Gitlab 的搭建。
Gerrit 由 Google 开发的一个代码托管平台,Android 这个开源项目就托管在 Gerrit 之上。
02 Git基本使用方式
Git基本命令
常见问题
- 为什么配置了 Git 配置,但是依然没有办法拉取代码?
- 为什么 Fetch 了远端分支,但是看本地当前的分支历史还是没有变化?
Git目录介绍
项目初始化
- mkdir study
- cd study
- git init
其他参数
- --initial-branch 初始化的分支
- --bare 创立一个裸仓库 (纯 Git 目录,没有工作目录)
- --template 可以通过模板来创建预先构建好的自定义 git 目录
Git Config
常见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/
Git 命令别名配置 git config --global alias.cin "commit --amend --no-edit"
Git Remote
查看 Remote git remote -v
添加Remote
git remote add origin_ssh git@github.com:git/git.git
git remote add origin_http github.com/git/git.git
HTTP Remote
免密配置
内存: git config --global credential.helper 'cache --timeout=3600'
硬盘 git config --global credential.helper "store --file /path/to/credential-file"不指定目录的情况默认是 ~/.git-credentials
将密钥信息存在指定文件中
具体格式: {user}:${password}@github.com
SSH Remote
URL: git@github.com:git/git.git
免密配置
SSH 可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问目前的 Key 的类型四种,分别是 dsa、rsa、 ecdsa、 ed25519
默认使用的是rsa,由于一些安全问题,现在已经不推荐使用 dsa 和 rsa 了,优先推荐使用 ed25519
ssh-keygen -t ed25519 -C "youremail@example.com"密钥默认存在 -/.ssh/id ed25519.pub
Git Add
Git Commit
Objects
commit / tree / blob 在 git 里面都统一称为 Object,除此之外还有个 tag 的 object
Blob 存情文件的内容
Tree 存情文件的目录信息
Commit 存储提交信息,一个 Commit 可以对应唯一版本的代码
如何把这三个信息串联在一起?
Refs
Annotation Tag
追溯历史版本
修改历史版本
-
commit - amend 通过这个命令可以修改最近的一次 commit 信息,修改之后 commit id 会变。
-
rebase 通过 git rebase -i HEAD-3 可以实现对最近三个 commit 的修改:
- 合井 commit。
- 修改具体的 commit message。
- 划除某个 commit。
-
filter - branch 该命令可以指定州除所有提交中的某个文件或者全局修改邮箱地址等操作。
Objects
Git GC
GC 通过 git gc 俞令,可以删除一些不需要的 object,以及会对 object 进行一些打包压缩来减少仓库的体积。
Reflog 是用于记录操作日志,防止误操作后数据丢失,通过reflog 来找到丢失的数据,手动将日志设置为过期。
指定时间 git gc prune=now 指定的是修剪多久之前的对象,默认是两周前。
完整的Git视图
Git Clone & Pull & Fetch
Clone 拉取完整的仓库到本地目录,可以指定分支,深度。 Fetch 将远端某些分支最新代码拉取到本地,不会执行 merge 操作会修改 refs/remote 内的分支信息,如果需要和本地代码合并需要手动操作. Pull 拉取远端某分支,并和本地代码进行合井,操作等同于 git fetch + git merge。 也可以通过 git pull --rebase 完成 git fetch + git rebase 探作。 可能存在冲突,需要解决冲突。
Git Push——Push是将本地代码同步至远端的方式
常用命令 一般使用 git push origin master 命令即可完成
冲突问题
- 如果本地的 commit 记录和远端的 commit 历史不一致,则会产生冲突,比如 git commit --amend or gitrebase 则有可能导致这个问题。
- 如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过 git push origin master -f来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送。
推送规则限制 可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失。
常见问题
1. 为什么配置了 Git 配置,但是依然没有办法拉取代码?
- 免密认证没有配。
- Instead of 配置没有配,配的 SSH 免密配置,但是使用的还是 HTTP 协议访问。
3. 为什么 Fetch 了远端分支,但是看本地当前的分支历史还是没有变化?
- Fetch 会把代码拉取到本地的远端分文,但是并不会合并到当前分文,所以当前分支历史没有变化
03 Git研发流程
不同的工作流
什么是集中式工作流?
只依托于 master 分支进行研发活动。
工作方式
- 获取远端 master 代码。
- 直接在 master 分支完成修改。
- 提交前拉取最新的 master 代码和本地代码进行合井(使用 rebase),如果有冲突需要解决冲突。
- 提交本地代码到 master。
集中式工作流-Gerrit
Gerrit 是由 Google 开发的一款代码托管平台,主要的特点就是能够很好的进行代码评审。 在 aosp (android open source project) 中使用的很广,Gerrit 的开发流程就是一种集中式工作流。
基本原理
- 依托于 Change ID 概念,每个提交生成一个单独的代码评审
- 提交上去的代码不会存储在真正的 refs/heads/ 下的分支中,而是存在一个 refs/for/ 的引用下
- 通过 refs/meta/conig 下的文件存储代码的配置,包括权限,评审等配置,每个 Change 都必须要完成 Review 后才能合入。
优点
- 提供强制的代码评审机制,保证代码的质量。
- 提供更丰富的权限功能,可以针对分支做细粒度的权限管控。
- 保证 master 的历史整洁性。
- Aosp 多仓的场景交持更好。
缺点
- 开发人员较多的情况下,更容易出现冲突。
- 对于多分支的支持较差,想要区分多个版本的线上代码时,更容易出现问题。
- 一般只有管理员才能创建仓库,比较难以在项目之问形成代码复用,比如类似的fork操作就不支持。
分支管理工作流
分支管理工作流-GitFlow——比较早期出现的分支管理策略
包含五种类型的分支
- Master: 主干分交
- Develop: 开发分交
- Feature: 特性分交
- Release: 发布分交
- Hotfix: 热修复分支
优点 如果能按照定义的标准严格执行代码会很清晰,并且很难出现混乱。
缺点 流程过于复杂,上线的节奏会比较慢。由于太复杂,研发容易不按照标准执行从而导致代码出现混乱。
分支管理工作流——GitHub Flow
Github 的工作流,只有一个主干分支,甚于 Pull Request 往主干分支中提交代码。
选择团队合作的方式
- owner 创建好仓库后,其他用户通过 Fork 的方式来创建自己的仓库,并在 fork 的仓库上进行开发。
- owner 创建好仓阵后,统一给团队内成员分配权限,直接在同一个仓库内进行开发。
创建一个 Pull Request
- 创建一个 main 主分支
- 创建一个 feature 分支
- 创建一个 feature 到 main 的 Pull Request
原则:upstream first上游优先
代码合并
如何选择合适的工作流
选择原则 没有最好的,只有最合适的
针对小型团队合作,推荐使用 Github 工作流即可
- 尽量保证少量多次,最好不要一次性提交上千行代码。
- 提交 Pull Request 后最少需要保证有 CR 后再合入。
- 主干分支尽量保持整洁,使用 fast-forward 合入方式,合入前进行 rebase。
大型团队合作,根据自己的需要指定不同的工作流,不需要局限在某种流程中。
常见问题
1.在 Gerrit 平台上使用 Merge 的方式合入代码 Gerrit 是集中式工作流,不推荐使用 Merge 方式合入代码,应该是在主干分支开发后,直接Push。
2.不了解保护分支,Code Review,CI 等概念,研发流程不规范 保护分支: 防止用户直接向主干分支提交代码,必须通过 PR 来进行合入Code Review,Cl: 都是在合入前的检查策略。Code Review 是人工进行检查,CI 则是通过一些定制化的脚本来进行一些校验。
3.代码历史混乱,代码合并方式不清晰 不理解 Fast Forward 和 Three Way Merge 的区别,本地代码更新频繁的使用 Three Way 的方式,导致生成过多的 Merge 节点,使提交历史变得复杂不清晰。
练习
- 熟悉一个开源项目,学习开源代码,整理成学习笔记,提交到 Github 上。
- 尝试向开源项目提一个 Pull Request。