
🔥承渊政道: 个人主页
❄️个人专栏: 《C语言基础语法知识》 《数据结构与算法》
✨逆境不吐心中苦,顺境不忘来时路! 🎬 博主简介:

引言:在软件开发的全流程中,版本控制是保障协作效率、规避开发风险的核心基石,而Git作为目前最流行、最强大的分布式版本控制系统,早已渗透到从个人开发到大型企业级项目的每一个环节.无论是多人协作时的代码冲突解决、开发过程中的版本回溯,还是跨环境的代码同步、分支管理,Git都以其高效、安全、灵活的特性,成为开发者必备的核心工具.然而,多数开发者对Git的使用仍停留在"会用基础命令"的层面------知道用git add提交暂存、git commit提交本地、git push推送远程,却未必理解这些命令背后的底层逻辑:暂存区(Stage)、本地仓库(Local Repository)、远程仓库(Remote Repository)之间的数据流是怎样的?Git如何高效追踪文件的每一次变更?分布式架构与SVN等集中式版本控制系统相比,核心优势到底体现在哪里?
基于此,本文将分为上下两篇,全面、深入地剖析Git的原理与使用.其中上篇将重点聚焦Git的核心原理------从版本控制的本质出发,拆解Git的核心架构、关键对象(Blob、Tree、Commit)、三大区域的工作机制,以及Git追踪文件变更的底层逻辑,帮你建立对Git的系统性认知,打破"知其然不知其所以然"的困境.下篇则会聚焦实际应用,结合高频场景讲解Git的核心命令、分支管理策略、冲突解决方法等,让原理落地到实操,真正实现"懂原理、会使用、能排查".无论你是刚接触Git的新手,还是有一定使用经验、希望突破瓶颈的开发者,相信通过本文的剖析,都能对Git形成更深刻的理解,让这个强大的工具真正为你的开发工作赋能.接下来,就让我们从Git的核心原理开始,开启这场深入学习之旅.那么它们到底又有哪些方面的知识是需要学习和掌握的呢?废话不多说,带着这些疑问,下面跟着小编的节奏🎵一起学习吧!
目录
- 1.初识Git
- 2.Git安装
-
- [2.1 Linux---centos系统安装Git](#2.1 Linux—centos系统安装Git)
- [2.2 Linux---ubuntu系统安装Git](#2.2 Linux—ubuntu系统安装Git)
- 2.3Windows系统安装Git
- 3.Git基本操作
-
- 3.1创建Git本地仓库
- 3.2配置Git本地仓库
- 3.3认识⼯作区、暂存区、版本库
- 3.4添加⽂件---场景一
- [3.5查看.git ⽂件](#3.5查看.git ⽂件)
- 3.6添加⽂件---场景⼆
- 3.7修改⽂件
- 3.8版本回退
- 3.9撤销修改---情况一
- 3.10撤销修改---情况二
- 3.11撤销修改---情况三
- 3.12删除⽂件
- 4.分⽀管理
1.初识Git
提出问题:
不知道你⼯作或学习时,有没有遇到这样的情况:我们在编写各种⽂档时,为了防⽌⽂档丢失,更改
失误,失误后能恢复到原来的版本,不得不复制出⼀个副本,⽐如:
"报告-v1" "报告-v2" "报告-v3" "报告-确定版" "报告-最终版" "报告-究极进化版" ...
每个版本有各⾃的内容,但最终会只有⼀份报告需要被我们使⽤ .但在此之前的⼯作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的⽂件就越来越多,⽂件多不是问题,问题是:随着版本数量的不断增多,你还记得这些版本各⾃都是修改了什么吗?
⽂档如此,我们写的项⽬代码,也是存在这个问题的!!!
如何解决?--版本控制器
为了能够更⽅便我们管理这些不同版本的⽂件,便有了版本控制器.所谓的版本控制器,就是能让你了解到⼀个⽂件的历史,以及它的发展过程的系统.通俗的讲就是⼀个可以记录⼯程的每⼀次改动和版本迭代的⼀个管理系统,同时也⽅便多⼈协同作业.
⽬前最主流的版本控制器就是Git .Git 可以控制电脑上所有格式的⽂件,例如 doc、excel、dwg、dgn、rvt等等.对于我们开发⼈员来说,Git 最重要的就是可以帮助我们管理软件开发项⽬中的源代码⽂件!
注意事项:
还需要再明确⼀点,所有的版本控制系统,Git 也不例外,其实只能跟踪⽂本⽂件的改动,⽐如TXT⽂件,⽹⻚,所有的程序代码等等.版本控制系统可以告诉你每次的改动,⽐如在第5⾏加了⼀个单词"Linux",在第8⾏删了⼀个单词 "Windows".⽽图⽚、视频这些⼆进制⽂件,虽然也能由版本控制系统管理,但没法跟踪⽂件的变化,只能把⼆进制⽂件每次改动串起来,也就是只知道图⽚从100KB改成了120KB,但到底改了啥,版本控制系统不知道,也没办法知道.
2.Git安装
Git是开放源代码的代码托管⼯具,最早是在Linux下开发的.开始也只能应⽤于Linux平台,后⾯慢慢的被移植到windows下,现在,Git可以在Linux、Unix、Mac和Windows这⼏⼤平台上正常运⾏了.
2.1 Linux---centos系统安装Git
如果你的的平台是centos,安装git相当简单,以centos7.6为例:
⾸先,你可以试着输⼊Git,看看系统有没有安装Git:
出现像下⾯的结果,Linux会友好地告诉你Git没有安装.
bash
$ git
-bash: git: command not found
bash
安装Git:
sudo yum -y install git
查看Git安装的版本:
git --version
2.2 Linux---ubuntu系统安装Git
如果你的的平台是ubuntu,安装git相当简单,以我的ubuntu22.04为例:
⾸先,你可以试着输⼊git,看看系统有没有安装Git:由于小编已经提前装过了,所以在查看的时候会出现对应的版本信息,初次安装时所展示的信息是"Command 'git' not found",提示你进行安装.
出现像下⾯的结果,Linux会友好地告诉你Git没有安装,还会告诉你如何安装Git.
bash
$ git
Command 'git' not found, but can be installed with:
sudo apt install git
bash
安装Git:
sudo apt-get install git -y
查看git安装的版本:
git --version
2.3Windows系统安装Git
3.Git基本操作
3.1创建Git本地仓库
要提前说的是,仓库是进⾏版本控制的⼀个⽂件⽬录.我们要想对⽂件进⾏版本控制,就必须先创建⼀个仓库出来.创建⼀个Git 本地仓库对应的命令为
git init,注意命令要在⽂件⽬录下执⾏,例如:
我们发现,当前⽬录下多了⼀个.git 的隐藏⽂件,.git ⽬录是Git 来跟踪管理仓库的,不要⼿动修改这个⽬录⾥⾯的⽂件,不然改乱了,就把 Git 仓库给破坏了.其中包含Git 仓库的诸多细节,我们可以打开看看.
至于这些文件是什么意思?有什么用处,后面我会选择性的介绍.
3.2配置Git本地仓库
当安装Git后⾸先要做的事情是设置你的⽤⼾名称和 e-mail 地址,这是⾮常重要的.配置命令为:
其中[--global]是⼀个可选项.如果使⽤了该选项,表示这台机器上所有的Git仓库都会使⽤这个配置.如果你希望在不同仓库中使⽤不同的name或e-mail,可以不要[--global]选项,但要注意的是,执⾏命令时必须要在仓库⾥.
查看配置命令为:git config -l
删除对应的配置命令为:
git config [--global] --unset user.name
git config [--global] --unset user.email
3.3认识⼯作区、暂存区、版本库
1️⃣⼯作区:是在电脑上你要写代码或⽂件的⽬录.
2️⃣暂存区:英⽂叫stage 或 index.⼀般存放在.git ⽬录下的 index ⽂件(.git/index)中,我们把暂存区有时也叫作索引(index).
3️⃣版本库:⼜名仓库,英⽂名 repository.⼯作区有⼀个隐藏⽬录.git,它不算⼯作区,⽽是Git 的版本库.这个版本库⾥⾯的所有⽂件都可以被 Git 管理起来,每个⽂件的修改、删除,Git都能跟踪,以便任何时刻都可以追踪历史,或者在将来某个时刻可以"还原".
下⾯这个图展⽰了⼯作区、暂存区和版本库之间的关系:
图中左侧为⼯作区,右侧为版本库.Git 的版本库⾥存了很多东西,其中最重要的就是暂存区.
①在创建 Git 版本库时,Git 会为我们⾃动创建⼀个唯⼀的 master 分⽀,以及指向 master 的⼀个指针叫 HEAD.
②当对⼯作区修改(或新增)的⽂件执⾏git add 命令时,暂存区⽬录树的⽂件索引会被更新.
③当执⾏提交操作git commit 时,master 分⽀会做相应的更新,可以简单理解为暂存区的⽬录树才会被真正写到版本库中.
由上述描述我们便能得知:通过新建或粘贴进⽬录的⽂件,并不能称之为向仓库中新增⽂件,⽽只是
在⼯作区新增了⽂件.必须要通过使⽤ git add 和 git commit 命令才能将⽂件添加到仓库中进⾏管理!!!
3.4添加⽂件---场景一
在包含.git 的⽬录下新建⼀个ReadMe ⽂件,我们可以使⽤git add命令可以将⽂件添加到暂存区:
①添加⼀个或多个⽂件到暂存区:git add [file1] [file2] ...
②添加指定⽬录到暂存区,包括⼦⽬录:git add [dir]
③添加当前⽬录下的所有⽂件改动到暂存区:git add .
再使⽤git commit 命令将暂存区内容添加到本地仓库中:
①提交暂存区全部内容到本地仓库中:git commit -m "message"
②提交暂存区的指定⽂件到仓库区:git commit [file1] [file2] ... -m "message"
注意:git commit 后⾯的 -m 选项,要跟上描述本次提交的 message,由⽤⼾⾃⼰完成,这部分内容绝对不能省略,并要好好描述,是⽤来记录你的提交细节,是给我们⼈看的.
git commit 命令执⾏成功后会告诉我们,1个⽂件被改动(就是我们新添加的ReadMe⽂件),插⼊了一⾏内容(ReadMe有一⾏内容).
我们还可以多次 add 不同的⽂件,⽽只 commit ⼀次便可以提交所有⽂件,是因为需要提交的⽂件是
通通被 add 到暂存区中,然后⼀次性 commit 暂存区的所有修改.
截⾄⽬前为⽌,我们已经更够将代码直接提交⾄本地仓库了.我们可以使⽤ git log命令,来查看下历史提交记录:
该命令显⽰从最近到最远的提交⽇志,并且可以看到我们 commit 时的⽇志消息.
如果嫌输出信息太多,看得眼花缭乱的,可以试试加上 --pretty=oneline 参数.
需要说明的是,我们看到的⼀⼤串类似11ae5079a637...735bc35的是每次提交的 commit id(版本号),Git 的 commit id 不是1,2,3......递增的数字,⽽是⼀个 SHA1 计算出来的⼀个⾮常⼤的数字,⽤⼗六进制表示(你看到的 commit id 和我的肯定不⼀样,以你⾃⼰的为准).
3.5查看.git ⽂件
先来看看我的.git的⽬录结构:
①index就是我们的暂存区,add 后的内容都是添加到这⾥的.
②HEAD就是我们的默认指向master分⽀的指针:
③⽽默认的master分⽀,其实就是:
打印的11ae5079a637b4d694236e4f452b448fa735bc35是什么东西呢?保存的就是当前最新的commit id.
④objects为Git 的对象库,⾥⾯包含了创建的各种版本库对象及内容.当执⾏git add 命令时,暂存区的⽬录树被更新,同时⼯作区修改(或新增)的⽂件内容被写⼊到对象库中的⼀个新的对象中,就位于 ".git/objects" ⽬录下,让我们来看看这些对象有何⽤处:
⑤查找object时要将 commit id 分成2部分,其前2位是⽂件夹名称,后38位是⽂件名称.找到这个⽂件之后,⼀般不能直接看到⾥⾯是什么,该类⽂件是经过sha (安全哈希算法)加密过的⽂件,好在我们可以使⽤ git cat-file 命令来查看版本库对象的内容:
⑥其中,还有⼀⾏ tree 15a37e9ef171cca4a5d985fccd1fcf9414b2c7cf,我们使⽤同样的⽅法,看看结果:
⑦再看ReadMe对应的8d0e41234f24b6da002d962a26c2495ea16a425f:
总结⼀下,在本地的 git 仓库中,有⼏个⽂件或者⽬录很特殊
1️⃣index: 暂存区,git add 后会更新该内容.
2️⃣HEAD:默认指向 master 分⽀的⼀个指针.
3️⃣refs/heads/master:⽂件⾥保存当前 master分⽀的最新 commit id.
4️⃣objects:包含了创建的各种版本库对象及内容,可以简单理解为放了 git 维护的所有修改.后⾯再学习过程中,最好能将常⻅的 git 操作与.git ⽬录当中的结构内容变化对应起来,这样有利于我们理解git 细节流程.后⾯还会学习什么分⽀,标签什么的,我们就应该可以对应着研究了!
3.6添加⽂件---场景⼆
学习到这⾥,我们已经清楚了如何向仓库中添加⽂件,并且对于⼯作区、暂存区、版本库也有了⼀定的认识.那么我们再展示⼀种添加⽂件的场景,能加深对⼯作区、暂存区、版本库的理解,示例如下:
提交后发现打印了1file changed, 0 insertions(+), 0 deletions(-),意思是只有⼀个⽂件改变了,这时我们提出了疑问,不是新增了两个⽂件吗?再来回忆下,git add 是将⽂件添加到暂存区,git commit 是将暂存区的内容添加到本地仓库中.由于我们并没有使⽤ git add file5 ,file5 就不在暂存区中维护,所以我们 commit 的时候其实只是把已经在暂存区的 file4 提交了,⽽遗漏了⼯作区的file5.如何提交 file5 呢?很简单,再次add,commit 即可.
3.7修改⽂件
Git⽐其他版本控制系统设计的优秀,因为Git 跟踪并管理的是修改,⽽⾮⽂件.什么是修改?⽐如你新增了⼀⾏,这就是⼀个修改,删除了⼀⾏,也是⼀个修改,更改了某些字符,也是⼀个修改,删了⼀些⼜加了⼀些,也是⼀个修改,甚⾄创建⼀个新⽂件,也算⼀个修改.让我们将 ReadMe ⽂件进⾏⼀次修改:添加一个hello world.
此时,仓库中的 ReadMe 和我们⼯作区的 ReadMe 是不同的,如何查看当前仓库的状态呢?git status命令⽤于查看在你上次提交之后是否有对⽂件进⾏再次修改.
上⾯的结果告诉我们,ReadMe 被修改过了,但还没有完成添加与提交.⽬前,我们只知道⽂件被修改了,如果能知道具体哪些地⽅被修改了,就更好了.有人会说,我刚改的我知道呀!可是,在大型项目中随着时间的推移,你还记得你很久以前写了什么代码吗?或者没写?显然是不可能的!
git diff [file]命令⽤来显⽰暂存区和⼯作区⽂件的差异,显示的格式正是Unix通⽤的diff格式.也可以使⽤git diff HEAD -- [file]命令来查看版本库和⼯作区⽂件的区别.知道了对 ReadMe 做了什么修改后,再把它提交到本地仓库就放⼼多了.
git add 之后,就没有看到上⾯ no changes added to commit (use "git add" and/or "git commit -a") 的消息了.接下来让我们继续 git commit 即可:
3.8版本回退
之前我们也提到过,Git 能够管理⽂件的历史版本,这也是版本控制器重要的能⼒.如果有⼀天你发现之前做的⼯作出现了很⼤的问题,需要在某个特定的历史版本重新开始,这个时候,就需要版本回退的功能了.
执⾏git reset命令⽤于回退版本,可以指定退回某⼀次提交的版本.要解释⼀下"回退"本质是:要将版本库中的内容进⾏回退,⼯作区或暂存区是否回退由命令参数决定:
git reset 命令语法格式为:git reset [--soft | --mixed | --hard] [HEAD]
1️⃣--mixed 为默认选项,使⽤时可以不⽤带该参数.该参数将暂存区的内容退回为指定提交版本内容,⼯作区⽂件保持不变.
2️⃣--soft 参数对于⼯作区和暂存区的内容都不变,只是将版本库回退到某个指定版本.
3️⃣--hard 参数将暂存区与⼯作区都退回到指定版本.切记⼯作区有未提交的代码时不要⽤这个命令,因为⼯作区会回滚,你没有提交的代码就再也找不回了,所以使⽤该参数前⼀定要慎重.
4️⃣HEAD 说明:
①可直接写成commit id,表示指定退回的版本.
②HEAD表示当前版本
③HEAD^ 上⼀个版本
④HEAD^^ 上上⼀个版本
⑤以此类推...
5️⃣可以使⽤ 〜数字表⽰:
①HEAD~0 表示当前版本
②HEAD~1 上⼀个版本
③HEAD^2 上上⼀个版本
④以此类推...
下面看看版本回退的情况:
如果我后悔了,又想回到含有5个file的版本:
当我们进行清屏操作时,或者将服务器关闭了,那么commit展示的信息就不见了,我们还想进行版本回退该如何解决呢?Git还提供了⼀个git reflog命令能补救⼀下,该命令⽤来记录本地的每⼀次命令.这样,你就可以很⽅便的找到你的所有操作记录了!Git 版本回退的时候,也可以使⽤部分 commit id 来代表⽬标版本.
可往往是理想很丰满,现实很⻣感.在实际开发中,由于⻓时间的开发了,导致 commit id 早就找不到了,可突然某⼀天,我⼜想回退到我想要的版本那该如何操作呢?貌似现在不可能了...值得说的是,Git 的版本回退速度⾮常快,因为 Git 在内部有个指向当前分⽀(此处是master)的HEAD指针,refs/heads/master ⽂件⾥保存当前 master 分⽀的最新 commit id.当我们在回退版本的时候,Git 仅仅是给 refs/heads/master 中存储⼀个特定的版本,可以简单理解成如下示意图:
3.9撤销修改---情况一
如果我们在我们的⼯作区写了很⻓时间代码,越来越写不下去,觉得⾃⼰写的实在是垃圾,想恢复到上⼀个版本.
情况⼀:对于⼯作区的代码,还没有 add.
我们在ReadMe文件中新增一行代码:xxx code,然后再删除代码!
虽然可以直接删除,⾟亏我们⼯作效率不⾼,才写了⼀⾏代码就发现不⾏了,要是你写了3天,⼀直都没有提交,该怎么删掉呢?你⾃⼰都忘了⾃⼰新增过哪些,有人说,我可以git diff xxx ⼀下,看看差别再删啊,
那你肯定⼜要花3天时间删代码了,并且很⼤的概率还会改出bug.⼀周过去了,你怎么向你的⽼板交
代呢?Git 其实还为我们提供了更好的⽅式,我们可以使⽤git checkout -- [file]命令让⼯作区的⽂件回到最近⼀次 add 或 commit 时的状态.要注意 git checkout -- [file] 命令中的--很重要,切记不要省略,⼀旦省略,该命令就变为其他意思了,后⾯我们再说.
3.10撤销修改---情况二
情况⼆:已经 add ,但没有 commit.
add后还是保存到了暂存区呢?怎么撤销呢?
让我们来回忆⼀下学过的 git reset 回退命令,该命令如果使⽤ --mixed 参数,可以将暂存区的内容退回为指定的版本内容,但⼯作区⽂件保持不变.那我们就可以回退下暂存区的内容了!!!⽤ git status 查看⼀下,发现现在暂存区是⼲净的,⼯作区有修改.如何丢弃⼯作区的修改吗?
3.11撤销修改---情况三
情况三:已经 add ,并且也 commit了
不要担⼼,我们可以 git reset --hard HEAD^ 回退到上⼀个版本!不过,这是有条件的,就是你还没有把⾃⼰的本地版本库推送到远程.还记得Git是分布式版本控制系统吗?我们后⾯会讲到远程版本库,⼀旦你推送到远程版本库,你就真的惨了...
3.12删除⽂件
在Git中,删除也是⼀个修改操作,我们实战⼀下,如果要删除 file5 ⽂件,怎么搞呢?如果你这样做了:
但这样直接删除是没有⽤的,反⽽徒增烦恼,git status 命令会⽴刻告诉你哪些⽂件被删除了:
此时,⼯作区和版本库就不⼀致了,要删⽂件,⽬前除了要删⼯作区的⽂件,还要清除版本库的⽂件.
⼀般⾛到这⾥,有两种可能:
①确实要从版本库中删除该⽂件
②不⼩⼼删错了
对第⼆种情况,很明显误删,需要使⽤ git 来进⾏恢复,很简单,我们刚学过(删除也是修改)
对于第⼀种情况,很明显是没有删完,我们只删除了⼯作区的⽂件.这时就需要使⽤git rm将⽂件从暂存区和⼯作区中删除,并且 commit :
4.分⽀管理
4.1理解分⽀
Git 的杀⼿级功能之⼀(注意是之⼀,也就是后⾯还有之⼆,之三......):分⽀.分⽀就是科幻电影⾥⾯的平⾏宇宙,当你正在电脑前努⼒学习 C++ 的时候,另⼀个你正在另⼀个平⾏宇宙⾥努⼒学习JAVA.如果两个平⾏宇宙互不⼲扰,那对现在的你也没啥影响.不过,在某个时间点,两个平⾏宇宙合并了,结果,你既学会了C++⼜学会了JAVA!
在版本回退⾥,你已经知道,每次提交,Git都把它们串成⼀条时间线,这条时间线就可以理解为是⼀个分⽀.截⽌到⽬前,只有⼀条时间线,在Git⾥,这个分⽀叫主分⽀,即 master 分⽀.再来理解⼀下HEAD,HEAD 严格来说不是指向提交,⽽是指向master,master才是指向提交的,所以,HEAD 指向的就是当前分⽀.
通过查看当前的版本库,我们也能清晰的理出思路:
4.2创建、切换、合并分⽀
1️⃣创建分⽀
Git ⽀持我们查看或创建其他分⽀,在这⾥我们来创建第⼀个⾃⼰的分⽀dev ,对应的命令为:
当我们创建新的分⽀后,Git 新建了⼀个指针叫 dev,* 表示当前 HEAD 指向的分⽀是 master 分⽀.
发现⽬前dev和master指向同⼀个修改.并且也可以验证下 HEAD ⽬前是指向master的.
2️⃣切换分支
那如何切换到dev分⽀下进⾏开发呢?使⽤git checkout 命令即可完成切换,示例如下:
我们发现HEAD已经指向了dev,就表示我们已经成功的切换到了dev上!接下来,在 dev 分⽀下修改 ReadMe⽂件,新增⼀⾏内容,并进⾏⼀次提交操作:
现在,dev 分⽀的⼯作完成,我们就可以切换回 master 分⽀:
切换回master 分⽀后,发现ReadMe⽂件中新增的内容不⻅了!!!赶紧再切回 dev 看看:
在dev分⽀上,内容还在.为什么会出现这个现象呢?我们来看看dev分⽀和master分⽀指向,发现两者指向的提交是不⼀样的:
看到这⾥就能明⽩了,因为我们是在dev分⽀上提交的,⽽master分⽀此刻的提交点并没有变,此时的状态如图如下所示:
当切换到master 分⽀之时,HEAD 就指向了master,当然看不到提交了!
3️⃣合并分⽀
为了在master主分⽀上能看到新的提交,就需要将dev 分⽀合并到 master 分⽀,示例如下:
git merge命令⽤于合并指定分⽀到当前分⽀.合并后,master 就能看到 dev 分⽀提交的内容了.此时的状态如图如下所示:
Fast-forward 代表"快进模式",也就是直接把master指向dev的当前提交,所以合并速度⾮常快.当然,也不是每次合并都能 Fast-forward,我们后⾯会讲其他⽅式的合并.
4.3删除分⽀
合并完成后,dev 分⽀对于我们来说就没⽤了,那么dev分⽀就可以被删除掉,注意如果当前正处于某分⽀下,就不能删除当前分⽀,如:
但是可以在其他分⽀下删除当前分⽀,如:
此时的状态如图如下所示:
因为创建、合并和删除分⽀⾮常快,所以Git⿎励你使⽤分⽀完成某个任务,合并后再删掉分⽀,这和直接在master分⽀上⼯作效果是⼀样的,但过程更安全.
4.4合并冲突
可是,在实际分⽀合并的时候,并不是想合并就能合并成功的,有时候可能会遇到代码冲突的问题.为了演示这问题,创建⼀个新的分⽀ dev1,并切换⾄⽬标分⽀,我们可以使⽤ git checkout -b dev1 ⼀步完成创建并切换的动作,示例如下:
在dev1分⽀下修改 ReadMe⽂件,更改⽂件内容如下,并进⾏⼀次提交,如:
切换⾄master 分⽀,观察 ReadMe⽂件内容:我们发现,切回来之后,⽂件内容由变成了⽼的版本,这种现象很正常,我们现在也完全能理解.此时在 master 分⽀上,我们对 ReadMe ⽂件再进⾏⼀次修改,并进⾏提交,如下:
现在,master 分⽀和 dev1 分⽀各⾃都分别有新的提交,变成了这样:
这种情况下,Git 只能试图把各⾃的修改合并起来,但这种合并就可能会有冲突,如下所示:
发现 ReadMe ⽂件有冲突后,可以直接查看⽂件内容,要说的是 Git 会⽤ <<<<<<<,=======,>>>>>>> 来标记出不同分⽀的冲突内容,如下所示:
此时我们必须要⼿动调整冲突代码,并需要再次提交修正后的结果!!(再次提交很重要,切勿忘记)
到这⾥冲突就解决完成,此时的状态变成了:
⽤带参数的git log也可以看到分⽀的合并情况,具体⼤家可以⾃⾏搜索 git log 的⽤法:
最后,不要忘记 dev1分⽀使⽤完毕后就可以删除了:
4.5分⽀模式
通常合并分⽀时,如果可能,Git 会采⽤ Fast forward 模式.还记得如果我们采⽤ Fast forward 模式之后,形成的合并结果是什么呢?回顾⼀下:
在这种 Fast forward 模式下,删除分⽀后,查看分⽀历史时,会丢掉分⽀信息,看不出来最新提交到底是 merge 进来的还是正常提交的.但在合并冲突部分,我们也看到通过解决冲突问题,会再进⾏⼀次新的提交,得到的最终状态为;
那么这就不是 Fast forward 模式了,这样的好处是,从分⽀历史上就可以看出分⽀信息.例如我们现在已经删除了在合并冲突部分创建的dev1 分⽀,但依旧能看到 master 其实是由其他分⽀合并得到:
Git ⽀持我们强制禁⽤ Fast forward 模式,那么就会在 merge 时⽣成⼀个新的 commit ,这样,从分⽀历史上就可以看出分⽀信息.下⾯我们实战⼀下 --no-ff ⽅式的 git merge .⾸先,创建新的分⽀ dev2 ,并切换⾄新的分⽀:
修改 ReadMe ⽂件,并提交⼀个新的 commit :
切回 master 分⽀,开始合并:
请注意 --no-ff 参数,表示禁⽤ Fast forward 模式.禁⽤ Fast forward 模式后合并会创建⼀个新的 commit .所以加上 -m 参数.把描述写进去.合并后.查看分⽀历史:
可以看到,不使⽤ Fast forward 模式,merge后就像这样:所以在合并分⽀时,加上 --no-ff 参数就可以⽤普通模式合并,合并后的历史有分⽀,能看出来曾经做过合并,⽽ fast forward 合并就看不出来曾经做过合并.
4.6分⽀策略
在实际开发中,我们应该按照⼏个基本原则进⾏分⽀管理:
⾸先,master分⽀应该是⾮常稳定的,也就是仅⽤来发布新版本,平时不能在上⾯⼲活;
那在哪⼲活呢?⼲活都在dev分⽀上,也就是说,dev分⽀是不稳定的,到某个时候,⽐如1.0版本发布时,再把dev分⽀合并到master上,在master分⽀发布1.0版本;你和你的⼩伙伴们每个⼈都在dev分⽀上⼲活,每个⼈都有⾃⼰的分⽀,时不时地往dev分⽀上合并就可以了.所以,团队合作的分⽀看起来就像这样:
4.7bug分⽀
假如我们现在正在dev2分⽀上进⾏开发,开发到⼀半,突然发现master 分⽀上⾯有 bug,需要解决.在Git中,每个bug 都可以通过⼀个新的临时分⽀来修复,修复后,合并分⽀,然后将临时分⽀删除.可现在dev2的代码在⼯作区中开发了⼀半,还⽆法提交,怎么办?例如:
Git 提供了git stash命令,可以将当前⼯作区信息进⾏储藏,被储藏的内容可以在将来某个时间恢复出来.
⽤ git status 查看⼯作区,就是⼲净的(除⾮有没有被 Git 管理的⽂件),因此可以放⼼地创建分⽀来修复bug.储藏 dev2 ⼯作区之后,由于我们要基于master分⽀修复 bug,所以需要切回 master 分⽀,再新建临时分⽀来修复 bug,示例如下:
修复完成后,切换到 master 分⽀,并完成合并:
⾄此,bug 的修复⼯作已经做完了,我们还要继续回到 dev2 分⽀进⾏开发.切换回 dev2分支:
⼯作区是⼲净的,刚才的⼯作现场存到哪去了?⽤git stash list命令看看:
⼯作现场还在,Git 把 stash 内容存在某个地⽅了,但是需要恢复⼀下,如何恢复现场呢?我们可以使⽤git stash pop命令,恢复的同时会把 stash 也删了,示例如下:
恢复完代码之后我们便可以继续完成开发,开发完成后便可以进⾏提交,例如:
但我们注意到了,修复 bug 的内容,并没有在 dev2 上显示.此时的状态图为:
Master 分⽀⽬前最新的提交,是要领先于新建 dev2 时基于的 master 分⽀的提交的,所以我们在dev2中当然看不⻅修复 bug 的相关代码.我们的最终⽬的是要让 master 合并 dev2 分⽀的,那么正常情况下我们切回 master 分⽀直接合并即可,但这样其实是有⼀定⻛险的.是因为在合并分⽀时可能会有冲突,⽽代码冲突需要我们⼿动解决(在 master上解决).我们⽆法保证对于冲突问题可以正确地⼀次性解决掉,因为在实际的项⽬中,代码冲突不只⼀两⾏那么简单,有可能⼏⼗上百⾏,甚⾄更多,解决的过程中难免⼿误出错,导致错误的代码被合并到 master上.此时的状态为:
解决这个问题的⼀个好的建议就是:最好在⾃⼰的分⽀上合并下 master ,再让 master 去合并dev ,这样做的⽬的是有冲突可以在本地分⽀解决并进⾏测试,⽽不影响 master .此时的状态为:
对应的实操演⽰如下,要说明的是,以下演⽰的merge操作,没有使⽤ --no-ff ,但上述的图示是禁⽤ Fast forward 了模式后得出的,主要是为了⽅便解释问题.
4.8删除临时分⽀
软件开发中,总有⽆穷⽆尽的新的功能要不断添加进来.添加⼀个新功能时,你肯定不希望因为⼀些实验性质的代码,把主分⽀搞乱了,所以,每添加⼀个新功能,最好新建⼀个分⽀,我们可以将其称之为 feature 分⽀,在上⾯开发,完成后,合并,最后,删除该 feature分⽀.
可是,如果我们今天正在某个 feature 分⽀上开发了⼀半,被产品经理突然叫停,说是要停⽌新功能的开发.虽然⽩⼲了,但是这个 feature 分⽀还是必须就地销毁,留着⽆⽤了.这时使⽤传统
的git branch -d命令删除分⽀的⽅法是不⾏的.演示如下:
⼩结:
分⽀在实际中有什么⽤呢?假设你准备开发⼀个新功能,但是需要两周才能完成,第⼀周你写了50%的代码,如果⽴刻提交,由于代码还没写完,不完整的代码库会导致别⼈不能⼲活了.如果等代码全部写完再⼀次提交,⼜存在丢失每天进度的巨⼤⻛险.现在有了分⽀,就不⽤怕了.你创建了⼀个属于你⾃⼰的分⽀,别⼈看不到,还继续在原来的分⽀上正常⼯作,⽽你在⾃⼰的分⽀上⼲活,想提交就提交,直到开发完毕后,再⼀次性合并到原来的分⽀上,这样,既安全,⼜不影响别⼈⼯作.并且Git ⽆论创建、切换和删除分⽀,Git在1秒钟之内就能完成!⽆论你的版本库是1个⽂件还是1万个⽂件.
敬请期待下一篇文章内容-->深入剖析Git的原理和使用(下)相关内容!
每日心灵鸡汤:
上船不思岸上人,下船不提船上事.
旧人无需知近况,新人不必问过往.
各有各的渡口,各有各的归舟.
一念执着,万般皆苦.一念放下,便是重生.


















































































