Git的正确使用姿势与最佳实践 | 青训营笔记

342 阅读10分钟

这是我参与「第三届青训营 -后端场」笔记创作活动的的第10篇笔记. 课前资料: juejin.cn/post/709818…

Git是什么

版本控制

  1. 是什么? 一种记录一个或若干文件内容变化,以便将来查阅特定版本修订情况的系统。

  2. 为什么需要? 更好的关注变更,了解改动,方便及时检查。能随时切换不同版本,回滚误删误改的问题代码。

本地版本控制

  • 最初的方式:本地复制文件夹。通过不同文件名区分版本
  • 解决方案:如RCS
  • 基本原理:本地保存所有变更的补丁集(Diff,通过补丁计算每个版本的实际的文件内容

集中版本控制(如SVN

  • 基本原理:
    • 提供远端服务保存文件,所有用户提交到远程服务器。
    • 增量保存每次提交的Diff,如果提交的增量与远端现存文件冲突,需要本地解决冲突
  • 优点:支持二进制文件,对大文件友好
  • 缺点:
    • 本地不存储版本管理的概念,所有的提交需要连接远程服务器
    • 分支上支持不友好,对于团队合作较困难
    • 用户本地不保存所有版本的代码,服务端故障会导致历史版本丢失

分布式版本控制(如Git

  • 基本原理
    • 每个库存储完整的提交历史,可以直接在本地进行代码提交
    • 每次提交的是完整的文件快照,而不是记录增量
    • 通过Push等操作完成与远端代码的同步
  • 优点:
    • 分布式开发,每个库都是完整的提交历史。支持本地提交,强调个体
    • 分支管理强大,方便团队合作
    • 校验和机制保证完整性,只添加数据很少进行删除,不容易导致代码丢失
  • 缺点:
    • 对于大文件支持不友好(可以使用git-lfs文件

基本使用

截屏2022-05-24 13.45.07.png

对于git init命令的其他参数

  • --initial-branch 指定其他分支
  • --bare 创建一个裸仓库,不会允许添加文件,一般用该命令行创建服务器仓库
  • --template 可以通过指定模板来创建预先构件好的自定义git目录

Git仓库.png

  • HEAD 存储当前指向的分支
  • objects 存储文件信息,存放所有git对象
  • ref 存储分支信息
  • hooks .sample是一些例子,不会执行

截屏2022-05-24 13.53.19.png 通过在本地(工作区)改动代码,使用git add 将本地改动代码提交到暂存区,通过git commit 将代码提交到git目录。

Git Config

每个级别的配置可能重复,但是低级别的配置会覆盖高级别的配置

  • --global
  • --system
  • --local

常见的git配置

  • 用户名配置
  • Instead of 配置
  • Git命令别名配置

Git Remote

  • git remote -v 查询远程信息
  • git remote add 增加远程
    • origin_ssh
    • origin_http
    • 可以通过配置不同源来配置不同平台 截屏2022-05-24 14.01.42.png

截屏2022-05-24 14.03.00.png 此时remote配置发生变化。
相应的,我们删除配置文件中的远程配置,也会删除对应的远程信息。

  • 如何在同一个Origin设置不同的Push和FetchURL?

截屏2022-05-24 14.07.28.png git remote set-url --add --push origin

Http Remote

使用http协议。推荐使用ssh协议。

SSH Remote

通过公私钥机制,将生成的公钥存放在服务端,从而实现免密访问。
目前的key类型有: dsa rsa ecdsa ed25519

Git Add

新建一个文件,通过git add从工作区提交到暂存区。

截屏2022-05-24 16.36.55.png

blob

第一个数 100644 是对象的文件属性的八进制表示。3b18e是hello world的blob的对象名。readme.md是与该blob关联的名字

散列

为readme.md创建一个对象时,Git不关心readme.md的文件名,只关心文件内容:表示hello world的12个字节和换行符。Git为blob执行一些操作,计算它的SHA1散列值,把散列值的十六进制作为文件名放入对象库中。
散列值是3b18e512dba79e4c8300dd08aeb37f8e728b8dad。160位的SHA1散列值对应20个字节,需要40个字节的16进制表示,因此内容另存为.git/objects/3b/18e512dba79e4c8300dd08aeb37f8e728b8dad.
可以使用散列值把hello world从对象库中提取。

文件和树

helloworld存在对象库,也可以通过文件名找到文件git。
git通过目录树的对象跟踪文件的对象名。git add时,git会给添加的每个文件的内容创建一个对象,但不会马上创建对象。而是更新索引。索引位于.git/index,跟踪文件的路径名和相应的blob。每次执行命令,git会使用新的路径名和blob来更新索引。

Refs

refs的内容就是对应的commitId.ref当做指针,指向对应的commit来表示当前ref对应的版本。

截屏2022-05-24 16.51.58.png refs/heads前缀表示的是分支。refs/tags前缀表示的是标签。

  • Tag表示一个稳定版本,指向的commit不会变更。但是branch是可以不断添加commit进行迭代。
(base)  main $ git tag v0.0.1
(base)  main $ cat .git/refs/tags/v0.0.1
b8220eb7916dcafe5ccd6e41b13c0994d4b4d3ce
  • Annotation Tag: 也可以使用git tag命令创建一个带有提交信息、带附注且未签名的标签
(base)  main $ git tag -a v0.0.2 -m "add feature1"
(base)  main $ cat .git/refs/tags/v0.0.2
225ce37cf5ba07ccf801b113c55125e9e5f16676
(base) main $ git cat-file -p 225ce37cf5ba07ccf801b113c55125e9e5f16676
//真正指向的提交对象
object b8220eb7916dcafe5ccd6e41b13c0994d4b4d3ce
type commit
tag v0.0.2
//作者消息
tagger 李一 <1280144091@qq.com> 1653382520 +0800

add feature1

使用git cat-file -p 查看标签对象。

追溯版本代码

  • 可以通过ref指向的commit获取唯一的版本代码
  • 通过commit存储的parent commit字段,通过commit的串联获取历史版本代码
(base)  ⚙ main $ git cat-file -p cdd647f24a2c810bb913d262cce9ff59e7081bc3
tree 386eb799bbfd0826f510b6587c5afaae2a962279
parent b8220eb7916dcafe5ccd6e41b13c0994d4b4d3ce
author 李一 <1280144091@qq.com> 1653383110 +0800
committer 李一 <1280144091@qq.com> 1653383110 +0800

根据当前的SHA1获取当前版本,根据parent获取之前的版本。
更改代码后新增三个SHA1 tree object、blob object、commit object ### 修改历史版本

commit --amend

修改后commidId会改变
会新增commit object

rebase

git rebase -i HEAD~3

  1. 合并commit
  2. 修改具体的commit message
  3. 修改某个commit

filter --branch

指定删除所有提交中的某个文件或全局修改邮箱地址等

Objects

新增的

悬空的

git fsck --lost-found

git GC

删除不需要的object,以及对object打包压缩来减少仓库体积。

  • Reflog 记录操作日志,防止误操作后数据丢失。需要手动设置过期
  • 指定时间 git gc --prune=<时间>
(base)   main  git reflog expire --expire=now --all
(base)   main  git gc --prune=now
枚举对象中: 7, 完成.
对象计数中: 100% (7/7), 完成.
使用 4 个线程进行压缩
压缩对象中: 100% (3/3), 完成.
写入对象中: 100% (7/7), 完成.
总共 7(差异 0),复用 0(差异 0),包复用 0

完整视图

image.png

研发流程

  1. merge合并代码
  2. 保护分支、Code Review、ci

分支管理工作流

Git Flow

  • Master
  • Develop
  • Featrue
  • Release
  • Hotfix

Github Flow

基于Pull Request向唯一的主干分支提交代码
其他用户通过Fork方式来创建自己的仓库,并在此开发
也可以统一为团队成员分配权限,在仓库内开发。 在已有main分支的基础上增加feature分支:

(base)  feature $ git push origin feature
Enter passphrase for key '/Users/luna/.ssh/id_rsa':
枚举对象中: 5, 完成.
对象计数中: 100% (5/5), 完成.
写入对象中: 100% (3/3), 258 字节 | 258.00 KiB/s, 完成.
总共 3(差异 0),复用 0(差异 0),包复用 0
remote:
remote: Create a pull request for 'feature' on GitHub by visiting:
//复制下链接在浏览器打开 得到github界面
remote:      https://github.com/Zerlina-ysl/demo/pull/new/feature
remote:
To github.com:Zerlina-ysl/demo.git
 * [new branch]      feature -> feature

打开github.com/Zerlina-ysl… pull request,得到: 截屏2022-05-24 17.49.03.png 也可以在平台的分支选项进行操作。
点击merge pull request,confirm后该featrue分支被合并。 切换到main 分支 通过git pull origin main同步数据

(base)  ⚙ main  git pull origin main
Enter passphrase for key '/Users/luna/.ssh/id_rsa':
remote: Enumerating objects: 1, done.
remote: Counting objects: 100% (1/1), done.
remote: Total 1 (delta 0), reused 0 (delta 0), pack-reused 0
展开对象中: 100% (1/1), 618 字节 | 309.00 KiB/s, 完成.
来自 github.com:Zerlina-ysl/demo
* branch            main       -> FETCH_HEAD
  e790d42..9046d59  main       -> origin/main
更新 e790d42..9046d59
Fast-forward
readme.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)

git log后 ,可以理解为多了一次commit操作

Merge: e790d42 096fbc8
Author: Zerlina-ysl <56157846+Zerlina-ysl@users.noreply.github.com>
Date:   Tue May 24 17:51:40 2022 +0800

   Merge pull request #1 from Zerlina-ysl/feature

   change

commit 096fbc8dd0dcd7436fe8abf4e85359bcb03f50ea (origin/feature, feature)
Author: 李一 <1280144091@qq.com>
Date:   Tue May 24 17:44:21 2022 +0800

   change

commit e790d42d944156b8efdd4f509592c20fe6ccc32e
Author: 李一 <1280144091@qq.com>
Date:   Tue May 24 17:43:03 2022 +0800

   readme
(END)

在github中有许多规则,settings->Branches->Add rule

截屏2022-05-24 17.55.19.png

Gitlab Floe

原则:upstream first

代码合并

Fast-Forward

保护产生merge节点,合并后历史线性。如果target结点更新,需要rebase操作更新source branch才可以合入。

(base)  ✘ ⚙  main  git checkout -b test
切换到一个新分支 'test'
(base)  ⚙ test  vim readme.md
(base)  ⚙  test ±  git add .
(base)  ⚙  test ✚  git commit -m "fast-forward"
[test 8f0a59b] fast-forward
 1 file changed, 1 insertion(+)
(base)  ⚙  test  git checkout main
切换到分支 'main'
您的分支与上游分支 'origin/main' 一致。
(base)  ⚙ main  git merge test --ff-only
更新 9046d59..8f0a59b
Fast-forward
 readme.md | 1 +
 1 file changed, 1 insertion(+)

git log后

commit 8f0a59b8c09811e9f20a3f171111a678965225f6 (HEAD -> main, test)
Author: 李一 <1280144091@qq.com>
Date:   Tue May 24 18:02:02 2022 +0800

   fast-forward

Three-Way Merge

三方合并,产生新的merge节点 git merge test --no-ff

(base)  ⚙  test  vim readme.md
(base)  ✘ ⚙  test ±  git add .
(base)  ⚙  test ✚  git commit -m "test agin:
dquote> Ω"
[test 753e679] test agin: Ω
 1 file changed, 1 insertion(+), 1 deletion(-)
(base)  ⚙ luna@localhost  ~/DeskTop/demo   test  git checkout main
切换到分支 'main'
您的分支领先 'origin/main'3 个提交。
  (使用 "git push" 来发布您的本地提交)
(base)  ⚙  main  git merge test --no-ff
提示:等待您的编辑器关闭文件... error: There was a problem with the editor 'vi'.
未提交合并,使用 'git commit' 完成此次合并。
(base)  ✘ ⚙ main ✚ >M<  git commit
[main f53b2f4] Merge branch 'test'

git log

commit f53b2f4d6c81699f6cf3512a833c3ee8d834fe78 (HEAD -> main)
Merge: b9bb9a8 753e679
Author: 李一 <1280144091@qq.com>
Date:   Tue May 24 18:10:21 2022 +0800

    Merge branch 'test'

commit 753e679f787be433d0a6e1c1c6b5d0d8d6e7fb4e (test)
Author: 李一 <1280144091@qq.com>
Date:   Tue May 24 18:09:52 2022 +0800

    test agin:
    Ω

Q&A

cherry-pick

(base)  ✘ ⚙ luna@localhost  ~/DeskTop/demo   main  vim test.txt
(base)  ⚙ luna@localhost  ~/DeskTop/demo   main  git  add .
(base)  ⚙ luna@localhost  ~/DeskTop/demo   main ✚  git commit -m "11"
[main 412a948] 11
1 file changed, 1 insertion(+)
create mode 100644 test.txt
(base)  ⚙ luna@localhost  ~/DeskTop/demo   main  git log

[13]  + 27081 suspended  git log
(base)  ✘ ⚙ luna@localhost  ~/DeskTop/demo   main  git checkout main
已经位于 'main'
您的分支领先 'origin/main'6 个提交。
 (使用 "git push" 来发布您的本地提交)
(base)  ⚙ luna@localhost  ~/DeskTop/demo   main  git checkout test
切换到分支 'test'
(base)  ⚙ luna@localhost  ~/DeskTop/demo   test  git cherry-pick 412a948aac5bc05340c2af230f3c05c8c709d58a
[test dc10714] 11
Date: Tue May 24 18:17:20 2022 +0800
1 file changed, 1 insertion(+)
create mode 100644 test.txt

在当前分支上应用给定提交引入的变更(通过commit的SHA1