1.Git的定位
在我们自己开发项目的过程中,经常会遇到这样的情况,为了防止代码丢失,或者新变更的代码影响到原有的代码功能,为了在失误后能恢复到原来的版本,不得不复制出一个副本,比如:“坦克大战1.0”“坦克大战2.0”“坦克大战-确定版”“坦克大战-验收版”…
虽然每个版本我们都进行了备份,但是每个版本都有自己的内容,随着版本的不断增多,各自版本修改的内容是什么我们就不清楚了。
因此,就需要有一个版本控制器,来记录每次的修改以及版本的迭代信息,比如“坦克大战2.0”在“坦克大战1.0”上变更了Move类中第3行的代码,那么git的作用就会记录此次的变更具体信息,以此来方便我们回滚或者继续开发。
Git可以控制电脑上所有格式的文档,不仅是文本文件,源代码,还可以是二进制文件,比如图片、视频(这类文件无法记录具体的变更,只能记录空间占用的变化,比如100kb–>200kb)
2.Git的安装(Linux-centos)
如果你的平台是centos,安装git如下操作,以我的centos7.6为例:
首先,你可以尝试输入git --version
,如果提示类似于git version 1.8.3.1
,说明你已经安装好了,如果提示-bash: git: command not found
,则执行以下命令进行安装sudo yum install git -y
,完成安装后,可以再通过命令git --version
来检验自己是否安装完成。
3.Git基本操作
3.1创建git本地仓库
只有把文件放在git仓库中,我们才能对文件的版本进行控制,所以我们首先需要创建一个仓库出来。
先通过mkdir xxx
命令创建一个目录,切换到对应目录后,输入命令git init
,来创建一个git本地仓库,可以通过tree .git/
命令来查看Git仓库的具体细节。
注意,不允许在.git
下手动修改
3.2配置git
当安装git以后,首先要做的就是配置你的用户名和Email地址,命令如下:
git config [--global] user.name "Your Name"
git config [--global] user.email "email@example.com"
# 把 Your Name 改成你的昵称
# 把 email@example.com 改成邮箱的格式,只要格式正确即可。
其中--global
是一个可选项,因为一台机器上可以创建多个Git仓库,通过该选项,就是给所有仓库都进行了配置(全局配置)。如果系统在不同仓库中使用不同的name
或e-mail
,可以不要--global
选项,但要注意,执行命令时必须要在想要操作的仓库中。
查看配置的命令为:
git config -l
删除对应配置的命令为:
git config [--global] --unset user.name
git config [--global] --unset user.email
3.3认识工作区、暂存区、版本库
我们前边说了,只有把文件放在git仓库中,才能对文件版本进行控制,那么我们在git仓库中通过touch ReadMe
,来创建一个ReadMe
文件以后,可以对ReadMe
文件进行操作吗?
答案是不行,通过以上命令,列举出来的,就划分为两个区域:
.git
在版本库(仓库中)
ReadMe
在工作区中
只有将工作区的内容,通过add
、commit
放入版本库中以后,才能进行文件版本控制。
下图展示了工作区、暂存区和版本库之间的关系:
第一步操作add
,就是将工作区中文件的修改内容,添加进版本库中的暂存区中;
修改内容:包含增加、修改、删除
第二步操作commit
,就是把暂存区中的内容,提交到版本库中的master分支下;
走完这两步操作,我们才可以真正意义上来管理ReadMe
文件了
版本库中还存在一个对象区——objects
,工作区每次的修改内容都会写入到一个git
对象中,每次add
,都会把这个git
对象来交给对象区来维护,那么也就相当于,对象区维护了文件所有的版本。
暂存区存储一个个git
对象的索引,master
分支下,存储的也是一个个git
对象的索引,不存储具体的对象。
如果文件出现变更,最下边应该还有分支index
。
3.4提交文件修改
我们在.git
目录下创建了ReadMe
文件,如果对ReadMe
文件进行修改,需要通过add
、commit
的方式来进行记录,以下是具体的操作过程。
首先通过vim
命令在ReadMe
文件中添加一行内容hello git
,如下展示,现在我们想要把ReadMe
文件的变更记录进行保存,就需要下一步的add
操作。
git add [文件名1] [文件名2]
:添加一个或者多个文件到暂存区git add .
:添加当前目录下的所有文件到暂存区
完成add
以后,我们紧接着就需要commit
操作,来真正意义上把文件变更内容保存到本地仓库中。
git commit -m "此次变更的具体描述细节"
:提交暂存区的全部内容到本地仓库git commit [文件名1] [文件名2] -m "此此变更的具体描述细节"
:提交暂存区的指定内容保存到本地仓库中
注意:
git commit
后边的-m
选项,要跟上本次提交的message
,由用户自己完成,这部分内容不可省略,并要好好描述,用于记录此次变更的细节,是给我们自己看的。
整体操作流程如下图所示:
我们可以通过git log
命令来查看我们的提交记录,该命令可以帮我们打印出来时间由近到远的所有记录,下图中都标红部分,就是该次commit的commit ID
,由这个唯一的ID,就可以定位到这次提交。
如果觉得输出的信息太多,可以加上 --pretty=oneline
参数
3.5查看.git文件
在我们完成上边的提交操作以后,再通过tree .git
命令来查看文件目录,如下图所示,多了index目录:
我们可以通过cat
命令来查看不同的目录下的文件.
首先我们来看HEAD
指针,通过cat .git/HEAD
命令,面板上打印出ref: refs/heads/master
(说明HEAD指针指向master分支),再通过命令cat .git/refs/heads/master
命令(该命令打印出最新一次提交的commitID),面板上打印出一行commitID
,再通过命令git cat-file -p commitID
,面板打印如下图结果,打印出了这次commitID
提交对应的提交信息。
我们可以发现,打印结果中的tree
后也跟着一个commitID
,打印这个commitID
,得到提交文件的commitID
,再打印这个commitID
,就可以得到文件里具体修改的内容。
总结:
HEAD指针指向.git/refs/heads/master
(master分支),打印该分支,可以得到最新一次提交的commitID
,打印这个commitID
可以得到该次commit的描述详情,打印结果的tree
后还跟着一个commitID
,打印这个ID可以得到具体提交的文件的commitID
,再打印这个ID,就可以得到文件具体修改的内容。
3.6场景学习
如上图所示,提交后打印1 file changed, 0 insertions(+), 0 deletions(-)
,意思是只有一个文件改变了,这是因为我们只将file1
提交到暂存区了,commit
不会把暂存区以外的东西提交,想要提交file2
,就需要再重新add
、commit
。
3.7修改文件
Git跟踪并管理的是修改,而非文件。
新增、删除或更改都属于修改,甚至创建一个新文件,也算一个修改。
让我们通过vim
命令将ReadMe文件进行一次修改,里边增加一行代码hello world
。
接着可以通过git status
来查看当前仓库的状态:
上面的结果告诉我们,ReadMe文件被修改过了,但还没有完成添加与提交。
目前,我们只知道文件被修改了,如果想要知道具体哪些地方被修改了,可以通过git diff
命令
git diff [file]
命令用来显示暂存区和工作区文件的差异,显示的格式是Unix通用的diff格式。
我们add
文件以后,再通过status
命令来查看状态,显示有待提交。
commit
文件并带上描述,再通过status
命令来查看状态:
3.8版本回退
Git能够管理文件的历史版本,如果有一天你发现之前的工作做的出现了很大的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本回退了。
执行git reset
命令用于回退版本,可以指定退回某一次提交的版本,回退的本质是将版本库中的内容进行回退,工作区或暂存区是否回退由命令参数决定:
git reset
命令语法格式为:git reset [--soft | --mixed | --hard [HEAD]]
--soft
参数对于工作区和暂存区的内容都不变,只是将版本库回退到某个指定版本。--mixed
为默认选项(使用时可以不带该参数),该参数将暂存区的内容退回为指定提交版本内容,工作区文件保持不变。--hard
参数将暂存区和工作区都退回到指定版本。该参数需要慎用,因为如果工作区有未提交的代码时,使用该命令工作区会回滚,你没有提交的代码就再也找不回了。
HEAD
说明:
- 可直接写成commit ID,表示指定退回的版本
- HEAD 表示当前版本
- HEAD^ 上一个版本
- HEAD^^ 上上一个版本
- 以此类推
可以使用~数字
表示:
- HEAD~0 表示当前版本
- HEAD~1 上一个版本
- HEAD~2 上上一个版本
以下是实际操作场景:
通过git reset --hard commit ID
回退到指定版本,ReadMe文件里边的内容也发生了回滚。
如果想要再返回原来的状态,就再找到指定的commit ID
进行回滚即可。
只要commit ID
我们能找到,我们就可以回退到指定的版本,可以通过git reflog
命令来查看所有的commit ID
记录(此处我们发现gommit ID很短,但是通过这个短的ID也能够进行正常回退)
我们进行版本回退的速度很快,这是因为我们的每个版本都有一个commit ID
,git只需要更改指针的指向(HEAD指针指向master,master存储最新一次commit ID)就可以进行版本的回退了。
总结:
3.9撤销修改
如果我们在工作区写了很长时间代码,但是后来写的代码质量都很差,想要撤销掉我们的修改,恢复到上一个版本,分以下几种情况来讨论。
- 情况一:工作区的代码没有add
- 情况二:已经add,但没有commit
- 情况三:已经add,并且也commit了
我们还以ReadMe
文件作为举例,我们在工作区,给ReadMe
文件新增了一行代码xxx code
。
对应情况一,我们只需要命令git checkout --[filename]
即可撤销
对应情况二,我们可以先通过命令git reset HEAD ReadMe
(默认参数为--mixed
),将暂存区的内容回退到当前版本区的内容(都为空),然后再通过git checkout --[filename]
命令来撤销工作区修改的内容。
对于情况三,我们可以通过命令git reset --hard HEAD^
将三个区域都回退到上一版本的内容【注意:该情况为commit之后,push之前】
总结:
3.10删除文件
在git中,如果我们想要做一个删除操作,只通过在工作区的rm
命令是无法完成删除的,还需要add
和commit
操作才能完成暂存区和工作区的删除
所以我们一般可以直接通过命令git rm
可以完成工作区和暂存区的删除,然后再通过git commit -m" "
,来完成版本库中的删除。
4.Git分支管理
4.1理解分支
分支就是一个复制版本的概念,它与主版本互不干扰,可以在分支上进行操作,防止在主干上直接操作造成失误;在分支上完成开发操作以后,可以再合并分支到主干上。
在版本回退中,每次提交,Git都会把它们串成一条时间线,这条时间线就可以理解为一个分支,如果只有这一条时间线,那么这个分支就叫做主分支,即master分支。
HEAD指针不仅可以指向master,还可以指向其他分支,被指向的分支就是当前正在工作的分支。
4.2创建、切换、合并分支
Git支持我们查看或创建其他分支,在这里我们创建第一个自己的分支dev
,对应的命令为:
*
表示当前HEAD
指向的分支是master
分支,并且目前dev和master指向同一个修改。
如果我们想要在dev
分支下进行开发工作,就需要将分支切换到dev
上来,可以通过git checkout dev
命令来完成切换,如下演示:
接下来我们在dev
分支下修改ReadMe
文件,新增一行内容,并进行一次提交操作:
在dev分支上commit以后,当前仓库的状态如下:
如果我们想要在master主分支上看到新的提交,就需要将dev
分支合并到master
分支,示例如下:
当前仓库的状态如下:
Fast-forward代表“快进模式”,也就是直接把master指向dev的当前提交,所以合并速度非常快。
命令:
git branch
:查看本地所有分支
git branch -r
:查看远程所有分支
git branch -a
:查看所有分支
4.3删除分支
合并完成以后,dev
分支对我们来说就没用了,那么dev分支就可以被删除掉,注意如果当前正处于某分支下,就不能删除当前分支,需要先切换到master
分支下,再来删除dev
分支。
git branch -d 分支名
此时的仓库状态如下所示:
因为创建、合并和删除分支非常快,所以更建议使用分支完成某个任务,合并后再删掉分支,这和直接在master分支上工作效果是一样的,但过程更安全。
如果我们在分支上开发了新功能,但是产品不要这个新功能了,这时候我们可以通过git branch -D 分支名
来强制删除该分支。
4.4合并冲突
在我们合并分支的时候,并不是想合并就能合并成功的,有时候可能会遇到代码冲突的问题。
比如我们在主分支上的ReadMe
文件内容为aaa on dev1
,我们创建并切换到新的分支上dev1
,将aaa on dev1
变更为bbb on dev1
,再换回到主分支上,并将aaa on dev1
改为ccc on dev1
,这时候想要将dev1
分支合并到master
主分支上,就会产生合并冲突。
为了演示这个问题,我们需要创建一个新的分支,并切换到该分支上,可以使用git checkout -b dev1
命令,一步完成创建分支并切换到该分支的操作。
接下来在dev1
分支上,将aaa on dev1
变更为bbb on dev1
,并add
commit
。
然后再切回到主分支上,将aaa on dev1
变更为bbb on dev1
,并add
commit
。
此时仓库中的状态就如下图所示:
如果想要将dev1
分支合并到master
分支上,就会有以下合并冲突的提示:
修改冲突内容并提交
此时合并冲突就修改了,仓库的状态如下图所示,我们也可以通过命令git log --graph --pretty=oneline --abbrev-commit
,来查看仓库的状态
4.5 分支管理策略
通常合并分支时,如果可能,Git会采用Fast forward
模式。那么形成的合并结果将如下图所示:
在这种Fast forward
模式下,删除分支以后,查看历史分支时,会丢掉分支信息,看不出来最新提交到底是merge进来的还是正常提交的。
但在合并冲突部分,我们也看到通过解决冲突问题,会再进行一次新的提交,得到的最终状态为:
那么这就不是Fast forward
模式了,这样的好处是,从分支历史上就可以看出分支信息。比如我们现在已经删除了在合并冲突部分创建的dev
分支,但依旧能看到master其实是由其他分支合并得到的。
Git支持我们强制禁用Fast forward
模式,那么就会在merge时生成一个新的commit
,这样,从分支历史上就可以看出分支信息。
下面是实战示例:
首先,创建新的分支dev
,并切换至新的分支:
修改ReadMe
文件,并提交一个新的commit
:
切回master
分支,开始合并:
--no-ff
参数,表示禁用Fast forward
模式,禁用Fast forward
模式后合并会创建一个新的commit
,所以加上-m
参数,把描述写进去。
合并后,查看分支历史:
可以看到,不使用Fast forward
模式,merge后就像下图状态:
所以在合并分支时,加上--no-ff
参数就可以用普通模式合并,合并后的历史有分支,能看出来曾今做过合并,而fast forwad
合并就看不出来曾经做过合并。
分支策略
在实际开发中,我们应该遵循几个基本原则来进行分支管理;
master分支是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;
而干活平时应该在dev分支上,每个人都在dev分支上干活,且每个人都有自己的分支,时不时往dev分支上合并就可以了,然后再把dev分支合并到master分支上;
所以团队合作的分支看起来就像下图:
4.6 bug分支
假如我们现在正在dev
分支上进行开发,开发到一半,突然发现master
分支上面有bug,需要解决。在Git中,每个bug都可以通过一个新的临时分支来修复,修复后,合并分支,然后将临时分支删除。
如果我们现在在dev
分支上开发了一部分,还无法提交,这时候需要使用git stash
命令,来将当前的工作区信息进行储藏(只存储被git追踪的文件),被储藏的内容可以在将来某个时刻恢复出来。
我们用git status
命令查看工作区,是干净的,因此可以放心地创建分支来修复bug。
储藏dev
分支的工作区以后,由于我们要基于master
分支修复bug,所以需要切回master
分支,再新建临时分支来修复bug
,示例如下:
修复完成后,切换到master
分支,并完成合并,最后删除fix_bug
分支。
这时,我们已经完成了bug的修复,要继续回到dev
分支上进行开发,切回dev
分支,使用git stash list
可以看到我们存储的工作现场版本,使用git stash pop
命令恢复现场:
然后我们可以继续开发,开发完成后提交:
此时git仓库的状态如下所示:
master
分支目前最新的提交,是要领先于dev
基于master
分支时提交的,我们最终想要让master
分支合并dev
分支,但如果切回master
分支直接合并,有可能存在dev
和fix_bug
分支上的冲突,而代码冲突需要我们手动解决(在master
上解决),直接在master
分支上变更,是有一定风险的,这时更好的做法时,切回dev
分支,将master
分支合并到dev
分支上,并在dev
分支上解决代码冲突,解决冲突以后,再将dev
分支合并到master
分支上,此时的状态为:
实操如下:
5.远程操作
5.1 分布式版本控制系统
我们之前所介绍到的所有内容(工作区、暂存区、版本库等等),都是在本地,也就是在你的笔记本或者计算机上。而我们的Git其实是分布式版本控制系统。
可以简单理解为,我们每个人的电脑上都是一个完整的版本库,大家可以在多台电脑上进行修改,然后可以把自己的修改推送到中央服务器上,其他同学可以通过拉取中央服务器上的代码来获取到最新的修改;中央服务器的作用就是方便“交换”大家的修改。
5.2 远程仓库
Git是分布式版本控制系统,同一个Git仓库,可以分布到不同的机器上,有一台电脑充当服务器的角色,每天24h开机,其他每个人都从这个“服务器”仓库克隆一份到自己的电脑上,并且各自把各自的提交推送到服务器仓库里,也从服务器仓库中拉取别人的提交。
网站GitHub或者gitee都是提供Git仓库托管服务的,下面我们来使用一下gitee(码云)远程仓库。
新建远程仓库
创建成功:
并且在刚创建好的仓库中,有且只有一个默认分支master
克隆远程仓库
克隆/下载远端仓库到本地,需要使用git clone
命令,后面跟上我们的远端仓库链接:
SSH协议和HTTPS协议是Git最常使用的两种传输协议。SSH协议使用了公钥加密和公钥登录机制,体现了其实用性和安全性,使用此协议需要将我们的公钥放上服务器,由Git服务器进行管理。使用HTTPS方式时,没有要求,可以直接克隆下来。
- 使用HTTPS方式:
- 使用SSH方式:
SSH方式直接使用git clone
命令是不能够将远程仓库克隆到本地的,需要我们将公钥添加到远端仓库中,才能够将仓库克隆下来。
第一步:创建SSH Key;在用户主目录下,看看有没有.ssh
目录,如果有,再看看这个目录下有没有id_rsa
和id_rsa.pub
这两个文件,如果已经有了,可以直接跳到下一步。如果没有,需要创建SSH Key:
注意此处要输入自己的邮箱,然后一路回车,使用默认值即可。
第二步:添加自己的公钥到远端仓库
再次打开ssh目录,查看公钥id_rsa.pub
,将公钥复制,粘贴到gitee上。
然后再输入git clone
命令就可以完成克隆操作了。
完成克隆以后,仓库的状态如下图所示:
向远程仓库推送
在向远程仓库推送时,需要注意设置的name和Email需要和gitee上配置的用户名和邮箱保持一致,否则会报错。
配置好以后,我们就可以向远程仓库推送了
推送的命令格式如下:
git push <远程主机名> <本地分支名> :<远程分支名># 如果本地分支名与远程分支名相同,则可以省略冒号后的内容
git push <远程主机名> <本地分支名>
具体的推送操作如下:
整个推送的流程如下图所示:
拉取远程仓库
如果其他同学向远端仓库push内容,那么远端仓库的代码是新于你本地的代码的,所以就需要通过命令来拉取远程仓库上最新的代码,代码格式如下:
git pull <远程主机名> <远程分支名>:<本地分支名># 如果远程分支是与当前分支合并,则冒号后面的部分可以省略
git pull <远程主机名> <远程分支名>
该命令会完成拉取并合并分支两步操作。
命令git pull
,后面不加其他内容,可以完成两种类型的拉取,一种是拉取对应分支下的内容【这种需要通过本地分支与远程分支建立连接】,另一种是拉取仓库的内容【即拉取远程最新的分支情况】
通过以下两种方式可以将本地分支与远程分支建立连接:
git checkout -b [branch-name] [origin/branch-name]
【该方法在新建分支时建立连接】
git branch --set-upstream-to=origin/branch-name branch-name
【该方法在建立好分支以后建立连接】
5.3 配置git
忽略特殊文件
在日常开发中,我们有些文件不想或者不应该提交到远端(比如说保存了数据库密码的配置文件),就需要在Git工作区的根目录下创建一个特殊的.gitignore
文件,然后把要忽略的文件名填进去,Git就会自动忽略这些文件了。
.gitignore
文件可以在gitee创建仓库时默认勾选生成,也可以由我们自己来创建,效果相同;
假如我们想要忽略以.so
和.ini
结尾的所有文件,.gitignore
的内容如下所示:
# 忽略以.so和.ini结尾的所有文件*.so
*.ini
如果某个以.so
或者.ini
结尾的文件又想要提交,可以在.gitignore
文件中添加文件名,比如想要提交a.so
,那么.gitignore
的内容如下所示:
# 忽略以.so和.ini结尾的所有文件*.so
*.ini!a.so # 排除a.so文件
如果.gitignore
文件里维护的代码太多了,而此时我们又新建了个文件(比如文件d.so
),没有被git追踪管理,我们可以通过命令git check-ignore -v d.so
来查看被忽略的原因。
最后,配置好.gitignore
文件以后,记得把.gitignore
文件也提交到远端,就完成了。
给命令配置别名
在我们使用Git期间,可以通过配置,来将命令简化。
比如我们想要将git status
命令简化为git st
,对应的命令为(--global
是全局参数):
git config --global alias.st status
将git log --pretty=oneline --abbrev-commit
命令简化为git lpa
,对应的命令为:
git config --global alias.lpa 'log --pretty=oneline --abbrev-commit'
使用效果如下所示:
6.标签管理
标签tag
,可以简单的理解为是对某次commit
的一个标识,相当于起了一个别名。例如,在项目发布某个版本的时候,针对最后一次commit
起一个v1.0
这样的标签来标识里程碑的意义,相较于难以记住的commit ID
,tag
可以很好的解决这个问题,因为其便于记忆且有意义,所以当我们需要回退到某个重要版本是,直接使用标签就能很快定位到。
6.1 操作标签
创建标签:
git tag v1.0
给最新的一次commit
打标
git tag -a[name] -m "xxx" [commit_id]
Git提供的带有说明的标签,用-a指定标签名,-m指定说明文字
如果想要给指定的commit
打标签,找到历史提交的commit id
,然后打标即可,如下命令
git tag v0.9 a009936
git tag
:查看所有标签(按时间排序)
git show [tagname]
:查看标签信息
git tag -d [tagname]
:删除标签
6.2 推送标签
因为创建的标签都只保存在本地,不会自动推送到远程,如果想要推送某个标签到远程,使用命令git push origin <tagname>
;
如果你本地有很多的标签,也可以通过命令git push origin --tags
,将所有标签一次性的全部推送到远端。
如果标签已经推送到远程,要删除远程标签,就需要两步操作,第一步先从本地删除,第二步再从远程删除。
git tag -d [tagname] #删除本地标签
git push origin :[tagname] #删除远程标签
7.多人协作
7.1 场景一:多人同一分支
现在我们有这样一个场景:
目标:在远程的
master
分支下的file.txt
文件,新增代码aaa
、bbb
实现:由开发者1新增
aaa
,由开发者2新增bbb
条件:在一个分支下协作完成
为了模拟实现以上场景,我们分别在linux和Windows上针对同项目进行协作开发。
目前,我们的远程仓库中只有一个master
主分支,但在实际的项目开发中,在任何情况下其实都是不允许直接在master
分支上修改代码的,这是为了保证主分支的稳定。所以在开发新功能是,常常会新建其他分支,供开发时进行迭代使用。
那么首先我们在gitee上新建dev
远程分支供我们使用:
然后分别在linux和windows上更新最新的分支及代码情况
将仓库克隆到本地以后,分支情况如下图,因为我们需要在本地分支上做修改,所以需要在本地新建分支dev
,并与远程分支dev
建立连接,通过命令git checkout -b dev origin/dev
来实现。
还可以通过命令
git branch --set-upstream-to=origin/<远程分支名> <本地分支名>
,来将本地分支与远程分支建立连接。
我们还可以通过git branch -vv
命令来查看分支的连接情况,通过将本地分支与远程分支建立连接,在使用git push
或者git pull
命令时,就不用在指定分支了,直接就是两个连接分支之间的推送和拉取操作。
接下来我们在linux系统下,完成新增aaa
的操作,并将新增操作推送到远程的仓库中。
然后再windows系统下,完成新增bbb
的操作
并将新增bbb
的操作变更,也推送到远程仓库中
但如果当我们输入git push
命令推送时,由于远程仓库中file.txt
文件新增了aaa
代码,windows中的仓库并没有感知,所以如果直接推送会产生代码冲突,需要先通过git pull
命令将远程仓库中的代码拉到本地,解决冲突以后再进行推送
完成远程分支的推送后,我们还需要将远程分支dev
合并进远程分支master
,才算完成了整个链路。
一般在远程仓库中进行合并操作时,需要在远程仓库中提交Pull Requests
合并请求工单,将远程的dev
分支合并到master
分支上,工单会被审核员或者管理员审批,审批通过才能完成合并,这也是在公司经常用的合并提交代码的方式。
总结一下,在同一分支下进行多人协作的工作模式通常是这样的:
- 首先,可以试图用
git push
推送自己的修改; - 如果推送失败,则因为远程分支比你的本地更新,需要先用
git pull
试图合并; - 如果合并有冲突,则解决冲突,并在本地提交;
- 没有冲突或者解决掉冲突以后,再用
git push
推送就能成功; - 功能开发完毕,将远程分支
merge
进master
,然后将临时分支删除
7.2 场景二:多人不同分支
现有场景2:
目标:远程
master
分支下新增function1
和function2
文件
实现:由开发者1新增function1
,由开发者2新增function2
条件:在不同分支下协作完成,各自让某一个功能私有某一个分支
基于以上场景的分支创建,我们有两种方式来实现:
1、在远程仓库基于master
创建分支,然后本地拉取该分支进行功能开发
这种方式比较建议,因为基于远程的
master
分支创建新的分支,得到的是最新的代码
2、基于本地分支创建新分支,完成代码开发后进行git push
操作
这种方式需要注意的是,本地的
master
分支可能不是最新的代码,需要先通过git pull
操作,拉取到最新的master
分支,然后再基于该分支创建新的分支进行开发。
下边我们用第二种方式来做演示:
首先在linux系统下的仓库,完成开发者1的工作:
然后在windows系统下,完成开发者2的工作:
至此我们发现,远程仓库的分支情况符合我们的预期:(新建了两个分支,并新增了对应的功能)
接下来就可以通过提交PR,来完成分支合并的请求了。
如果将临时分支,合并到master
分支上时,产生了冲突,就需要先将master
分支合并到临时分支上,在临时分支上解决了冲突,再将临时分支合并到master
分支上,该场景就如下图所示【先完成了分支feature-2
的合并,然后在合并feature-1
分支时,产生了冲突】:
本地解决完冲突并push
以后,可以接着提交PR来完成合并分支的请求及操作了。
7.3 处理远程已经删除的分支
如果我们已经在远程仓库中删除了某些分支,但是我们通过git branch -a
命令还是会看到这些被删除的分支
如果想处理掉这些在远程已经删除的分支,可以通过命令git remote show origin
来查看这些分支的详情,并会提示你用git remote prune origin
来删除这些在远程仓库中已经被删除的分支。
8.企业开发运用
当今企业,经常使用的软件迭代开发、维护的工具,是DevOps
,在Devops
的软件开发过程包含计划、编码、构建、测试、预发布、发布、运维、监控,由此可见DevOps
的强大。
8.1系统开发环境
在系统开发中,有以下几个环境需要我们必须了解:
1、开发环境:开发环境是开发者们专门用于日常开发的服务器。
2、测试环境:一个程序在测试环境中是不稳定的,该环境是开发环境到生产环境的过渡环境。
3、预发布环境:该环境是为避免因测试环境和线上环境的差异带来的缺陷漏测而设立的一套环境。其配置等基本和生产环境一致,目的是能让我们发正式环境时更有把握!所以预发布环境是产品质量的最后一道防线,因为下一步项目就要上线了,要注意预发布环境服务器不在线上集成服务器范围之内,为单独的一些机器。
4、生产环境:是指正式提供对外服务的线上环境,例如我们目前在移动端或PC端能访问到的APP都是生产环境。
这几个环境也可以说是系统开发的三个重要阶段:开发–》测试–》上线。
8.2 Git分支管理规范
对于开发人员来说,一般会针对不同的环境来设计不同的分支,例如:
分支 | 名称 | 适用环境 |
---|---|---|
master | 主分支 | 生产环境 |
release | 预发布分支 | 预发布/测试环境 |
develop | 开发分支 | 开发环境 |
feature | 需求开发分支 | 本地 |
hotfix | 紧急修复分支 | 本地 |
master分支
master
为主分支,该分支为只读且唯一分支。用于部署到正式发布环境,一般由合并release
分支得到。- 主分支作为稳定的唯一代码库,任何情况下不允许直接在
master
分支上修改代码。 - 产品的功能全部实现以后,最终在
master
分支对外发布,另外所有在master
分支的推送应该打标签(tag)做记录,方便追溯。 master
分支不可删除
release分支
release
为预发布分支,基于本次上线所有的feature
分支合并到develop
分支以后,基于develop
分支创建。可以部署到测试或预发布集群。- 命名以
release/
开头,建议命名的规则:release/version_publishtime
release
分支主要用于提交给测试人员进行功能测试。发布提测阶段,会以release
分支代码为基准进行提测。- 如果在
release
分支测试出问题,需要回归验证develop
分支看是否存在此问题。 release
分支属于临时分支,产品上线后可选删除。
develop分支
develop
为开发分支,基于master
分支创建的只读且唯一分支,始终保持最新完成以及bug修复后的代码。可部署到开发环境对应集群。- 可根据需求大小程度确定是由
feature
分支合并,还是直接在上面开发(不建议)。
feature分支
feature
分支通常为新功能或新特性开发分支,以develop
分支为基础创建feature
分支- 命名以
feature/
开头,建议的命名规则:feature/user_createtime_feature
- 新特性或新功能开发完成后,开发人员需要合到
develop
分支。 - 一旦该需求发布上线,便将其删除。
hotfix分支
hotfix
分支为线上bug修复分支或叫补丁分支,主要用于对线上的版本进行bug修复,当线上出现紧急问题需要马上修复时,需要基于master
分支创建hotfix
分支。- 命名以
hotfix/
开头,建议的命名规则:hotfix/user_createtime_hotfix
- 当问题修复完成后,需要合并到
master
分支和develop
分支并推送远程。一旦修复上线,便将其删除。
用一张图来总结:
8.3 DevOps开发实战
【Gitee企业版】
准备工作
创建项目
创建仓库
添加成员
实战演练
现有一个订单管理的新需求需要开发,首先可以基于develop
分支创建一个feature/ldj_order_20231024
分支。
1.需求在feature/ldj_order_20231024
分支开发完毕,这时研发人员可以将代码合并到develop
分支,将其部署在开发环境的服务器中,方便开发人员进行测试和调试。
a.开发者在feature
分支下发起请求评审(PR)
b.审核员审查代码
c.审查通过,合并分支
d.合并通过,查看结果
2.在develop
下开发人员自测通过后,先确定下develop
不存在未测试完毕的需求,然后研发人员可基于develop
分支创建一个release/xxx
分支出来,可交由测试人员进行测试。
3.测试人员测试release
通过后(包含测试环境和预发布环境的测试),就可将代码合并入master
。
4.测试人员在master(正式环境)
测试通过后,便可删除feature/xxx
分支。
修复bug
修复测试环境bug
在develop
测试出现了Bug,建议大家直接在feature
分支上进行修复。修复后的提测上线流程与新需求加入的流程一致。
修改预发布环境Bug
在release
测试出现了Bug,首先要回归下develop
分支是否同样存在这个问题。如果存在,修复流程与修复测试环境Bug流程一致。
如果不存在,这种可能性较少,大部分时数据兼容问题,环境配置问题等。
修改正式环境Bug
在master
测试出现了Bug,首先要回归下release
和develop
分支是否同样存在这个问题。
如果存在,修复流程与修复测试环境Bug流程一致。
如果不存在,这种可能性也比较少,大部分是数据兼容问题,环境配置问题等。
紧急修复正式环境Bug
需求在测试环节未测试出Bug,上线运行一段时候出现了Bug,需要紧急修复的。
可基于master
创建hotfix/xxx
分支,修复完毕后发布到master
验证,验证完毕后,将master
代码合并到develop
分支,同时删除hotfix/xxx
分支。