Git 学习笔记——git 文件结构、内部原理和 GitFlow 工作流理解

Git 内部原理

本地仓库下有个名为 .git 的隐藏目录,本章将带你了解这个目录下的文件结构和内容。

# 创建一个名为 test 的新仓库
$ mkdir test
$ cd test
$ git init

新仓库会自动创建一个 .git 目录,该目录包含了几乎所有 Git 存储和操作内容。若想备份或复制一个版本库,只需将该目录拷贝至另一处即可。

目录结构如下:

├── *config               配置信息(比如本地 repo 是否大小写敏感, remote 端 repo 的 url, 用户名邮箱等) 
├── description           无需关心(仅供 GitWeb 程序使用)
├── *HEAD                 目前被检出的分支
├── index                 保存暂存区信息
│
│
├── hooks/                钩子脚本(执行 Git 命令时自动执行一些特定操作)
├── info/                 包含一个全局性排除文件
│   └── exclude           放置不希望被记录在 .gitignore 文件中的忽略模式
├── logs/                 记录所有操作
├── *objects/             存储所有数据内容
│   ├── SHA-1/            保存 git 对象的目录(三类对象 commit, tree 和 blob)
│   ├── info/
│   └── pack/             
└── *refs/                存储指向数据(分支、远程仓库和标签等)的提交对象的指针
    ├── heads/           
    ├── remotes/         
    └── tags/     

objects目录

对象存储

初始化仓库后:objects 目录下只有子目录 pack 和 info ,但均为空。

运行以下命令,创建两个文件并提交

# 创建了 blob1
$ echo "version1" > test.txt
$ git add .

# 创建了 blob2
$ mkdir folder1
$ echo "file inside folder1" >folder1/file.txt
$ git add .

# 创建了 tree1, tree2 和 commit
$ git commit -m "test"
[master (root-commit) 67f0856] test
 2 files changed, 2 insertions(+)
 create mode 100644 folder1/file.txt
 create mode 100644 test.txt

此时查看 objects 目录,会发现新增了 5 个子目录。

.git/objects
├── 40
│   └── fa006a9f641b977fc7b3b5accb0171993a3d31
├── 5b
│   └── dcfc19f119febc749eef9a9551bc335cb965e2
├── 67
│   └── f0856ccd04627766ca251e5156eb391a4a4ff8
├── 87
│   └── db2fdb5f60f9a453830eb2ec3cf50fea3782a6
├── ac
│   └── f63c316ad27e8320a23da194e61f45be040b0b
├── info
└── pack

现在解答下面几个问题:

1. 什么是对象?

objects目录下存储三种对象:数据对象(blob),树对象(tree)和提交对象(commit)。

5个子目录的含义如下图所示:2个blob, 2个tree和1个commit

object

2. Git如何存储对象?

Git会根据对象内容生成一个 SHA-1 哈希值(称为该文件的校验和)

例如:40fa006a9f641b977fc7b3b5accb0171993a3d31

截取校验和的前两个字符 => 用于命名子目录

例如:40

校验和余下的 38 个字符 => 用于文件名

例如:fa006a9f641b977fc7b3b5accb0171993a3d31

将对象内容存储在 子目录/文件名 内

3. 如何查看对象的存储内容

可以根据校验和,查看存储的内容及对象类型

# 查看文件类型
$ git cat-file -t 40fa006a9f641b977fc7b3b5accb0171993a3d31
blob

# 查看文件内容
$ git cat-file -p 40fa006a9f641b977fc7b3b5accb0171993a3d31
file inside folder1

包文件的存储机制

Gi t默认保存文件快照,即保存每个文件每个版本的完整内容。但假设只更改了某大文件中的一个字符,保存两次全部内容是不是有点低效?

Git最初向磁盘存储对象时采用"松散"对象格式;但为了节省空间和提高效率,Git 会时不时将多个对象打包成一个称为"包文件"。

当版本库中有太多的"松散"对象,或者手动执行 git gc 命令,或者向远程服务器执行推送时,Git 都会进行打包。

运行以下命令,手动打包

$ git gc
Enumerating objects: 113, done.
Counting objects: 100% (113/113), done.
Delta compression using up to 8 threads
Compressing objects: 100% (92/92), done.
Writing objects: 100% (113/113), done.
Total 113 (delta 15), reused 102 (delta 13), pack-reused 0

此时查看objects目录,会发现很多子目录不见了,而 info 和 pack 目录非空。

.git/objects
├── info
│   ├── commit-graph
│   └── packs
└──  pack
    ├── pack-XXX.idx
    └── pack-XXX.pack

包文件 pack-XXX.pack:包含了刚才从文件系统中移除的所有对象的内容;
索引文件 pack-XXX.idx:包含了包文件的偏移信息。通过索引文件可以快速定位任意一个指定对象

refs目录

Git把一些常用的 SHA-1 值存储在文件中,用文件名来替代,这些别名就称为引用。有三种引用类型:heads, remotes和tags。

运行以下命令,更新refs目录下的内容

# 基于当前commit创建新分支test
git branch test

# 基于 commit 打tag
git tag -a v1.0 <commitId>

# 连接远程仓库
git remote add origin https://github.com/datawhalechina/faster-git.git
git fetch

# 本地修改文件,然后运行git stash
echo "version2" > test.txt
git stash

此时查看 refs 目录,内容如下

.git/refs
├── heads         记录本地分支的最后一次提交
│   ├── master
│   └── test
├── remotes       记录远程仓库各分支的最后一次提交
│   └── origin
│         └── main
├── tags
│   └── v1.0
└── stash

HEAD引用

HEAD 引用有两种类型

两种hard引用

远程引用

存储位置: .git/refs/remotes 目录下
指代内容:远程仓库各分支的最后一次提交
注意点:用于记录远程仓库;文件是只读的,乱改就崩了

标签引用

tag 主要用于发布版本的管理:一个版本发布之后,我们可以为 git 打上 v1.0 v2.0 … 这样的标签

存储位置:.git/refs/heads 目录下
指代内容:tag 可以指向任何类型(更多的是指向一个commit,赋予它一个更友好的名字)
文件内容:SHA-1值

stash

存储位置:.git/refs/stash 文件
指代内容:当你想转到其他分支进行其他工作,又不想舍弃当前修改的代码时 - stash可把当前的修改暂存起来

config文件

运行以下命令,连接远程仓库

git remote add origin https://github.com/datawhalechina/faster-git.git
git fetch

此时查看 .git/config 文件,会发现新添加了一段小节:

[remote "origin"]
	url = https://github.com/datawhalechina/faster-git.git
	fetch = +refs/heads/*:refs/remotes/origin/*

引用规范由 git remote add origin 命令自动生成

获取远端 refs/heads/ 下的所有引用
将其写入本地的 refs/remotes/origin/ 中
更新本地的 .git/config 文件
一些常用命令:

常用命令

环境变量

Git有三种环境变量:

1)系统变量
适用范围:对所有用户都适用
命令选项:git config --system

2)用户变量
适用范围:只适用于该用户
命令选项:git config --global

3)本地项目变量
适用范围:只对当前项目有效
命令选项:git config --local
存储位置:.git/config

一些常用命令:
查看所有配置 git config --list
配置用户名 git config --global user.name “你的用户名”
配置邮箱 git config --global user.email “你的邮箱”

练习

  1. Tom 想把自己的本地分支 feature1(当前也为 HEAD ),推送到远端分支的 feature,应当执行什么命令?(BC)
    A. git push origin/feature1:feature
    B. git push origin feature1:feature
    C. git push origin HEAD:feature
    D. git push origin :feature
  2. Tom工作在多个Git项目上,大部分属于公司的项目,都是使用他的工作邮箱提交。今天他新建了一个私人项目,想使用私人邮箱进行提交。他运行什么命令更合适呢?(C 当前目录有效)
    A. git config --system user.email “tom@私人邮箱”
    B. git config --global user.email “tom@私人邮箱”
    C. git config --local user.email “tom@私人邮箱”
    D. 以上选项都可以

GitFlow 工作流

在实际项目开发工作中,常常会有自测、联调、提测、线上紧急修复等多工作环节,对应可能需要本地、内测、开发、测试、生产等多环境部署代码的需求,对应每个环节会产生不同的分支;本章将从 Git-Flow 模型原理出发,通过命令行演示实际可操作⼿段并进⾏总结,最终希望 Git-Flow 在实际项⽬中应⽤起来,从⽽⾼效完成代码开发、版本管理等实际⼯作。

(注:不同的公司或者不同的项目的GitFlow工作流模型标准也不同,具体以实际应用为准;本文提供的为常用模板,较为全面和通用,建议多加练习,达到熟练掌握的程度)

深⼊理解Git-Flow⼯作流模型原理

Git-Flow模型解决什么问题?

为了解决实际项⽬中代码开发、代码测试、bug修复、版本发布等⼀系过程列严重耦合从⽽产⽣各种问题,如冲突过度、版本混乱。

Git-Flow模型⼜是如何解决上述问题的呢?

基于Git定义5种类型的分⽀,各分⽀严格定义其指责、起⽌点等,从⽽使开发、测试、发版等过程有条不紊进⾏。

6.1.1 Git-Flow流程图
该流程图完整描述Git-Flow模型处理过程,当我们深⼊理解各分⽀,然后结合项⽬阶段与⾃身的⻆⾊(开发/测试/项⽬经理),就会发现每个角色在某个阶段需要关注的可能也就⼀两个分⽀,⽐如在开发阶段,开发⼈员只需关注⾃⼰的新功能分⽀(Feature分支);release阶段,测试⼈员和开发⼈员都只需关注Release分⽀,各⾃的职责有所差异⽽已;具体如下图(建议读者动手手绘一遍该流程图以便于加深理解):

git-flow流程图

Git-Flow 各分⽀的说明

各分支说明

不同⻆度理解各分⽀

  • ⽣命周期
    Master分⽀和Develop分⽀贯穿项⽬;其他分⽀均为承担特定指责的临时分⽀。
  • 项⽬阶段
    开发阶段主要涉及Feature分⽀、Develop分⽀; 发布阶段 主要涉及Release分⽀、Production分⽀、Develop分⽀; 紧急修复阶段 主要涉及Hotfix分⽀、Production分⽀、Develop分⽀。
  • 成员关注点
    开发⼈员关注Develop分⽀、Feature分⽀以及特殊阶段关注Hotfix、Release分⽀的bug修复; 测试⼈员关注 Release分⽀、Hotfix分⽀的功能测试;项⽬经理关注Production分⽀、Release分⽀。

另外要说明,项⽬阶段在时间纬度有可能重叠。
⽐如:release 阶段(当前版本)与下各版本的开发阶段可同时存在,因为当前release阶段的发起同时也就意味着下⼀个release的开发阶段的开始;⼀旦线上出现bug(任何时候都可能出现),紧急修复阶段就可能与开发阶段、发版阶段重叠…
因此,要求团队成员都要理解Git-Flow⼯作流,以及⾃身所处的项⽬阶段.

命令行演示⼀个完整的Git-Flow流程

原理总是枯燥的,接下来实践⼀个从功能开发到版本发布的完整的流程,感受⼀下Git-Flow的具体操作.

特此说明,以下shell命令是在win10环境下,‘/e/PycharmProjects/DatawhaleChina’目录,使用git bash工具进行演示;‘′符号所在行为演示命令,如有内容输出,会在‘’ 符号所在行的下面输出。

初始化项⽬,创建Develop分⽀

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina
$ pwd
/e/PycharmProjects/DatawhaleChina

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina
$ mkdir git-demo-workflow-project

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina
$ cd git-demo-workflow-project/

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project
$ touch readme.md

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project
$ git init
Initialized empty Git repository in E:/PycharmProjects/DatawhaleChina/git-demo-workflow-project/.git/

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git add .

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git commit -m "init"
[master (root-commit) 1ae2455] init
 1 file changed, 0 insertions(+), 0 deletions(-)
 create mode 100644 readme.md

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git checkout -b develop master
Switched to a new branch 'develop'

模拟开发阶段过程

(创建新功能Feature分⽀、实现⼀个⽤户登录模块、然后合并到Develop分⽀、删除功能分⽀)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git checkout -b feature-login develop
Switched to a new branch 'feature-login'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ touch LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$echo "hi, this is user html" > LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ cat LoginUser.html
hi, this is user html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ ls
LoginUser.html  readme.md

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git add .
warning: LF will be replaced by CRLF in LoginUser.html.
The file will have its original line endings in your working directory

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git commit -m "feat: add LoginUser.html"
[feature-login 182444e] feat: add LoginUser.html
 1 file changed, 1 insertion(+)
 create mode 100644 LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ touch LoginUser.js


Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ echo "hi, this is user js" > LoginUser.js

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git add .
warning: LF will be replaced by CRLF in LoginUser.js.
The file will have its original line endings in your working directory

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git commit -m "feat: add LoginUser.js"
[feature-login b0d494c] feat: add LoginUser.js
 1 file changed, 1 insertion(+)
 create mode 100644 LoginUser.js

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git status
On branch feature-login
nothing to commit, working tree clean

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (feature-login)
$ git checkout develop
Switched to branch 'develop'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git merge --no-ff feature-login
Merge made by the 'recursive' strategy.
 LoginUser.html | 1 +
 LoginUser.js   | 1 +
 2 files changed, 2 insertions(+)
 create mode 100644 LoginUser.html
 create mode 100644 LoginUser.js

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git branch -d feature-login
Deleted branch feature-login (was b0d494c).

模拟Release阶段过程

(创建Release分⽀、进⾏bug修复、合并到Production分⽀与Develop分⽀)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git checkout  -b release-v0.1 develop
Switched to a new branch 'release-v0.1'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (release-v0.1)
$ echo "bugifx LoginUser.html" >> LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (release-v0.1)
$ git add .
warning: LF will be replaced by CRLF in LoginUser.html.
The file will have its original line endings in your working directory

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (release-v0.1)
$ git commit -m "fix: bugfix for LoginUser.html"
[release-v0.1 a37a88c] fix: bugfix for LoginUser.html
 1 file changed, 1 insertion(+)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (release-v0.1)
$ git checkout master
Switched to branch 'master'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git merge --no-ff release-v0.1
Merge made by the 'recursive' strategy.
 LoginUser.html | 2 ++
 LoginUser.js   | 1 +
 2 files changed, 3 insertions(+)
 create mode 100644 LoginUser.html
 create mode 100644 LoginUser.js

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git tag v0.1

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git checkout develop
Switched to branch 'develop'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git merge --no-ff release-v0.1
Merge made by the 'recursive' strategy.
 LoginUser.html | 1 +
 1 file changed, 1 insertion(+)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git branch -d release-v0.1
Deleted branch release-v0.1 (was a37a88c).

模拟线上故障,创建Hotfix分⽀

(创建Hotfix分⽀、进⾏bug修复、合并到Production分⽀与Develop分⽀)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git checkout -b hotfix-v0.1.1 master
Switched to a new branch 'hotfix-v0.1.1'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ git status
On branch hotfix-v0.1.1
nothing to commit, working tree clean

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ echo "hotfix for LoginUser.html" >> LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ cat LoginUser.html
hi, this is user html
bugifx LoginUser.html
hotfix for LoginUser.html

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ git add .
warning: LF will be replaced by CRLF in LoginUser.html.
The file will have its original line endings in your working directory

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ git commit -m "hotfix: do something for LoginUser.html"
[hotfix-v0.1.1 bcb680e] hotfix: do something for LoginUser.html
 1 file changed, 1 insertion(+)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (hotfix-v0.1.1)
$ git checkout master
Switched to branch 'master'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git merge --no-ff hotfix-v0.1.1
Merge made by the 'recursive' strategy.
 LoginUser.html | 1 +
 1 file changed, 1 insertion(+)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git tag v0.1.1

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (master)
$ git checkout develop
Switched to branch 'develop'

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git merge --no-ff hotfix-v0.1.1
Merge made by the 'recursive' strategy.
 LoginUser.html | 1 +
 1 file changed, 1 insertion(+)

Administrator@WIN MINGW64 /e/PycharmProjects/DatawhaleChina/git-demo-workflow-project (develop)
$ git branch -d hotfix-v0.1.1
Deleted branch hotfix-v0.1.1 (was bcb680e).

参考文件

https://gitee.com/martin/faster-git/tree/main/lecture05

https://gitee.com/martin/faster-git/tree/main/lecture06

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值