前言
无论热爱技术的阅读者你是希望掌握Git的企业级应用,能够深刻理解Git操作过程及操作原理,理解工作区暂存区、版本库的含义;还是想要掌握Git的版本、分支管理,自由的进行版本回退、撤销、修改等Git操作方式与背后原理和通过分支创建、切换、合并、删除整个声明周期,灵活进行各种场景的分支管理,学习常见的分支策略;亦或是掌握Git远程仓库与本地仓库,理解分布式的版本控制系统,远程仓库和本地仓库的交互操作,理解企业中常见的分支(master/release/develop/feature/hotfix等)策略掌握多人开发模式,这篇文章都能帮助到你,希望你能从中获得启发。
一、Git的版本控制
1.1、情景引入
我们在进行求学的过程中,撰写实验报告是常有的事情,当我们进行上交第一版实验报告时,假如我们这个导师比较严格,为我们指出许多不足的地方,于是我们进行修改出第二版,还是没有通过老师的审核,继续进行更改出第三版......一直到第六版,老师说越改越糊涂,第三版是你这几版中最好的,你直接进行交第三版就行了,这时候你傻眼了,我哪记得第三版是什么样的啊,我都是直接在每一版的上一版的基础上进行修改的,此时直接寄了。。
即使是将每一版进行修改的时候进行拷贝一份,当要进行查看这一版相对于上一版的变化时,要是内容少还好,要是内容较多(比如代码)进行观察也是很麻烦的。
1.2、版本控制器
1.2.1、版本控制器的作用
为了能够更方便我们管理这些不同版本的文件,便有了版本控制器。Git是当下主流的版本控制器。
所谓的版本控制器就是可以控制电脑上所有格式文件,对于我们开发人员来说主要维护项目中的源码文件(本质上就是文本文件),对于文本文件,可以进行清晰的描述出进行增删的内容具体到某一行,但是对于二进制文件(照片、视频)只能进行粗略记录大小的变动
相信通过上面Git 版本控制的演示,已经激起了对于版本控制的求知欲,那么下面就来介绍如何进行使用Git 进行版本控制
1.2.2、版本控制器的使用
Git 的安装(Linux)
- centos:
以我的centos7.6为例:
sudo yum -y install git
检查是否安装成功
git --version
- ubuntu:
以我的ubuntu20.04为例:
sudo apt-get install git -y
检查是否安装成功
git --version
Git 的基础操作
要想使用 git 进行版本控制,首先要进行创建本地仓库,只有在本地仓库下,才能使用git 对本地仓库下的文件进行版本控制。
- 创建本地仓库
- 配置本地仓库
首先要进行设置仓库(当前)的使用者名称和邮箱
git config user.name "XXX"
git config user.email "XXXXXXXXXXXXXXXXX"
补充:删除操作
如果想要进行全部的 git 仓库进行配置执行下面的命令
git config --global user.name "XXX"
git config --global user.email "XXXXXXXXXXXXXXXXX"
进行删除时,执行上面那种配置当前仓库的删除命令是无法进行删除的,需要执行下面的命令
git config --global unset user.name
git config --global unset user.email
版本控制的底层结构
工作区、暂存区以及版本库的认识
向上面那样在 gitcode_temp 文件下进行和.git 在同一目录下进行创建了code,文件,但是目前情况下 git 并不能对 code 文件进行管理,上面我们提及,放入本地仓库中的文件是可以被管理的,这里为什么又不能被管理了呢?其实这里是概念混淆了,我们gitcode_temp 这个目录并不是本地仓库,这个 .git 才是真正的本地仓库,通常称为版本库。
那直接将进入 .git 下 进行创建目录吗?
当然不可以,.git 下结构是不能手动进行修改的,直接在git 下进行创建目录会导致 git 结构错乱,不在能够进行版本控制。
这里采用的策略是,通过add 将工作区的修改进行放入版本库中的暂存区,commit 将暂存区的内容进行提交到master 分支,只有经历过这一步才能称为将文件进行放入到git中进行管理。
版本控制是如何做到的??
通过 git add 命令,将工作区(Working Directory)中的修改保存到暂存区(Stage / Index);然后通过 git commit,将暂存区中的快照生成一个新的提交对象(commit),并更新当前分支(如 master)指向该提交。
当工作区发生修改会在对象区进行新增一个对象,而暂存区中的分支都是指向对象区的,master 当中的分支结构也是指向对应的对象,这也是 .git 非常轻量的原因。
容易混淆的点
暂存区(Stage/Index),它是一个二进制文件(.git/index),确实存储在版本库(.git 目录中。
但在 Git 的概念模型中,暂存区通常被视作一个“中间区域”,不是版本库真正的提交历史部分。
如何进行理解不是真正的提交历史部分?
版本库(提交历史)就像是一本日记,里面每一页都记录了历史上的一次改动(一个提交)。暂存区就像是“准备写到日记里的内容的草稿纸”。
添加文件
- 先将工作区的文件进行添加到版本库的暂存区
- 指定文件去新增版本库的暂存区
git add .
- 将工作区中的所有文件全部新增到版本库的暂存区
git add + 指定文件名
- 指定文件去新增版本库的暂存区
- 在将暂存区中的文件进行添加到分支结构中
git commit -m "这里进行输入提交的日志信息"
通过
git log
进行打印 提交的信息
其中进行标注的这一列是通过哈希映射出来的提交ID
查看git文件
找到master分支中的提交管理的id后直接进行打印即可。
修改文件
git 进行管理的修改,并不是文件。
这是没有将工作区中的内容进行添加到暂存区
将工作区中的数据进行放到mater 进行分支管理后,通过进行产看code的状态:工作区是干净的了。
版本回退
这是git非常核心的功能
工作区 | 暂存区 | 版本库 | 本质是回退版本库的内容 |
Invalid content: hello git hello world (其中第一条是第一次进行提交的,其他都是第二次进行提交的) | Invalid content: hello git hello world | Invalid content: hello git hello world | git reset + 选项 + commit ID (使HEAD进行指向此commit ID) |
Invalid content: hello git | Invalid content: hello git | Invalid content: | -soft (选项选择) |
Invalid content: hello git | Invalid content: | Invalid content: | -mixed (默认选项) |
Invalid content: | Invalid content: | Invalid content: | -hard |
- -soft : 只进行回退版本库中的内容,工作区和暂存区的内容并未回退
- -mixed : 进行回退版本库中和暂存区的内容,工作区并未回退
- -hard: 工作区、暂存区、版本库中的内容全部进行回退
版本回退的原理
本质就是更新HEAD的指向,是的HEAD指针之后的内容失效
撤销修改
如果我们在我们的⼯作区写了很长时间代码,越写越写不下去,觉得⾃⼰写的实在是垃圾,想恢复到上⼀个版本。
对于撤销修改又分为三种情况,只撤销工作区、只撤销工作区和暂存区、将工作区、暂存区、版本库中的都进行撤销。
工作区 | 暂存区 | 版本库 | 解决方式 |
xxx code | | ||
xxx code | xxx code | 然后就回到的第一种情况 | |
xxx code | xxx code | xxx code | 前提条件: commit 之后没有进行push 先进行将代码库进行回退一个版本,然后就变成了上面的第二种情况。 |
删除文件
将我们的修改进行提交到版本库我们是使用的 git add 和 git commit ,其实删除文件也是这样进行的,只不过增加了一个先使用rm 进行删除本地文件的操作。
-
rm + 要删除的文件名
-
git add + rm删除的文件名
-
git commit -m "日志信息"
其中可以将上述的1 、2 步骤进行简化成一个步骤
git rm + 文件名
二、Git的分支管理
2.1、理解分支
2.2、分支管理的操作
2.2.1、创建、切换分支、合并分支、删除分支
- 进行产看当前的本地仓库的分支结构
git branch
- 创建分支
git branch + 分支名称
通过tree命令进行查看.git 发现确实新增了一个分支。 并且进行新建分支的时候,都是指向最新的提交,通过下面进行输出master 和 dev 指向的commit ID 都是一致的即可证明
- 切换分支
git checkout + 想要进行切换到的分支名称
- 合并分支
通过切换分支进行的提交,另一个分支是没有进行更新的,在哪个分支上进行的提交在哪个分支上生效,要想进行在别的分支上也生效,需要进行合并分支。
进行合并分支的时候需要进行切换到主分支(master)上,然后进行执行合并命令
git merge + 想要进行合并的分支
合并冲突
当在不同的分支下都对同一个文件进行修改,在进行合并分支时就会出现合并冲突,通过进行输出文件中的信息发现会出现如下图所示的情况,然后需要进行手动进行删除冲突,最后在进行删除分支即可
- 删除分支
当分支进行合并后分支就没有用了,需要进行删除分支,进行删除分支的时候需要在别的分支上进行,不能在该分支上进行删除该分支
git branch -d + 要删除的分支的名称
既然我们进行创建分支后,还需要进行删除,为什么不在master上直接进行提交呢?
因为创建、合并和删除分支非常快,所以git鼓励使用分支进行完成某个任务,合并后再删掉分支,这和 直接在master分支工作效果是⼀样的,但过程更安全。
2.2.2、合并的模式
fast-forword模式
什么是fast-forword 模式??
如上图所示的,当通过分支进行提交然后从主分支上进行将其他分支进行合并,通过 git log --graph --abbrev-commit 来进行打印提交的分支结构,可以看出通过分支进行提交后,然后将分支进行合并无法进行辨别是否由分支进行提交,这样就会出现一个重要的问题,当进行在公司进行协同开发时,当有一段代码出现了问题,无法进行定位是那个具体的开发人员也就无法进行追责。
注:命令的含义
git log --graph --abbrev-commit
-
git log
:查看提交历史记录 -
--graph
:用 ASCII 字符画出提交历史的分支结构(合并、分支关系) -
--abbrev-commit
:只显示提交 ID 的前几位(简短版),不显示完整的 40 位哈希
禁用fast-forward模式
不使用fast-forward 进行提交,通过上图可以明显看到分支进行提交的记录信息。
如何使用禁用fast-forward 进行提交??
git merge --no-ff -m "merge with no-ff" + 要进行合并的分支
2.3、分支管理要遵循的原则
2.3.1、在dev分支上进行开发,测试稳定后的版本在进行合并到master 分支
在实际开发中,我们应该按照几个基本原则进行分支管理: 首先,master分支应该是非常稳定的,也就是仅用来发布新版本,平时不能在上面干活;那在哪干活呢?干活都在dev分支上,也就是说,dev分支是不稳定的,到某个时候,比如1.0版本发布 时,再把dev分支合并到master上,在master分支发布1.0版本 。
你和你的小伙伴们每个⼈都在dev分支上干活,每个人都有自己的分支,时不时地往dev分支上合并就 可以了。 所以,团队合作的分支看起来就像上图这样。
2.3.2、bug分支用来单独进行bug的修复的
当我们在dev 上进行开发,开发到一半时,突然发现 master 分支上出现了BUG,需要进行解决,但是呢现在工作区的代码正在进行开发到一半,也无法进行提交,这该怎么处理呢?
首先我们需要进行明确的是,进行修复BUG我们是需要通过单独创建一个用来修复BUG分支来进行的,
然后将dev工作区中的内容进行储藏,通过下面命令
git stash
当想要将储藏的内容进行放回到原来位置时,通过下面命令
git stash pop
通过bug进行修复的主要步骤以为思想
Master 分支目前最新的提交,是要领先于新建 dev2 时基于的 master 分⽀的提交的,所以我们 在 dev2 中当然看不见修复bug的相关代码。
我们的最终⽬的是要让 master 合并 dev2 分⽀的,那么正常情况下我们切回 master 分⽀直接合 并即可,但这样其实是有⼀定风险的。 是因为在合并分⽀时可能会有冲突,⽽代码冲突需要我们⼿动解决(在 master 上解决)。我们⽆法 保证对于冲突问题可以正确地⼀次性解决掉,因为在实际的项目中,代码冲突不只⼀两行那么简单, 有可能几十上百行,甚至更多,解决的过程中难免手误出错,导致错误的代码被合并到 master 上。 此时的状态为:
解决这个问题的⼀个好的建议就是:最好在⾃⼰的分⽀上合并下 master ,再让 master 去合并 dev ,这样做的目的是有冲突可以在本地分⽀解决并进⾏测试,⽽不影响 master 。此时的状态 为:
最后删除bug 分支和dev分支即可。
2.4、强制进行删除分支
我们之前进行通过git branch -d 命令进行删除的分支都是向mater 进行合并之后的,要是出现了上面这种情况,想要进行删除时,git是会保护这个分支的,因为这个分支没有参与合并,要想进行删除,只能采用强制删除的方式。
git branch -D + 想要删除的分支名称
三、Git的远程操作
3.1、分布式版本控制的认识
前面我们进行学习了通过进行创建本地分支进行协同开发,但是这个分支都是本地的,也就是说在同一台服务器上进行,这样的话一人进行开发一会不但不会进行提高效率,甚至还会降低开发效率。
基于上面分析的弊端,我们将A程序员的本地仓库进行拷贝一份,然后A,B两个程序员就可以同时进行开发,开发某一部分完成时相互向对方进行推送,使得双方的仓库保持同步。
但是这种方式也是存在弊端的,当想要与对方进行同步时,对方关机的状态就直接寄了。
最终
于是就有了代码托管平台githup 和 gitee , 这两个平台的服务器是永远在线的,进行提交直接在平台上进行提交,其他人想要进行同步代码时,直接进行push拉取即可。 这种方式也成为分布式版本控制。
3.2、创建远程仓库
以码云(gitee)为例进行创建远程仓库进行演示
在进行创建仓库时建议按照上述的方式进行创建,关于勾选的选项,后面都会进行介绍
选项 Issues的作用
有问题的人和创建仓库的人进行交流的地方,当我们的这个仓库是开源的,当访问我们这个仓库的人发现一些BUG或者有一些更好的思维可以通过Issues 进行向指定仓库成员进行反应。
选项 Pull Requests 的作用
当我们进行合并分支时,并不是直接向我们之前的那种方式进行合并的,而是需要进行提交合并的申请,该仓库的相关人员进行审查后才将分支进行合并到master中,防止在master中出现BUG。
3.3、克隆远程仓库
通过HTTP协议进行克隆远程仓库
1、首先先将git中HTTPS协议的连接进行复制
2、然后在我们本地仓库之外的文件目录下进行执行一下命令
git clone + 刚刚进行复制的协议链接
进行执行完上述两个命令,克隆仓库就完成了,远程仓库和本地仓库就建立了关系,可以通过推拉进行维持仓库的关系
通过SSH协议进行克隆远程仓库
ssh 使用的是公钥加密,将本地服务器的公钥进行放到git服务器上进行管理,如下图中的步骤
第⼀步:
创建SSHKey。在用户主目录下,看看有没有.ssh目录,如果有,再看看这个目录下有没有 id_rsa 和 id_rsa.pub 这两个⽂件,如果已经有了,可直接跳到下⼀步。如果没有,需要创建 SSH——Key:
ssh-keygen -t rsa -C "自己的邮箱"
第二步:
先找到创建的密钥的目录,然后将密钥进行复制到git中进行添加。
第三步:
git clone + ssh的链接即可
3.4、向远程仓库进行推送
首先进行说明push 的条件:
本地仓库的开发是领先于远程仓库的。
本地分支和远程分支的交互
git push origin master:master
3.5、拉取远程仓库
首先进行说明pull 的条件:
远程仓库的开发是领先于本地仓库的。
当多人进行协同开发时,别人在远程仓库进行push了,这时候远程仓库中的内容版本是领先于这个用户的本地仓库的。通过下面的命令可以进行将版本进行推进。
git pull origin master:master
3.6、忽略特殊文件
在⽇常开发中,我们有些⽂件不想或者不应该提交到远端,⽐如保存了数据库密码的配置⽂件,那怎 么让Git知道呢?在Git⼯作区的根⽬录下创建⼀个特殊的 .gitignore ⽂件,然后把要忽略的⽂件 名填进去,Git就会⾃动忽略这些⽂件了。 不需要从头写 .gitignore ⽂件,gitee在创建仓库时就可以为我们⽣成,不过需要我们主动勾选⼀ 下:
如果已经进行创建了本地仓库, 通过增加配置文件来规避
另一种方式就是通过更改.gitignore 文件
# 排除所有.开头的隐藏⽂件:
.*
# 不排除.gitignore
!.gitignore
git add -f +想要进行添加的文件名,将忽略的文件进行添加到远程仓库
进行检查某个文件为什么被忽略了
git check-ignore
3.7、配置命令别名
当一个命令太长时,进行执行该命令太麻烦,比如
git log --pretty=oneline --abbrev-commit
我们可以进行使用以下命令进行将这个命令进行起别名,下次进行使用时直接使用别名即可。
git config --global alias.lpa 'log --pretty=oneline --abbrev-commit'
以后直接使用git lpa 即可。