Git实用技巧
前言
如果我们希望能够快速了解或体验一下 Git
的操作的话,推荐前往这个网站进行学习,其不需要我们安装工具,而且我们的每一步操作都可以在右侧实时看到状态,对于我们学习和理解 Git
工作方式和原理非常有帮助的。
常见工作流程
Git Flow
- 主干分支
- 稳定分支
- 开发分支
- 补丁分支
- 修改分支
GitHub Flow
- 创建分支
- 添加提交
- 提交 PR 请求
- 讨论和评估代码
- 部署检测
- 合并代码
GitLab Flow
- 带生产分支
- 带环境分支
- 带发布分支
日常使用最佳实践
总结日常工作中应该遵循的 Git
使用方式和方法!
使用命令行代替图形化界面:
- 使用命令行来操作,简洁且效率高
提交应该尽可能的表述提交修改内容:
- 区分
subject
和body
内容,使用空行隔开 subject
一般不超过 50 个字符body
每一行的长度控制在 72 个字符subject
结尾不需要使用句号或者点号结尾body
用来详细解释此次提交具体做了什么
使用 .gitignore
文件来排除无用文件:
- 可使用模板文件,然后根据项目实际进行修改
基于分支或 fork
的开发模式:
- 不要直接在主干分支上面进行开发
- 在新建的分支上进行功能的开发和问题的修复
使用 release
分支和 tag
标记进行版本管理:
- 使用
release
分支发布代码和版本维护(release/1.32
) - 使用
tag
来标记版本(A-大feature
功能;B-小feature
功能;C-只修bug
)
常用命令汇总整理
日常使用只要记住 6 个命令就可以了。
1 | # 工作区 -> 暂存区 |
1 | # 工作区 <- 暂存区 |
1 | # 工作区 <- 本地仓库 |
配置实用参数选项
虽然配置比较简单,但是非常有用!
全局配置
1 | # 用户信息 |
个人配置
1 | # 不加 --global 参数的话,则为个人配置 |
合并和变基的选择
到底什么时候使用 merge
操作,什么时候使用 rebase
操作呢?
使用 merge
操作 Python
中的 Requests
库在使用
支持使用 merge
的开发者,他们认为仓库的提交历史就是记录实际发生过什么,它是针对于历史的一个文档,本身其实是有价值的,我们不应该随意修改。我们改变历史的话,就相当于使用“谎言”来掩盖实际发生过的事情,而这些痕迹是应该被保留的。可能,这样并不是很好。
1 | # 3rd 的两个分支的 commit 修改相同内容 |
使用 rebase
操作 - Python
中的 Django
库在使用
支持使用 rebase
的开发者,他们认为提交历史是项目过程中发生过的事情,需要项目的主干非常的干净。而使用 merge 操作会生成一个 merge
的 commit
对象,让提交历史多了一些非常多余的内容。
当我们后期,使用 log
命令参看提交历史的话,会发现主干的提交历史非常的尴尬。比如,同样的修改内容重复提交了两次,这显然是分支合并导致的问题。
1 | # 3rd 的两个分支的 commit 修改相同内容 |
两者的使用原则
总的原则就是,只对尚未推送或分享给其他人的本地修改执行变基操作清理历史,从不对已经推送到仓库的提交记录执行变基操作,这样,你才可能享受到两种方式带来的便利。
更新仓库提交历史
Git
提供了一些工具,可以帮助我们完善版本库中的提交内容,比如:
合并多个 commit 提交记录
日常开发中,我们为了完成一个功能或者特性,提交很多个 commit
记录。但是在最后,提交 PR 之前,一般情况下,我们是应该整理下这些提交记录的。有些 commit
需要合并起来,或者需要将其删除掉,等等。
1 | # 调整最近五次的提交记录 |
编号 | 选项列表 | 对应含义解释 |
---|---|---|
1 | p /pick |
使用这个 commit 记录 |
2 | r /reword |
使用这个 commit 记录;并且修改提交信息 |
3 | e /edit |
使用这个 commit 记录;rebase 时会暂停允许你修改这个 commit |
4 | s /squash |
使用这个 commit 记录;会将当前 commit 与上一个 commit 合并 |
5 | f /fixup |
与 squash 选项相同;但不会保存当前 commit 的提交信息 |
6 | x /exec |
执行其他 shell 命令 |
7 | d /drop |
移除这个 commit 记录 |
删除意外调试的测试代码
有时候提交之后,我们才发现提交的历史记录中存在这一些问题,而这个时候我们又不想新生成一个 commit
记录,且达到一个修改的目录。即,修改之前的 commit
提交记录。
1 | # 不使用分页器 |
取消多个 commit
中的部分提交
我们开发了一个功能,而在上线的时候,产品经理说这个功能的部分特性已经不需要了,即相关特性的提交记录和内容就可以忽略/删除掉了。
1 | # 回滚操作(可多次执行回滚操作) |
合并某些特定的 commit
提交
我们不希望合并整个分支,而是需要合并该分支的某些提交记录就可以了。
1 | # 摘樱桃 |
使用引用日志记录
如何找回我们丢失的内容和记录?
我们之前说过,使用下面命令回退内容、强制推送代码、删除本地分支,都是非常危险的操作,因为重置之后我们就没有办法在找到之前的修改内容了。
1 | # 回退 |
其实 Git
给我们留了一个后门,就是使用 relflog
命令来找回之前的内容,只不过是相对来说麻烦一些。而原理也很简答,就是在我们使用 Git
命令操作仓库的时候,Git
偷偷地帮助我们把所有的操作记录了下来。
1 | # 查看日志记录 |
批量修改历史提交
批量修改历史提交虽然不常用,但是理解的话可以省下很多时间!
之前我们学习到的命令都是针对于一个或者多个 commit
提交信息进行修改的,如果我们需要全局修改历史提交呢?当然,Git
中也是支持全局修改历史提交的,比如全局修改邮箱地址,或者将一个文件从全局历史中删除或修改。
- 开源项目中使用了公司邮箱进行提交了
- 提交文件中包含隐私性的密码相关信息
- 提交时将大文件提交到了仓库代码中了
这里我们可以使用 filter-brach
的方式进行修改,但是建议在使用之前,新建一个分支,在上面进行测试没有问题之后,再在主干上操作,防止出现问题,背个大锅在身上。
1 | # 创建分支 |
灵活使用钩子函数
主要介绍.git/hooks 目录下面的示例钩子函数!
在 Git
里面有两类,分别对应客户端和服务端钩子函数。客户端的钩子函数,是在执行提交和合并之类的操作时调用的。而服务端钩子函数,就是当服务端收到代码提交之后,可以出发代码检查和持续集成的步骤。作为开发者我们并不会搭建 Git
服务器,所以基本不会涉及。
下面就是 Git
自带的钩子脚本,但是自带的都 .sample 作为后缀,表示并没有启用,表示为一个示例。如果需要启用的话,将 .sample
作为后缀删除掉,即可。而其钩子脚本的对应内容,都是使用 Shell
语法进行编写的。
1 | ➜ ll .git/hooks |
其实,钩子脚本使用任何语言编写都是可以的,只要你让程序返回对应的退出码就可以了。
正常的代码合入流程就是,我们本地修改之后,提一个 PR 请求并通过 Github
的 CI
检查,接下来进行代码评审,最后被合并入主干。但是,好的一个习惯就是,在代码提交之前就应该保证代码不会出现语法错误等基础问题,比如通过 flake8
和 PEP8
标准等。
这个时候我们就可以使用 pre-commit[1]
这个 GitHub
的开源项目了,其本质就是给项目添加钩子函数的一个脚本,可以保证我们在提交代码或者推送代码之前,先检查代码的质量。
而 pre-commit-hooks[2]
这个项目里面包含的就是,现在所支持的钩子脚本,即开箱即用的钩子脚本集合。而其钩子脚本的对应内容,都是使用 Python
语法进行编写的。
1 | # 安装方式 |
快速克隆大型项目
在大项目中工作中,拉取代码非常占时间!
我们如果想为 Linux
或 Python
这样的大型项目贡献提交的时候,首先遇到的问题就是,如果快速的 clone
该项目到本地。因为改项目提交历史超多且仓库巨大,加了国内网络的问题,可能等项目完全拉下来的时候,我们的热情都消减下去了。
好在 Git
也帮我们想到了这样的问题,我们可以使用 --depth
参数值拉取远程仓库上面最新一次的提交历史,并不包含项目历史记录,即 .git/objects/
目录下的对象只是本地的,并不包含之前的多次修改产生的对象。
1 | # 克隆不包含之前历史 |
但是,有时间我们可能会需要 clone
仓库中的某个 tag
版本对应下的内容。如果我们直接使用 clone
命令是无法做到的,需要执行如下操作,即可完美解决。
1 | # 克隆特定版本代码 |
上面的效果已经基本可以满足我们日常使用需求了,但是不幸的是,你现在接受了一个机器学习的项目,里面包含了大量的 lfs
文件,现在 clone
又会变得非常慢。可以使用如下操作来避免,Git
工具主动拉去 lfs
文件,来达到目录。
1 | # 克隆不包含 LFS 数据 |
如何处理工作中断
如果在多路运转的时候,还能够高效的进行开发!
比如,我们现在正在一个分支为项目添加一个小的功能,此时,产品经理找到你说是线上环境现在有一个 bug
需要让你来修复下。但是,此时我们添加的小功能并没有完成。
如果此时,我们直接切换到主干分支的话,会将之前分支没有来得及提交的内容全部都带到了主干分支上来,这是我们不想看到的情况。此时,我们需要保存上个分支的工作状态,在我们修改完成线上 bug
之后,再继续工作。
好在 Git
也帮我们想到了这样的问题,我们可以使用 stash
子命令帮助我们将当前工作区、暂存区当中的修改都保存到堆栈之中。等到需要处理的时候,再弹出堆栈中的内容,我们再次进行开发
1 | ➜ git stash -h |
1 | # 存储当前的修改但不用提交 commit |
其实比较保险的做法就是,将当前的所有修改进行 push
并保存到远程仓库里面。这样的好处在于,可以远端备份我们的修改,不会害怕本地文件丢失等问题。等到我们需要继续开发的时候,拉下对应内容,再想办法进行补救,比如使用 --amend
或者 reset
命令。
1 | # 将工作区和暂存区覆盖最近一次提交 |