Git基础操作大全 | 青训营笔记

149 阅读8分钟

这是我参入[第三届青训营-后端场]笔记创作活动的第10篇笔记。

Git使用入门

项目初始化

mkdir study

cd study

git init

其他参数

--initial-branch 初始化的分支

--bare 创建一个裸仓库

--tempalate 可以通过模板来创建预先构建好的自定义git目录

SSH remote

免密配置

SSH可以通过公私钥的机制,将生成公钥存放在服务端,从而实现免密访问

目前的Key的类型四种,分别是dsa、rsa、ecdsa、ed25519

默认使用的是rsa,由于一些安全问题,现在已经不推荐使用dsa和rsa了,优先推荐使用ed25519

Objects

commit/tree/blob 在git里面都统一称为Object,除此之外还有个tag的object

Blob:存储文件的内容

Tree:存储文件的目录信息

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

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

1.通过Commit寻找到Tree信息,每个Commit都会存储Tree ID

2.通过Tree存储的信息,获取到对应的目录树信息

3.从tree中获得blob的ID,通过Blob ID获取对应的文件内容

Refs

Refs文件存储的内容:对应的Commit ID

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

不同种类的ref

refs/heads 前缀表示的是分支,除此之外还有其他种类的ref,比如refs/tags前缀表示的是标签

Branch

git checkout -b 可以创建一个新分支

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

Tag

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

通过git tag生成tag

Annotation Tag

什么是附注标签?

一种特殊的Tag,可以提供一些额外的信息。

如何创建附注标签?

git tag -a v0.0.2 -m "xxxx"

修改历史版本

1.commit --amend

通过这个命令可以修改最近一次commit信息,修改之后commit id 会变

2.rebase

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

1.合并commit

2.修改具体的commit message

3.删除某个commit

3.filter --branch

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

新增的Object==》导致悬空的Object:没有ref指向的object

Git GC

GC

通过git gc 命令,可以删除一些不需要的object

以及会对object进行一些打包压缩来减少仓库的体积

Reflog

reflog是用于记录操作日志,防止误操作后数据丢失

通过reflog来找到丢失的数据,手动将日期设置为过期

指定时间

git gc prune=now 指定的是修剪多久之前的对象,默认是两周前

Git clone pull fetch

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

fetch 将远端某些分支最新代码拉取到本地,不会执行merge操作

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

pull 拉取远端分支,冰河本地进行合并,操作等同于git fetch + git merge,

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

可能存在冲突,需要解决冲突

git push

Push是将本地代码同步至远端的方式

常用命令:

一般使用git push origin master 命令即可完成

冲突问题

1.如果本地的commit记录和远端的commit历史不一致,则会产生冲突,比如git commit --amend or git rebase 都有可能导致这个问题。

2.如果该分支就自己一个人使用,或者团队内确认过可以修改历史则可以通过git push origin master -f 来完成强制推送,一般不推荐主干分支进行该操作,正常都应该解决冲突后再进行推送

推送规则限制

可以通过保护分支,来配置一些保护规则,防止误操作,或者一些不合规的操作出现,导致代码丢失

1.为什么我明明配置Git配置,但是依然没有办法拉取代码?

免密认证没有配

instead of 配置没有配,配的SSH免密配置,但是使用的还是HTTP协议访问

2.为什么我Fetch了远端分支,但是我看本地当前的分支历史还是没有变化?

Fetch会把代码拉取到本地的远端分支,但是并不会合并到当前分支,所以当前分支历史没有变化。

1.在Gerrit平台上使用Merge的方式合入代码

2.不了解保护分支,Code Review,CI等概念,研发流程不规范

3.代码历史混乱,代码合并不清晰

集中式工作流

只依托于master分支进行研发活动

工作方式

1。获取远端master代码

2.直接master分支完成修改

3。提交前拉取最新的master代码和本地代码进行合并(使用rebase),如果有冲突需要解决冲突

4.提交本地代码到master

Gerrit

基本原理:

1.依托于Change ID概念,每个提交生成一个单独的代码评审。

2.提交上去的代码不会存储在真正的refs/heads/下的分支中,而是存在一个refs/for/的引用下。

3.通过refs/meta/config下的文件存储代码的配置,包括权限,评审等配置,每个Change都必须要完成Review后才能合入。

优点:

1.提供强制的代码评审机制,保证代码的质量

2.提供更丰富的权限功能,可以针对分支做细粒度的权限管控

3.保证master的历史整洁性

4.Aosp多仓的场景支持更好

缺点:

1.开发人员较多的情况下,更容易出现冲突

2.对于多分枝的支持较差,想要区分多个版本的线上代码时,更容易出现问题

3.一般只有管理员才能创建仓库,比较难以在项目之间形成代码复用,比如类似fork操作就不支持

分支管理工作流

Git Flow 分支类型丰富,规范严格:

包含五种类型的分支

Master:主干分支

Develop:开发分支

Feature:特性分支

Release:发布分支

Hotfix:热修复分支

优点:

如果能按照定义的标准严格执行,代码会很清晰,并且很难出现混乱。

缺点:

流程过于复杂,上线的节奏会比较慢。

由于太复杂,研发容易不按照标准执行,从而导致代码出现混乱。

Github Flow

Github的工作流,只有一个主干分支,基于Pull Request往主干分支中·提交代码。

选择团队合作的方式

1.owner创建好仓库后,其他通过通过Fork的方式来创建自己的仓库,并在fork地仓库上进行开发

2.owner创建好仓库后,统一给团队内成员分配权限,直接在同一个仓库内进行开发

Gitlab Flow

Gitlab推荐的工作流是在GitFlow和Github Flow上做出优化,既保持了单一主分支的简便,又可以适应不同的开发环境。

原则:upstream first上游优先

只有在上游分支采纳的代码才可以进入到下游分支,一般上游分支就是master

代码合并

Fast-Forward

不会产生一个merge节点,合并后保持一个线性历史,如果target分支有了更新,则需要通过rebase操作更新source branch 后才可以合入。

命令:git merge test --ff-only

Three-Way Merge

三方合并,会产生一个新的merge节点

命令:git merge test --no-ff

如何选择合适的工作流

选择原则:没有最好的,只有最合适的

针对小型团队合作,推荐使用Github工作流即可

1.尽量保证少量多次,最好不要一次性提交上千行代码

2.提交Pull Request后最少需要保证有CR后再合入

3.主干分支尽量保持整洁,使用fast-forward合入方式,合入前进行rebase

大型团队合作,根据自己的需要指定不同的工作流,不需要局限在某种流程中

1.在Gerrit平台上使用Merge的方式合入代码

Gerrit是集中式工作流,不推荐使用Merge方式合入代码,应该是在主干分支开发后,直接Push

2.不了解保护分支,Code Review,CI等概念,研发流程不规范

保护分支:防止用户直接向主干分支提交代码,必须通过PR来进行合入

Code Review,CI:都是在合入前的检查策略,Code Review是人工进行检查,CI则是通过一些定制化的脚本来进行一些校验。

3.代码历史混乱,代码合并方式不清晰

不理解Fast Forward 和 Three Way Merge的区别,本地代码更新频繁的使用Three Way的方式,导致生成过多的Merge节点,使提交历史变得复杂不清晰。