目录
一、Git简介
二、Git的一些基本操作
1、安装Git
2、配置Git全局用户名和全局邮箱
3、创建本地仓库、创建远程仓库、本地仓库关联远程仓库
4、代码重置revert
5、节点阅读、分支和分支小标签阅读、创建分支和合并分支阅读
三、一些规范
1、gitignore规范
2、commit规范
3、分支规范
4、打tag规范
一、Git简介
Git和SVN的区别
Git是分布式版本控制系统,SVN是集中式版本控制系统。
所谓分布式主要是指本地仓库这个概念,也就是说我们每个人都可以在自己的电脑上通过本地仓库来进行源代码版本控制,它是去中心化的、不需要联网的;所谓集中式主要是指远程仓库这个概念,也就是说我们每个人都必须直接面向同一个远程仓库来进行源代码版本控制,它是中心化的、需要联网的。当然Git也有远程仓库这个概念,但它的远程仓库并非一个必要角色,而只是为了方便不同空间、多人之间的合作而扩展的一个辅助工具。
Git的分区
Git有工作区、暂存区、本地仓库、远程仓库四个分区,工作区--添加add-->
暂存区--提交commit-->
本地仓库--推送push-->
远程仓库--拉取pull-->
本地仓库。
工作区:项目目录下.git
以外的部分,存储着我们的源代码。
本地仓库:项目目录下.git
- 暂存区:存储着准备添加到版本控制的源代码;
- 贮存区:存储着已经添加到版本控制的源代码。
广义的本地仓库由暂存区和贮存区两部分组成,但是为了方便描述,后面我们提到本地仓库时都是指狭义的本地仓库——即贮存区。
远程仓库:GitHub || GitLab || Gitee,本地仓库的一个云端备份。
Sourcetree里能看到工作区和暂存区,看不到本地仓库和远程仓库。
Git的文件状态
- 紫色的
?
:只可能出现在工作区,代表我们新增了该文件、还未添加到版本控制 <==> 对应Xcode里的?
——untracked - 绿色的
+
:只可能出现在暂存区,代表我们新增了该文件、准备添加到版本控制 <==> 对应Xcode里的A
——added - 黄色的
...
:有可能出现在工作区或暂存区,代表我们修改了该文件的内容 <==> 对应Xcode里的M
——modified - 橙色的
!
:有可能出现在工作区或暂存区,代表该文件里有冲突需要我们解决 <==> 对应Xcode里的C
——conflicted - 红色的
-
:有可能出现在工作区或暂存区,代表我们删除了该文件 <==> 对应Xcode里的D
——deleted
二、Git的一些基本操作
1、安装Git
安装SourceTree,Git也会被自动安装。
2、配置Git全局用户名和全局邮箱
配置Git全局用户名和全局邮箱,将来我们每次提交的“作者”就是“全局用户名 <全局邮箱>”了。
3、创建本地仓库、创建远程仓库、本地仓库关联远程仓库
创建本地仓库
- 先创建一个项目
- 创建好后,项目目录下就这俩文件
- 为项目创建本地仓库
- 目标路径选择刚才的项目
- 点击创建后,就为项目创建好了本地仓库
- 我们会发现项目目录下多了一个隐藏文件夹
.git
,它就是用来做源代码版本控制的本地仓库,如果我们想让项目脱离版本控制,只需要把.git
删掉就可以了
- 添加
.gitignore
文件,在项目目录下创建一个.gitignore
文件,然后去gitignore.io这个网站搜索一个现成的.gitignore
文件,把内容复制到我们创建的.gitignore
文件里即可
- 添加
README.md
文件,在项目目录下创建一个README.md
文件,然后写一些关于该项目如何使用的指导即可
- 此时把所有的文件都提交,做一次
init commit
创建远程仓库
- 远程仓库可以是GitHub || GitLab || Gitee,这里我们选择Gitee做演示。注意创建远程仓库的时候,创建一个完全空的仓库,不要勾选创建
.gitignore
文件和README.md
文件,否则本地仓库关联远程仓库的时候容易出问题
- 点击创建后,就创建好了远程仓库
本地仓库关联远程仓库
本地仓库关联远程仓库的方式有两种:HTTPS或SSH,它们俩的主要区别就是HTTPS直接用就行,但是后面每次拉取代码或推送代码都得输入GitHub || GitLab || Gitee的用户名和密码(存储到钥匙串好像就不用输入了),而SSH则需要先配置公钥和私钥后才能使用,以后随便拉取代码或推送代码,这里我们采用SSH的方式来演示。
- 创建和配置SSH公钥和私钥
终端输入ssh-keygen -t ed25519 -C "ineyee@foxmail.com"
(后面跟的是你的邮箱),然后连摁三次回车就完事了(期间不用输地址,会有一个默认的存储公钥和私钥的地址;期间不用输密码和确认密码,否则将来在推送代码和拉取代码的时候就得输密码)。
打开.ssh
文件夹,就可以看到生成的公钥和私钥。
打开.pub
公钥文件,把里面的内容全部复制下来。
打开Gitee,点击你的头像 --> 设置 --> SSH公钥,把复制下来的内容填进去,点击确定即可。
然后在终端输入ssh -T git@gitee.com
(如果是GitHub || GitLab,则输入ssh -T git@github.com
|| ssh -T git@gitlab.com
),因为首次使用需要确认并添加主机到本机SSH可信任列表,这就配置成功了。
- 把远程仓库的SSH地址复制下来
- 关联本地仓库
4、代码重置revert
工作区重置
工作区的内容,如果我们不想要了,可以采用工作区重置。
暂存区重置
暂存区的内容,如果我们不想要了,可以采用暂存区重置。
本地仓库重置
选中要重置到的某次提交,右键选择“将XXX重置到这次提交”。
- 软合并(麻烦、一般使用混合合并代替;最终效果其实和混合合并一样,但是比混合合并少做了一步“暂存区 --> 工作区”的操作)
软合并是指我们重置到这次提交后,这次提交后面的提交记录还会给我们保存在暂存区,以备我们可能还会用到这些代码,如果用不到直接重置掉即可。
- 混合合并(靠谱、常用;相当于软合并后,我们手动把暂存区的代码移动到工作区)
混合合并是指我们重置到这次提交后,这次提交后面的提交记录还会给我们保存在工作区,以备我们可能还会用到这些代码,如果用不到直接重置掉即可。
- 强行合并(危险、慎用;相当于混合合并后,我们手动把工作区的代码重置掉)
混合合并是指我们重置到这次提交后,这次提交后面的提交记录全部抹掉、不会给我们保存在暂存区或工作区,我们就丢失掉这些可能还会有用的代码了,如果你非常确定这些代码你就是用不到了,再选择强行合并。
远程仓库重置
方式一、不推荐:完成了上面的本地仓库重置,我们可以通过本地仓库重置 + 强制推送的方式达到远程仓库重置的效果,但是这种方式会抹掉提交记录,在团队开发中是不推荐的。
方式二、推荐:推荐的方式是本地仓库重置两次(强行合并一次 + 软合并一次) + 提交、推送的方式,这种方式可以保留提交记录。
先把本地仓库重置到3,注意选择强行合并,以便本地仓库丢掉4、5、6这三次提交的代码,当然你也可以选择软合并或混合合并、然后再自己重置掉保存下来的代码,这样本地仓库就可以比远程仓库少掉不想要的代码了。
然后再把本地仓库重置到远程仓库当前所在的节点6,注意需要选择软合并,这样我们刚才删除4、5、6三次提交代码的操作就会被扔到暂存区了(如果想不通,可以这样想,代码多的那个节点往代码少的那个节点重置时,选择软合并是把多出来的代码——即增加代码操作扔到暂存区了;那代码少的那个节点往代码多的那个节点重置时,选择软合并是把少出来的代码扔到暂存区了——即删除代码操作)
此时我们做一次提交、推送操作,就完成远程仓库重置了。
5、节点阅读、分支和分支小标签阅读、创建分支和合并分支阅读
节点阅读
我们每一次提交都会形成一个节点,当前所在的节点用一个空心圆圈表示,以前的节点用一个实心圆点表示。
分支和分支小标签阅读
创建分支和合并分支阅读
master分支小标签是蓝色的,它对应的节点树就是蓝色的那条;dev分支小标签是粉色的,它对应的节点树就是粉色的那条。dev分支是从master分支的“重置:到3”节点处创建出来的,提交了三次,然后就合并到了master分支(注意:Git会自动帮我们把dev分支的多次提交合并成一次“Merge brach ‘xxx’”的提交合并到master分支),合并分支完成后master分支就会落到“Merge brach ‘xxx’”的这一次提交节点上。
三、一些规范
1、gitignore规范
忽略精确的文件:
Extra.txt
;忽略所有后缀为此的:*.dll
;忽略某个文件夹下所有的文件:abc/
。
我们项目里的.gitignore
文件通常都是去gitignore.io这个网站搜索一个现成的.gitignore
文件,把内容复制到我们创建的.gitignore
文件里,不过复制完后还有几点需要注意:
-
.gitignore
文件本身不能忽略,要加入版本控制,保证每个开发者忽略的东西都一样。不过这个我们不用动,.gitignore
文件模板本来就没忽略.gitignore
文件; -
README.md
文件不能忽略,要加入版本控制,保证每个开发者读到的东西都一样。不过这个我们不用动,.gitignore
文件模板本来就没忽略README.md
文件; -
xcuserdata
文件夹得忽略,不要加入版本控制,它里面存储的是每个开发者Xcode的一些情况,如Xcode的目录展开情况、Xcode里面打的断点等,这些东西大家没必要共享。不过这个我们不用动,.gitignore
文件模板本来就忽略了xcuserdata
文件夹; -
project.pbxproj
文件不能忽略,要加入版本控制,它里面存储的是项目里源文件的描述信息,如我们新增了一个文件、移动了一个文件的目录、重命名了一个文件等,这些东西大家需要共享。不过这个我们不用动,.gitignore
文件模板本来就没忽略project.pbxproj
文件; -
project.xcworkspce
文件不能忽略,要加入版本控制,它对应于project.pbxproj
文件、存储的是工作区源文件的描述信息; - Pods文件夹得忽略,不要加入版本控制,它里面存放的是所有三方库的源码文件,我们一般不把它放到版本控制里,是为了避免不同分支依赖了不同版本的三方库、进而导致在分支合并时的冲突,而是把Podfile和Podfile.lock文件加入版本管理,团队其他人员拉取代码后得执行一下pod install项目才能跑起来。这个需要我们去
.gitignore
文件里打开/Pods
的注释,.gitignore
文件模板默认是没忽略Pods文件夹的。
2、commit规范
格式:type: content
例如:feature: 新增JS弹窗功能
、bugfix: 修复JS弹窗bug
1类型type
- feature:新增xxx功能(常用)
- bugfix:修复xxxbug(常用)
- test:测试xxx功能(常用)
- build:依赖项的修改,例如Podfile文件的修改等(常用)
- chore:不修改源代码的其余修改,例如调整文件目录顺序等(常用)
- docs:变更xxx文档
- style:变更xxx代码格式或注释
- refactor:重构xxx功能或方法
- revert:重置到xxx(常用)
2内容content
末尾不要加。
或.
标点
3、分支规范
创建分支规范和gitflow工作流
创建新项目的时候,我们会固定创建两个常驻分支:master分支和develop分支,develop分支的当前节点永远应该 >= master分支的当前节点。
1、master分支(常驻)
发布分支或者叫线上分支或者
对应线上最稳定的代码
2、develop分支(常驻)
开发分支
develop分支对应当前功能最全代码
上面两个分支在创建好后就没什么好说的了,我们平常的工作流主要是下面三个分支。
3、feature分支
功能分支
创建:当需要开发一个新功能时,从develop分支的最新节点处拉一个feature分支出来,之所以要从最新的节点处拉是为了保证该feature分支是基于最全的代码开发,如果不是从最新的节点处创建出来,那就很有可能后面的节点已经提供了某些公共的方法,你这个分支却没有还得自己写,这会增加工作量和冲突的概率;分支名为feature/功能描述,例如我们要开发一个点赞功能,分支名可以命名feature/add_like_butto
开发 + 测试:新功能都在该分支上开发,新功能开发完成后,测试需要先在该分支回归,确保该功能的代码时没问题健全的
删除:该分支测试完没问题了,如果下个版本不准备发布这个功能那就不要合到develop、还是放着这个分支就行了,等哪个版本想发布这个功能了再合到develop;如果下个版本准备发布该功能,那就开发者发起MR,管理员把该分支的代码合并回develop分支,注意此时先不要删除feature分支、feature分支要等该功能的确上线后再删除(原因见下面的疑问)
4、release分支
预发布分支或者叫预上线分支
创建:当我们开发完一个feature合并到develop后,测试可能就会问我们要一个最终包来进行最后一波集中测试、修复bug和准备发布,此时我们就需要在develop分支的最新节点(保证有自己这个feature分支代码的同时也保证有别的团队该有的代码)处创建出来一个release预发布分支交给测试去测试,名字一般就是release/下一个要发布的版本
修复bug + 测试:最后一波bug的修复都要在该分支修复,继续测试,直到认为没bug准备发布
结束:该分支回归完成后,那此时release分支上是最全的代码,开发者发起MR,管理员把release分支的代码合并回develop分支以保证develop分支的代码为最新,并合并到master分支、在master分支上打包发布、在master分支上打tag,注意此时先不要删除release分支、release分支要等该功能的确上线后再删除(原因见下面的疑问)
(1)此时你可能会有个疑问:既然feature分支已经单独测试过一波了,而且代码已经很健全了,为什么把它合回develop分之后,还要再开出一个release分支再测一遍呢,为什么不直接把develop合并到master上发布呢,要release分支不是多此一举吗?上图中1的那个节点处就描述的是这个现象。
当然最简单地回答就是再对你这个feature分支的功能测试一遍,以免第一波测试遗漏,这个回答也可以,但不是根本原因,因为你完全可以在feature分支上多测几遍嘛。其实最重要的原因是我们的一次发布可能不仅是你的一个feature分支,而是有好团队的几个功能一起发布,就像上图中的2一样,功能1和功能2都要在3.0.0这个版本发布,那如果没有release分支的话,就是功能1分支开发完直接合并到master,功能2分支开发完又直接合并到master,没有中间的release做集中过渡,那就可能存在这样一个问题:功能1单独测试的时候是ok的,功能2单独测试的时候也是ok的,于是大家认为那它们合并到master后也都是ok的,但其实两个功能之间可能会存在对一些公共数据的处理而互相影响,也就是说单独测试是ok的,但合在一起就不ok了,所以如果develop直接把两个feature带进到master就出bug了,所以release分支的定位其实就是发布前的最后集中测试,注意“集中测试”这个说法,所以“集中测试”就是指把多个要发布的feature再合并到一起测一波,即便每个feature分支单独> 测起来是ok的
所以release分支更多的价值体现在某一个版本要同时发布多个feature分支时,一个版本如果只发一个feature分支确实release分支存在不存在没多大区别,但流程规范嘛最好还是走走,无论是这次版本只发一个feature还是要发多个feature。
(2)你可能还会有第二个疑问:我们把release分支最后一波都测完了,结果上级决定临时不发布先开发先合并上来的feature1、feature2了,只发布后开发后合并上来的feature3,怎么办?
这就是我们在把feature分支合并到develop后不删除feature分支的原因,此时我们revert掉这三次提交,然后再单独把feature3给合并上来,因为从提交记录中间抽走feature1、feature2来revert很难搞,容易出错。
5、hotfix分支
紧急修复线上bug分支
创建:当发现线上有bug时,从master分支最新的节点处拉一个hotfix分支出来,分支名为bugfix/修复bug描述,例如我们要修复一个用户头像的UI bug,分支名可以命名为bugfix/fix_avatar_ui_bug
修复bug + 测试:bug的修复都要在该分支修复,继续测试,直到认为没bug准备发布
删除:该分支回归完成后,那此时hotfix分支上是最全的代码,开发者发起MR,管理员把hotfix分支的代码合并回develop分支以保证develop分支的代码为最新,并合并到master分支、在master分支上打包发布、在master分支上打tag,就可以删除release分支来保持分支的清晰整洁
使用Gitflow工作流的优点包括:
严格的分支管理,使团队成员清楚各个分支的作用和使用规则。
分离稳定版本和开发版本,有助于保持生产环境的稳定性。
提供了一种结构化的方式来管理功能开发、版本发布和紧急修复。
然而,Gitflow工作流也可能带来一些挑战:
如分支数量增多、合并冲突的潜在增加等。
因此,团队在选择工作流时应权衡其优点和挑战,并根据项目的特定需求进行调整。
合并分支规范
1、master分支保护和合并请求(PR/MR,Pull Request/Merge Request)
为了减少事故的发生,我们一般会做master分支保护,不允许开发者直接往master分支上推送代码。
而是让开发者在需要合并其它分支到master分支时提交一个合并请求,项目管理者收到合并请求后把经过code review的代码合并到master分支上。
建议开发者在提交合并请求之前,先把master分支的代码合并到feature分支/bugfix分支,看看代码是否有冲突,有冲突的话优先在feature分支/bugfix分支上解决掉,这样可以保证开发者的分支在合并到master分支时没有冲突。
2普通合并和变基合并
想保留提交记录就用普通合并,不想保留提交记录就用变基合并rebase。
普通合并:Git会自动帮我们把dev分支的多次提交合并成一次“Merge brach ‘xxx’”的提交合并到master分支,dev分支的提交记录会被记录在提交历史上。
reabase:Git不会帮我们把dev分支的多次提交合并成一次统一的提交,而是直接把dev的几次提交平移合并到master分支,dev分支的提交记录不会被记录在提交历史上,就像我们是直接在master分支上修改的一样。
4、打tag规范
每当release分支上线后,我们都会给release分支打一个和线上版本号一致的tag并推送到远程仓库,这样方便我们查看版本、追踪问题。
版本号一般由四个部分组成:MAJOR、MINOR、PATCH、BUILD。
MAJOR是指主版本号,通常在重大更新的时候才会需要更新主版本号。例如iOS每年都会更新一个主版本号;而对于第三方库来说,主版本号的更新,表示该库的API新增了重大功能,或者引入了不可兼容的更新。
MINOR是指副版本号,用于小功能的改善。例如iOS14在发布主版本后,在一年内可能发布多个副版本如14.1、14.2来完善其系统功能;而对于第三方库来说,副版本号的更新就是新增一些API,但不包含不可兼容的更新。
PATCH是指补丁版本号,一般用于bugfix以及修复安全性问题等。对于第三方库来说,补丁版本号的更新也不应该有不可兼容的更新。虽然实际操作中这会有些困难,但我们可以通过把原有 API 标记为 deprecated,或者为新API参数提供默认值等办法来解决。
BUILD是指构建版本号,通常在内部测试时使用。一般当我们使用CI服务器进行自动构建时,构建版本号会自动更新。