这是我参与「第三届青训营 -后端场」笔记创作活动的的第2篇笔记
一、学习准备
1 Git安装(Ubuntu 18.04)
sudo apt install git-all
2 Git基本操作(from:菜鸟教程)
- 常用命令:
- git clone
- git push
- git add
- git commit
- git checkout(切换分支)
- git pull
2.1 创建仓库命令
| 命令 | 说明 |
|---|---|
git init | 初始化仓库 |
git clone | 拷贝一份远程仓库 |
2.2 提交与修改
| 命令 | 说明 |
|---|---|
git add | 添加文件到暂存区 |
git status | 查看仓库当前的状态,显示有变更的文件 |
git diff | 比较文件的不同,即暂存区和工作区的差异 |
git commit | 提交暂存区到本地仓库(注意和git push区分) |
git reset | 回退版本 |
git rm | 删除工作区文件 |
git mv | 移动或重命名工作区文件 |
2.3 提交日志
| 命令 | 说明 |
|---|---|
git log | 查看历史提交记录 |
git blame <file> | 以列表形式查看指定文件的历史修改记录 |
2.4 远程操作
| 命令 | 说明 |
|---|---|
git remote | 远程仓库操作 |
git fetch | 从远程获取代码库 |
git pull | 下载远程代码并合并 |
git push | 上传远程代码并合并 |
3 github账号关联
3.1 配置用户名和邮箱
-
设置用户名
git config --global user.name "username" -
设置邮箱
git config --global user.email useremail@email.com -
查看用户名 git config user.name
-
查看邮箱 git config user.email
用户名和邮箱地址是本地Git客户端的一个变量。用户每次提交代码都会记录用户名和邮箱。
3.2 创建SSHkey
ssh-keygen -t rsa -C "<your@email.com>"
- 在root用户文件夹(登录用户名)下生成 .ssh文件夹,里面包含 id_rsa 和id_rsa.pub两个文件
3.3 在github中配置(gitlab同理)
(1)
(2)
(3)
(4)
- title随意
- 将id_rsa.pub中的内容复制入Key框中
3.3 验证
ssh -T git@github.com
yes
二、Git的基本使用
1 Git初始化
1.1 项目初始化
mkdir git-demo
cd git-demo
git init
git init
git --initial-branch
- 初始化的分支
git init --bare
–bare参数,一般用来初始化一个空的目录,作为远程存储仓库
git init --template template_dir
- –template 参数,相当于复制模版仓库,template_dir模版仓库的目录
git init --separate-git-dir git_dir
- 相当于复制仓库git_dir(可以是工作仓库,也可以是存储仓库)中的.git目录
1.2 Git目录简介
tree .git
| 文件(夹)名 | 说明 |
|---|---|
| hooks | 包含客户端或服务端的钩子脚本(可以自定义在代码提交过程中应该做的事,比如在代码提交前检测代码格式是否符合规范) |
| info | 包含一个全局性排除文件(记录不需要git进行管理的文件) |
| logs | 保存日志信息 |
| objects | 存储所有数据内容 |
| refs | 存储指向数据(分支)的提交对象的指针 |
| config | 文件包含项目特有的配置选项 |
| description | 用来显示对仓库的描述信息 |
| HEAD | 文件指示目前被检出的分支 |
| index | 文件保存暂存区信息 |
2 Git配置
2.1 Git Config
- --global
- --system
- --local
每个级别的配置可能会重复,但是低级别的配置会覆盖高级别的配置
2.1.1 用户名配置
-
设置用户名
git config --global user.name "username" -
设置邮箱
git config --global user.email useremail@email.com
2.1.2 instead of 配置
git config --global url."https://".insteadOf git://
可以把 git:// 替换成 https:// 方便使用 https 协议
2.1.3 Git命令别名配置
git config --global alias.cin "commit --amend --no-edit"
将commit --amend --no-edit简化为cin
2.2 Git Remote
Remote 可以有几个,每一个通常是只读、只写或读/写,它可以理解为是远端代码托管平台的地址。Git 借助 remote 可以实现添加远程存储库、删除不再有效的远程存储库、管理各种远程分支并定义它们是否被跟踪等等。
2.2.1 添加remote
git remote add origin_ssh git@github.com:git/git.git
- 其中的origin_ssh可以看作是别名
git remote add origin_http https://github.com/git/git.git
可以通过配置remote实现fetch和push指向不同的源
2.2.2 查看Remote
git remote --v
cat .git/config
- 实际上,修改remote的实际结果就是修改了config文件中对应的部分,所以也可以直接修改config文件
- 可以用
-h参数查询git remote的其他具体用法
2.2.3 HTTP Remote(不推荐)
url样式:github.com/git/git.git
(1)免密配置1
(2)免密配置2
2.2.4 ssh Remote
url样式:git@github.com:git/git.git
(如果拉不下代码,可能是新版本windows无法使用不安全的加密方式)
可以配置多个key,但是需要在git上进行key的指定
3 代码提交
3.1 Git Add
git add:将代码从工作区提交到暂存区
touch readme.md
vim readme.md //写入Hello World!
git add .
- 可以发现,objects文件夹中多了一个文件。
- 可以用
git cat-file -p 980a0d5f19a64b4b30a87d4206aade58726b60e3读取出文件内容
3.2 Git Commit
git commit -m "add readme"
object文件夹中新增了两个文件,一个tree(a8),一个commit(4d)
4 Objects
4.1 Objects的组成
commit / tree / blob在git中统称为Object。除此之外,还有个tag的object。
- Blob--存储文件的内容
- Tree--存储文件的目录信息
- Commit--存储提交信息,一个Commt可以对应唯一版本的代码
- tag--存储真正指向的Commit Object以及一些附加信息
4.2 Objects中信息的串联
(1)通过Commit寻找到Tree信息,每个Commit都会存储着对应的Tree ID;
(2)通过Tree存储的信息,获取到对应的目录树信息;
(3)从Tree中获得blob的ID,通过Blob ID获取对应的文件内容。
5 Refs
5.1 Refs/heads
Refs/heads前缀表示的是分支
refs/heads/master中存储的是对应分支的commit ID
尝试创建并切换到新分支
因此把ref当作指针,指向对应的Commit来表示当前Ref对应的版本
git checkout -b可以创建一个新分支,分支一般用于开发阶段,可以不断添加Commit进行迭代(commit本身不会变更,指向变更了)
5.2 Refs/tag
5.2.1 tag
refs/tags前缀表示的是标签
在Git中,用标签(tag)为项目的各版本提供了一个让人容易记住的有意义的名字。标签总是跟某个commit绑在一起——发布一个版本时,在版本库中打一个标签,就可以实现利用标签提取对应的历史版本。标签一般表示的是一个稳定版本,指向的Commit一般不会变更 标签的使用文档(from:廖雪峰)
使用git tag v0.0.1生成tag
查看tag的内容
指向的正是当前分支的commit object(此处的commit因为没有变动过,所以和master分支指向相同)
5.2.2 Annotation Tag
一种特殊的Tag,可以给Tag提供一些额外的信息。 执行命令
git tag -a v0.02 -m"add feature1"
发现objects中多出了一个object,这个object的类型正是前面没有介绍的tag object
对比发现,
- 不加注释的tag直接指向了当前commit;
- 而Annotation指向了tag object。 查看tag object
发现tag object中保存了实际指向的commit object、annotation以及一些其他信息。
6 历史版本
6.1 追溯历史版本
Commit里会存有
parent commit字段,通过commit的串联获取历史版本代码 执行以下命令:
vim readme.md
git add.
git commit -m "update readme"
发现
objects中增加了三个objcet,分别是tree、blob以及commit
使用git log命令,获取最新的Commit ID
使用git cat-file -p命令可以在显示的结果中找到当前commit版本的parent的Commit ID
6.2 修改历史版本
6.2.1 git commit --amend
通过这个命令可以修改最近一次的commit的message信息,修改之后的commit id会变化,但是其tree和parent指向不会发生变化
不是回滚版本,只是修改
message信息
6.2.2 git rebase
通过git rebase -i HEAD~3 可以实现对最近三个commit的修改,比如:
- 合并commit
- 修改具体的commit message
- 删除某个commit
6.2.3 filter --branch
filter --branch
该命令可以指定删除所有提交中的某个文件或者全局修改邮箱地址等操作
6.2.4 悬空object
通过git fsck --lost-found命令可以查看当前是否有悬空的Object(没有ref指向的object)
通过上述操作,
git commit --amend命令使得之前的那个commit id指代的代码版本已经没有作用了。
6.2.5 Git GC
- GC
通过git gc命令,可以删除一些不需要的object,以及对object进行一些打包压缩来减少仓库的体积
- Reflog
reflog用于记录操作日志,防止误操作之后数据丢失,通过reflog来找到丢失的数据,手动将日志设置为过期
- 指定时间
git gc prune=now指的是修剪多久之前的对象,默认是两周前
执行以下命令
git reflog expire --expire=now --all
git gc --prune=now
观察
.git目录
发现git目录中删除空悬对象,并对其他对象进行了打包
7 远端操作
7.1 Git Clone & Pull & Fetch
- Clone
拉取完整的仓库代码到本地目录,可以指定分支,深度。
- Fetch(不清楚远端情况)
将远端的某些分支最新代码拉取到本地,不会执行merge操作,会修改refs。remote内的分支信息,如果需要和本地代码合并需要手动操作。
- Pull(清楚远端情况)
拉取远端分支,并和本地代码进行合并,操作等同于git fetch + git merge,也可以通过git pull --rebase 完成 git fetch + git rebase操作。可能存在冲突,需要解决。
7.2 Git push
push是将本地代码同步至远端的方式.
7.2.1 常用命令:
一般使用 git push origin master 命令。
7.2.2 冲突问题:
-
本地的commit 记录和远端 commit 不一致,会产生冲突,如
git commit --amendorgit rebase命令都有可能导致这个问题。 -
如果该分支只有自己使用,或者团队内确认可以修改历史,则可以通过
git push origin master -f来完成强制推送,一般不推荐主干分支执行该操作,正常都应该解决冲突后再进行推送。
7.2.3 推送规则:
设置一些分支保护规则防止误操作(Branch protection rules)
三、Git研发流程
1 集中式工作流
(1)获取远端master分支代码;
(2) 直接在master分支完成修改;
(3) 提交前拉取最新master代码和本地代码合并使用(rebase),如果有冲突解决冲突;
(4)提交本地代码到master。
2 分支管理工作流
2.1 Git Flow
分支类型丰富,规范严格
- Master:主干分支
- Develop:开发分支
- Feature:特性分支
- Release:发布分支
- Hotfix:热修复分支
2.2 Github Flow
Github的工作流,只有主干分支和开发分支,规则简单,基于Pull Request 往主干分支中提交代码。
团队的合作方式:
- owner 创建好仓库之后,其他用户通过Fork的方式创建自己的仓库,并在fork的仓库上进行开发。
- owner 创建好仓库之后,统一给团队内成员分配权限,直接在同一个仓库内进行开发。
接下来用github模拟github-flow的工作模式。
2.2.1 创建仓库并克隆到本地
先到自己的GitHub中创建一个仓库:github-flow-demo,并克隆到本地。(路径忘记切了...)
git clone git@github.com:laochonger/github-flow-demo.git
选择ssh协议
2.2.2 本地仓库中创建文件并commit
2.2.3 git push
浏览器中查看
origin为当前项目的默认配置
2.2.4 创建一个feature分支,修改readme文件后commit并push
2.2.5 pull request
上图中GitHub自动生成了一个向main分支合入的pull request链接,复制后去浏览器打开。(也可以直接在平台上打开)
创建一个pull request
确认没问题后,merge
可以看到,已经merge成功了,回到本地仓库,切换回master分支,拉取远程master分支最新的代码。
2.2.6 Branch protection rule(配置分支保护规则)
2.3 Gitlab Flow
在主干分支和开发分支的基础上构建环境分支,版本分支,满足不同发布or环境的需要。
原则:upstream first 上游优先
只有上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master。
3 代码合并
3.1 Fast-Forward
不会产生一个merge节点,合并之后保持一个线性的历史,如果target分支又了更新,则需要通过rebase操作更新source branch 后才可以合入。
3.2 Three-Way Merge
三方合并,会产生一个新的merge节点。
4 如何选择合适的工作流
没有最好,只有最合适。
针对小团队合作,推荐使用 Github 工作流即可:
- 尽量保证少量多次,最好不要一次性提交上千行代码
- 提交Pull Request 后最少需要保证有CR(Code Review)后再合入
- 主干分支尽量保持整洁,使用fast-forward 合入方式,合入前进行rebase