跳到主要内容
Git 原理与使用(上):深入剖析核心机制与实战 | 极客日志
Shell / Bash
Git 原理与使用(上):深入剖析核心机制与实战 Git 作为分布式版本控制系统,是软件开发协作的核心工具。本文聚焦 Git 核心原理与基础操作,涵盖工作区、暂存区与版本库的交互逻辑,详解文件添加、修改、回退及分支管理策略。通过 Linux 环境下的实操演示,帮助开发者理解 commit、merge、reset 等命令背后的数据流向,建立对版本控制的系统性认知,避免常见误操作风险。
Git 原理与使用(上):深入剖析核心机制与实战
在软件开发的全流程中,版本控制是保障协作效率、规避开发风险的核心基石。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 的核心命令、分支管理策略、冲突解决方法等,让原理落地到实操。
1. 初识 Git
不知道你工作或学习时,有没有遇到这样的情况:我们在编写各种文档时,为了防止文档丢失、更改失误,失误后能恢复到原来的版本,不得不复制出一个副本,比如:'报告-v1'、'报告-v2'、'报告-确定版'……每个版本有各自的内容,但最终只会有一份报告被使用。但在此之前的工作都需要这些不同版本的报告,于是每次都是复制粘贴副本,产出的文件就越来越多。随着版本数量的不断增多,你还记得这些版本各自都是修改了什么吗?
文档如此,我们写的项⽬代码,也是存在这个问题的!如何解决?——版本控制器。
所谓的版本控制器,就是能让你了解到一个文件的历史,以及它的发展过程的系统。通俗地讲就是一个可以记录工程的每一次改动和版本迭代的一个管理系统,同时也方便多人协同作业。目前最主流的版本控制器就是 Git。Git 可以控制电脑上所有格式的文件,例如 doc、excel、dwg 等等。对于我们开发人员来说,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 相当简单,以 CentOS 7.6 为例:
首先,你可以试着输入 git,看看系统有没有安装 Git:
$ git -bash: git: command not found
出现像下面的结果,Linux 会友好地告诉你 Git 没有安装。
安装 Git:
sudo yum -y install git
查看 Git 安装的版本:
git --version
2.2 Linux — Ubuntu 系统安装 Git
如果你的平台是 Ubuntu,安装 Git 相当简单,以 Ubuntu 22.04 为例:
首先,你可以试着输入 git,看看系统有没有安装 Git。由于已经提前装过了,所以在查看的时候会出现对应的版本信息,初次安装时所展示的信息是'Command 'git' not found',提示你进行安装。
出现像下面的结果,Linux 会友好地告诉你 Git 没有安装,还会告诉你如何安装 Git:
$ git Command 'git' not found, but can be installed with: apt install git
sudo
sudo apt-get install git -y
2.3 Windows 系统安装 Git Windows 环境下可以直接下载官方安装包进行安装,过程较为直观,此处不再赘述。
3. Git 基本操作
3.1 创建 Git 本地仓库 要提前说的是,仓库是进行版本控制的一个文件目录。我们要想对文件进行版本控制,就必须先创建一个仓库出来。创建一个 Git 本地仓库对应的命令为 git init,注意命令要在文件目录下执行,例如:
我们发现,当前目录下多了一个 .git 的隐藏文件,.git 目录是 Git 来跟踪管理仓库的,不要手动修改这个目录里面的文件,不然改乱了,就把 Git 仓库给破坏了。其中包含 Git 仓库的诸多细节,我们可以打开看看。至于这些文件是什么意思?有什么用处,后面我会选择性的介绍。
3.2 配置 Git 本地仓库 当安装 Git 后首先要做的事情是设置你的用户名和 e-mail 地址,这是非常重要的。配置命令为:
git config --global user.name "Your Name"
git config --global user.email "[email protected] "
其中 [--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 文件 ① 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 添加文件—场景二 学习到这里,我们已经清楚了如何向仓库中添加文件,并且对于工作区、暂存区、版本库也有了一定的认识。那么我们再展示一种添加文件的场景,能加深对工作区、暂存区、版本库的理解,示例如下:
提交后发现打印了 1 file 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️⃣ 可以使用 〜数字表示:
① HEAD0 表示当前版本
② HEAD1 上一个版本
③ 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 撤销修改—情况一 如果我们在我们的工作区写了很长时间代码,越来越写不下去,觉得自己写的实在是垃圾,想恢复到上一个版本。
我们在 ReadMe 文件中新增一行代码:xxx code,然后再删除代码!
虽然可以直接删除,幸亏我们工作效率不高,才写了一行代码就发现不行了,要是你写了 3 天,一直都没有提交,该怎么删掉呢?你自己都忘了自己新增过哪些,有人说,我可以 git diff xxx 一下,看看差别再删啊,那你肯定又要花 3 天时间删代码了,并且很大的概率还会改出 bug。一周过去了,你怎么向你的老板交代呢?Git 其实还提供了更好的方式,我们可以使用 git checkout -- [file] 命令让工作区的文件回到最近一次 add 或 commit 时的状态。要注意 git checkout -- [file] 命令中的 -- 很重要,切记不要省略,一旦省略,该命令就变为其他意思了,后面我们再说。
3.10 撤销修改—情况二 让我们来回忆一下学过的 git reset 回退命令,该命令如果使用 --mixed 参数,可以将暂存区的内容退回为指定的版本内容,但工作区文件保持不变。那我们就可以回退下暂存区的内容了!!!用 git status 查看一下,发现现在暂存区是干净的,工作区有修改。如何丢弃工作区的修改吗?
3.11 撤销修改—情况三 不要担心,我们可以 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:
请注意 --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.7 Bug 分支 假如我们现在正在 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 万个文件。
相关免费在线工具 Base64 字符串编码/解码 将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online
Base64 文件转换器 将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online
Markdown转HTML 将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online
HTML转Markdown 将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online
JSON 压缩 通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online
JSON美化和格式化 将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online