Git 是什么

版本控制

版本控制是什么?

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

本地版本控制

  • 代表:RCS
  • 依托于本地磁盘进行版本控制

集中式版本控制

  • 代表:SVN
  • 存在一个统一的远端服务器,用于版本控制,本地不存储版本控制

分布式版本控制

  • 代表:Git
  • 每个库都拥有所有的版本控制信息,远端服务器用于不同库之间进行版本信息同步

Git 发展历史

最初版由 Liunx 创始人 Linus Torvalds 花两周时间开发而成,主要是为了用于 Linux 项目的维护

Git 基本使用方式

image-20220525154853031

Git 目录介绍

项目初始化

1
2
3
mkdir demo
cd demo
git init

其他参数

  • --initial-branch 初始化的分支
  • --bare 创建裸仓库(纯 Git 目录,没用工作目录)
  • --template 通过模板构建自定义 git 目录

观察 .git 目录

image-20220525155845823

  • HEAD:当前的分支

    image-20220525160214027

  • config:配置

  • objects:文件信息

  • refs:分支信息

hooks 里面的一堆 .sample 文件是例子,不会执行


工作区和暂存区

image-20220525160351818

Git Config

不同级别的 Git 配置:全局、系统、当前

image-20220525160529835

低级别可以覆盖高级别

常见的 Git 配置

  • 用户名配置

    1
    2
    git config --global user.name "xxx"
    git config --global user.email [email protected]
  • Instead of 配置

    相当于替换

    1
    git config --global url [email protected]:.insseadOf https://github.com/
  • Git 命令别名配置

    1
    git config --global alias.cin "commit --amend --no-edit"

Git Remote

  • 查看

    1
    git remote -v
  • 添加

    1
    2
    git remote add origin_ssh [email protected]:git/git.git
    git remote add origin_http https://github.com/git/git.git

image-20220525161645758

这时再看 config,就会发现多了两个配置

image-20220525161800582


可以同一个 Origin 设置不同的 Push 和 Fetch URL

1
2
git remote add origin [email protected]:git/git.git
git remote set-url --add --push origin [email protected]:my_repo/git.git

image-20220525162258054


HTTP Remote

出于安全性,一般不使用这种方式

URL: https://github.com/git/git.git

免密配置:

image-20220525163200720

SSH Remote

URL: [email protected]:git/git.git

免密配置

image-20220525163338738


生成并查看公钥,之后存到 GitHub 上即可

image-20220525163444895

可以创建多对公私钥

Git Add

将文件添加至暂存区

1
2
3
touch readme.md
nano readme.md // 写入 "HelloWorld"
git add .

之后查看 .git 目录,可以看见多两个文件

image-20220525164201687

这个文件的内容实际上就是我们修改的内容

image-20220525164255689

Git Commit

把修改从暂存区提交到储存库中

1
git commit -m "add readme"

image-20220525164501205

检查 .git 目录,发现多了两个文件,分别存储 blob 类型和 tree 类型

image-20220525164541833

image-20220525164813305

Objects

commit、tree 和 blob 在 git 里统一称为 Object,除此之外还有一个 Object,在下文会提到

Blob

存储文件的内容

Tree

存储文件目录信息

Commit

存储提交信息,一个 Commit 可以对应唯一版本的代码


如何把这三个信息串联在一起?

  1. 通过 Commit 寻找 Tree
  2. 通过 Tree 寻找若干 Blob
  3. 通过若干 Blob 获取对应文件内容

image-20220525165743302

Refs

Refs 中存储的内容

refs 的内容就是对应的 Commit ID

image-20220525165957720

因此把 ref 当作指针,指向对应的 Commit 来表示当前对应的版本


Branch 和 Tag

  • Branch

    分支一般用于开发阶段,可以不断添加 Commit 来迭代

  • Tag

    标签一般表示一个稳定版本,指向的 Commit 一般不会变更


新建分支

1
git checkout -b test

创建好后,可以在 refs 中看见

image-20220525170140076

新建标签

1
git tag v0.0.1

image-20220525170835658

Annotation Tag

附注标签:一种特殊的 Tag,可以提供一些额外的信息

1
git tag -a v0.0.2 -m "add feature 1"

创建之后,发现 objects 中也新建了一个文件,而这个附注标签就是指向这个文件的

image-20220525171336802

这个文件就属于上文中的第四种 object —— Tag Object,指向的是一个 commit,内容是一些附加信息

image-20220525171522477

追溯历史版本

  • 获取当前版本代码

    通过 Ref 指向的 Commit 可以获取唯一的代码版本

  • 获取历史版本代码

    现在修改一下 readme.md ,在开头加一个井号,之后提交

    image-20220525210827540

    查看 object 文件夹,又多了三个文件

    image-20220525210911251

    通过 git log ,找到这次提交的编号

    image-20220525211021615

    检查这次提交,发现多了一个 parent 值,这就关联到了历史版本

    image-20220525211207339

修改历史版本

  1. commit --amend

    修改最近的一次 commit 信息,修改后 commit id 会变

  2. rebase

    通过 git rebase -i HEAD~3 可以实现对最近三个 commit 的修改

    • 合并 commit
    • 修改具体的 commit message
    • 删除某个 commit
  3. filter --branch

    可以指定删除所有提交中的某个文件或者全局修改邮件地址等操作

这里尝试一下第一个命令,执行后会打开文本编辑窗口让你修改

1
git commit --amend

image-20220525211603557

可以看见 commit id 已经改变了

image-20220525211742208

而老的 commit 其实就已经没有 ref 去指向了,这时就变成了一个悬空的 object,可以使用下面的命令来查找

1
git fsck --lost-found

image-20220525212035044

Git GC

GC

删除一些不需要的 object,已经会对 object 进行一些打包压缩来减少仓库体积

使用 --prune 参数指定修剪多久之前的对象,默认是两周

Reflog

记录操作日志,防止误操作后数据丢失,


尝试以下命令

1
2
git reflog expire --expire=now --all
git gc --prune=now

然后观察目录变化,发现变化很大

image-20220525212536083

完整的 Git 视图

image-20220525212629318

Git Clone & Pull & Fetch

Clone

拉取完整的仓库到本地,可以指定分支、深度

Fetch

将远程某些分支最新代码拉取到本地,不会 merge

会修改 refs/remote 内的分支信息,如果需要和本地代码合并需要手动操作

Pull

拉取并合并,相当于 git fetch + git merge

也可以通过 git pull --rebase 完成 git fetch +get rebase 操作

Git Push

常用命令

一般使用 git push orgin master 即可

冲突问题

  1. 如果本地的 commit 记录和远端的 commit 记录历史不一致,则会产生冲突
  2. 如果该分支就自己一个人使用,或团队内确认可以修改历史则可以通过 -f 参数强制推送

推送限制规则

可以通过保护分支,来配置一些保护规则

Git 研发流程

不同的工作流

  • 集中式工作流

    代表:SVN,Gerrit

    只依托于主干分支进行开发,不存在其他分支

  • 分支管理工作流

    代表GitHub,Gitlab

    可以定义不同特性的开发分支,上线分支,在分支开发完成后在通过 MR/PR 合入主分支

集中式工作流

image-20220525214004158

Gerrit

image-20220525214128038

image-20220525214214758

image-20220525214222928

分支管理工作流

工作流 特点
Git Flow 分支类型丰富,规范严格
GitHub Flow 只有主干和分发分支,规则简单
GitLab Flow 在主干分支和开发分支之上构建环境分支,版本分支,满足不同发布和环境的需要

Git Flow

17cda4db-cc5d-46d0-adeb-7cdd32f3a05f

image-20220525214528516

GitHub Flow

只有一个主干,基于 Pull Request 往主干分支提交代码

image-20220525214814587

GitLab Flow

GitLab 推荐的工作流是上面两者的优化

image-20220525215728533

代码合并

Fast-Forward

1
git merge test --ff-only

Tree-Way Merge

1
git marge test --no-ff
913d1ef3-be50-4d89-9306-9fdc17756cac

如何选择合适的工作流

没有最好的,只有最合适的

image-20220525220355920