Understanding Git through images Hello Dev community! What is Git? Branch Merge Rebase Keep local repositories up-to-date Useful Functions End

2025-05-25

通过图像理解 Git

大家好,开发者社区!

什么是 Git?

分支

合并

变基

保持本地存储库为最新

实用函数

结尾

大家好,开发者社区!

我是个新手,在日本从事开发工作才几个月。我受到了Nico Riedmann 的《学习 Git 概念而非命令》一书的启发,并以自己的方式对 Git 进行了总结。当然,我也参考了官方文档
。 从系统结构入手理解 Git 会让 Git 变得更有趣。最近我对 Git 非常着迷,正在创建自己的 Git 系统。

最近,我写了一篇关于如何制作类似 git 的软件的文章!
制作原始的 git

什么是 Git?

管理版本和分发工作

Git 是一种称为分布式版本控制系统的源代码管理系统。Git
是一个通过记录和跟踪文件的变更日志(版本)、比较过去和当前文件以及明确变更来简化开发工作的工具。
该系统还允许多个开发人员同时编辑文件,从而实现工作分布式。

使用 Git 意味着

首先,在您的计算机上(以下称为“本地仓库”)一个可共享的存储位置(以下称为“远程仓库”)复制该文件或其他文件,然后添加或编辑新的代码或文件。之后
,通过将文件从本地仓库注册到远程仓库,即可更新这些文件。

git_workflow

通过图像理解

在使用 Git 时,重要的是遵循从“什么”到“什么”的“如何工作”的原则。
如果只是操作命令,可能会不理解正在发生的事情并使用错误的命令。

(信息)

当操作 Git 时,尝试想象操作前后发生的事情。

开始新的工作

存储库

Git 中的存储库是文件的存储,可以是远程的,也可以是本地的。

远程仓库是指将源代码放置在互联网服务器上,供所有人共享的仓库。
本地仓库是指将源代码放置在您的计算机上,只有您可以进行更改的仓库。

复制存储库并开始工作

首先,准备好你自己的开发环境。
你只需要决定在哪个目录中工作即可。
例如,你的主目录就可以,或者任何你常用的目录都可以。

接下来,从远程存储库复制并导入文件。
这称为clone

克隆

所调用的远程存储库project仅包含,这是您远程存储库first.txt时的图像。clone

(信息)

当然,您可以先创建本地仓库,然后镜像远程仓库。
这被称为“本地仓库initialize”,允许您将正在处理的目录转换为仓库。

(补充)工作目录

工作目录并非任何特殊目录,而是您在计算机上始终工作的目录。
如果您将其视为一个目录,并且可以project通过 Git 暂存区或本地存储库连接到 Git 管理的目标目录(在本例中为 ),则更容易理解。

工作目录

更改和添加文件

源代码的修改是通过工作目录(暂存区)进行的。
实际上,我们在工作目录中工作。

让我们创建一个名为 的新文件second.txt

新文件

接下来,将修改后的文件移至暂存区。
这称为add

Git 的一个特性是,在更改反映到本地仓库之前会有一个缓冲。
稍后我会详细解释为什么存在这个缓冲。

添加

然后,我们将暂存区的内容注册到本地仓库。
这称为commit

顺便说一句,我们可以在您 时发表评论commit
在本例中,我们添加了一个文件,因此请写入git commit -m 'add second.txt'

犯罪

(信息)

提交操作会在仓库中创建一个提交对象
。 提交对象简单来说就是包含更新者信息和修改文件的数据。
(所有数据都会被保存,不仅仅是差异,还包括文件当时的完整状态(快照)。有关 Git 对象的更多信息,
请参阅Git对象。

适应远程存储库

那么,工作就完成了!
最后一步是将本地存储库中的更改反映到远程存储库。
这称为push

推

如果您将其视为对远程存储库的提交,可能会更容易理解。

查看差异

同一个文件之间的变化称为diff
我们可以在文件中看到变化点。

我不会详细介绍这些命令,但这里有三个我经常使用的命令。
git diff --stage查看之前原始工作目录的更改add
git diff --stage查看之后工作目录的更改add
git diff <commit> <commit>比较提交。

(旁白)有一个步骤叫做暂存区

随着开发工作的增长,我们经常在一个工作目录中进行大量更改。
如果将所有更改一次性放入本地仓库,会发生什么情况?
在这种情况下,在解析提交时,您可能不知道某个功能是在何处实现的。

在 Git 中,建议commit每个功能创建一个。
因此,有一个暂存区,您可以在其中将commit单元细分为更小的单元。

暂存区

Git 的概念是仅暂存所需的内容,然后继续工作或commit提前进行,以促进高效的开发,并可以追溯每个实现的历史记录。

概括

基本工作流程是一次clone然后、 和每次工作。addcommitpush

基本.gif

(信息)

clone:从远程存储库复制到您的开发环境(本地存储库和工作目录)。
add:将文件从工作目录添加到暂存区并准备提交。
commit:将文件从暂存区注册到本地存储库。此时,将创建一个提交对象。
push:将本地存储库中的更改注册到远程存储库。

分支

我们创建一个分支,branch用于在多个分支中更改和添加文件。
保存在分支中的文件main正在被持续使用。
使用独立分支的原因是为了在不影响当前正在运行的源代码的情况下工作。

创建新分支

让我们创建一个名为 的分支develop!我们可以使用或 来
创建分支 前者直接创建一个分支,后者创建一个分支并将你移动到该分支。 (分支在代码库中维护。)git branch <new branch>git checkout -b <new branch>

新分支

生成分支时的关键在于从哪个分支派生
我们可以将源指定为git checkout -b <new branch> <from branch>
如果不指定,则当前正在处理的分支将成为<from branch>

(信息)

分支实际上是指向提交的指针(严格来说,是提交对象的哈希值)。
生成新的分支意味着新分支也向提交指明了 from 分支所指向的提交。

在分支机构工作

移动分支称为checking out
指向当前正在处理的分支的指针称为HEAD
因此,从main分支移动到develop分支意味着更改HEAD

查看

现在两个分支都指向名为 的提交Atr3ul
您刚刚second.txt通过在分支中提交添加了内容main,因此您领先于提交f27baz
从这里开始,假设您在分支second.txt中进行了更改develop并进行了新的提交。

新提交

然后,如图所示,develop分支创建了一个名为的提交m9sgle并指向该提交。

当前的 HEAD 位置(工作分支位置)、文件已在哪个阶段进行处理、或者谁在处理该文件的状态称为status

(信息)

如果你熟悉面向对象,你可能会理解提交上箭头的含义。
它表示“父”提交和“子”提交之间的关系。
假设是parent←-child,即从父提交(提交)衍生的子提交(提交)增长(改变了)了多少。

(题外话)Git-Flow 和 GitHub-Flow

不同开发团队的分支管理方式各不相同。
另一方面,就像编程命名约定一样,Git 中也有一个通用的分支增长模型。
以下是两个简单的模型。我认为了解这一点就足够了。

“Git Flow” 是一个相当复杂和精妙的结构。
我认为它是 Git 应该如何使用的一个模型。

git flow

各分支的定义

master:发布产品的分支。此分支上没有工作。

development:用于开发产品的分支。准备发布时,请合并到release。此分支上没有工作。

feature:用于添加功能的分支,准备发布时合并到开发中。

hotfix:对于紧急的发布后工作(关键错误修复等),从 master 分支出来,合并到 master,然后合并到 development。

release:用于产品发布的准备。从develop包含待发布功能和错误修复的分支开始。
准备发布后,合并到 master 分支,再合并到 development 分支。

“GitHub Flow”是 Git Flow 的一个稍微简化的模型。

GitHub 流程

如您所见,它仅由master和组成feature
重要的区别在于 的缓冲pull requests(在下面的拉动中解释),它允许分支之间的集成。

概括

基本上,由于主干(master)上没有工作,我们为想要做的每个工作单元创建一个分支并创建一个新的提交。

分支动漫.gif

(信息)

branch:指向提交的新指针
checkout:移动HEAD以改变branch要进行的工作。

合并

整合分支称为merge
基本上,我们会合并到maindevelop分支。
请注意不要搞错哪个分支合并(吸收)了哪个分支。
我们始终会将 HEAD 移动到您要从中派生的分支,然后从您要从中派生的分支进行整合。

我目前正在feature分支上工作并创建了以下内容third.txt

图片描述

第三.txt



Hello, World! I'm noshishi, from Japan.
I like dancing on house music.


Enter fullscreen mode Exit fullscreen mode



然后我们add又完成了commit


快进

feature分支指向可以追溯到该分支的提交时develop,该develop分支处于某种fast-forward状态。

develop首先,使用移至checkout

图片描述

在这种情况下,develop分支根本没有进展,因此merge分支feature只会将提交向前移动。
在这种情况下,developfeature分支共享相同的提交。

图片描述

禁止快进

如果develop分支通过提交或合并进展到新的提交会怎么样?
这被称为no fast-forward情况。

develop分支中,您已对 进行了更改first.txt并已完成commit
因此develop分支与feature分支已完全分裂。

图片描述

如果你尝试merge一个分支切换到另一个分支,Git 会检查你的变更日志。 如果没有冲突的编辑,就会立即创建一个。 这被称为featuredevelop
merge commit
automatic merge

图片描述

处理冲突

no fast-forward状态下,工作内容的差异被称为conflict
在这种情况下,我们必须手动修复conflict内容 和commit

develop分支中,我们创建了以下third.txtcommitted

第三.txt



Hello, World! I'm nope, from USA.
I like dancing on house music.


Enter fullscreen mode Exit fullscreen mode

develop分支中,I'm nope, from USA
feature分支中,I'm noshishi, from Japan
第一行的内容有冲突。

merge如果此时执行 a ,conflict则会发生 a 。Git会在解决 后
要求您执行commitconflict

图片描述

(我们工作的分支是develop分支)

如果你按照说明查看third.txt,你会看到以下附加内容

third.txt(冲突后)



<<<<<<<< HEAD
Hello, World! I'm noshishi, from Japan.
=======
Hello, World! I'm nope, from USA.
>>>>>>>> feature
I like dancing on house music.


Enter fullscreen mode Exit fullscreen mode

上方HEAD以 分隔的=======表示分支的内容develop
下方表示feature分支。

feature你首先考虑了要采用哪一个,并决定采用这次分支中的更改。之后唯一的操作就是手动
编辑(删除不需要的部分)。third.txt

third.txt(编辑后)



Hello, World! I'm noshishi, from Japan.
I like dancing on house music.


Enter fullscreen mode Exit fullscreen mode

接下来你要做的就是addcommit
被解决,并创建conflict一个新的。merge commit

图片描述

冲突是初学者所害怕的,但是一旦你学会了这一点,你就不再害怕了。

(信息)

如果你merge解决了conflict,为什么不merge再次解决呢?
当你merge解决了 之后,develop分支会进入merge状态,如果没有conflicts,新文件会自动addedcommit
所以它并不是一个特殊的解决commit之后。 这就是为什么它被称为conflict
merge commit

删除不需要的分支

合并后的分支基本上没什么用,所以我们会将其删除。
如果我们保留某个分支,您可以从要删除的分支移动到另一个分支,然后git branch -d <branch>
您可能认为该分支上的提交已被删除。
实际上,这些提交会被转移到合并后的分支。
您可以使用git log来查看您在该分支上所做的所有提交以及合并分支上的提交。

(旁白)什么是分支

我们说过,分支是指向提交的指针,但它还包含另一个重要数据:
该分支上的所有提交。

分支是提交的集合,它有一个指向该集合中最新提交的指针。(严格来说,提交可以追溯到之前的提交。)

下图说明了这一点。

图片描述

所以我们可以像 Git Flow 那样,将分支想象成水平轴上的分支。
顺便说一下,如果你把上面的图以水平轴为分支来绘制,它看起来就像这样。

图片描述

概括

fast-forward merge

快速合并.gif

no fast-forward merge

nofast_merge1.gif

no fast-forward merge with conflict

nofast_merge2.gif

(信息)

merge:将工作分支(例如feature)集成(吸收)到特定分支(例如maindevelop)并创建新的提交。

变基

Rebase是通过更改分支的提交来合并分支的过程。
它与 类似merge,不同之处在于您正在处理的分支是目标分支。

develop假设您正在和分支上工作feature

图片描述

移动分支

您可能想将develop分支上的当前提交反映到feature分支中。
您需要将feature分支从gp55sw提交移动到3x7oit提交。

feature可以通过执行以下操作立即将其从分支中移出git rebase develop

图片描述

这个过程更像是feature从分支上的最新提交重新生成分支,develop而不是执行merge
区别在于,你移动整个提交并进行新的提交。

图片描述

这样做的一个原因是,它随时都fast-forward易于操作。 另一个原因是,提交是对齐的,这样可以轻松追溯提交历史,并且文件更新的顺序保持一致。merge

处理 rebase 冲突

当然,在conflict中也有一个rebase
您在分支fourth.txt中添加了内容feature,但没有在分支fourth.txt中更改内容develop
conflict

然而,如果以下变化被彼此覆盖,conflict就会发生。

图片描述

你可以像处理 一样处理它merge
但是,在检查了差异并完成文件编辑后,你应该使用 来完成你的工作git rebase --continue
你不必commit,它会自动提交。

(信息)

rebase:将派生分支的提交移动到新的提交。

保持本地存储库为最新

在完成一些本地工作后,您可能会遇到远程存储库已被其他开发人员更新的情况。
在这种情况下,您可以使用pull将远程存储库中的信息重新安装到本地存储库。

分支和存储库

每个仓库中都存储着分支。
这是实际工作完成的分支。

图片描述

另一方面,本地仓库拥有远程仓库的复制分支。
这被称为“远程跟踪分支”。
它是一个名称与远程分支绑定的分支remotes/<remote branch>

这只是监控远程存储库。

图片描述

查看最新状态

假设您遇到这样一种情况:develop远程存储库中的分支比远程跟踪分支领先一步。

图片描述

将远程存储库中分支的最新状态反映到远程跟踪分支上称为fetch

图片描述

更新至最新状态

如果您希望它反映在本地分支中,您可以执行pull
当您 时pull,本地远程跟踪分支将首先更新。
然后更新merge到本地分支。

图片描述

这一次,有一个提交比分支领先一个分支develop,因此您merge在本地develop分支中创建了一个新的提交。

处理拉取冲突

当远程仓库的提交与本地仓库的提交发生冲突时,您将面临conflict远程跟踪分支和本地分支之间的冲突pull
在以下情况下,remotes/developdevelop分支发生冲突。

图片描述

由于pushfetch且,您可以按照merge相同的方法进行求解 这次,,因此工作分支是 打开导致问题的文件夹,并在修复问题后进行操作。conflictmerge
develop merges remotes/developdevelop
commit

(题外话)拉取请求的身份

远程和本地的关系基本上是从远程存储库拉取到本地存储库,再从本地存储库推送到远程存储库。
然而,GitHub 和其他服务有一种机制,在从远程存储库的分支合并到主分支(例如主分支)之前会发送请求。
这是因为如果开发人员推送到主分支并更新远程存储库,则没有人可以检查它,并且可能会发生重大故障。
Pull request就是插入一个由更高级别的开发人员审查一次代码的流程。

图片描述

(信息)

pull: fetch+ merge.pull是为了在本地存储库中反映远程存储库的状态。

实用函数

更正提交

更正commit先前的提交称为revert
例如,假设您second.txt使用 向本地存储库添加了m9sgLe

当您 时revert,提交将被撤销并且second.txt不再位于本地存储库中。

图片描述

的优点revert是它允许你离开commit
这与 的区别reset,稍后会介绍。

删除提交

撤消当前最新提交并再次对其进行处理称为reset

--soft选项允许您立即返回到 之后的阶段add
--mixed选项允许您返回到工作目录中之前工作的阶段。
--hard <commit>选项将删除到您要返回的提交点之前的所有提交,并移动head到指定的提交。

图片描述

由于会reset 完全删除提交,因此建议您不要使用它,除非您有充分的理由,尤其是对于“--hard”选项。

如果您想恢复您的提交,您可以使用git reflog查看已删除的提交。

撤离工作

由于如果存在更改文件,您就无法移动到其他分支,因此您必须选择是继续更改commit还是放弃更改。
这时就stash派上用场了。
您可以暂时撤离工作目录或暂存区中的文件。

图片描述

当您想要移动到另一个分支时,stash以及当您返回时,使用它stash pop来检索撤离的文件并恢复工作。

带来提交

将任何提交带到当前分支以创建提交称为cherry-pick
这是一个非常好的功能。

图片描述

例如,当您只想恢复分支中以前实现的功能并将其用于当前分支中的工作时,可以使用功能featuredevelop

掌握 HEAD

我解释过,HEAD 是指向你当前正在处理的分支的指针。
我还解释过,分支是指向提交的指针。

参见下图。

图片描述

HEAD 指向develop分支 ,develop分支 指向 提交eaPk76
所以,在这种情况下, HEAD 指的是 提交eaPk76

HEAD你是否经常看到 Git 文档或文章在命令后使用?
例如git revert HEAD
这是一个可以实现的命令,因为你可以HEAD用 commit 替换它。

结尾

无需 Git 的源代码管理

Mercurial 的历史与 Git 相同。Mercurial
的命令行界面 (CLI) 非常简单,牺牲了 Git 的灵活性。
最近,Meta 基于 Mercurial 开源了一个名为 Sapling 的新源代码管理系统。
我想再次尝试一下,并写下我的使用感受。

远程存储库在哪里

托管服务是一种为远程存储库租用服务器的服务。
典型的例子是 GitHub、Bitbucket 和 AWS Code Commit 等供私人使用的服务。Git
和 Git Hub 完全不同。
顺便说一下,如上所述,我们可以将自己的服务器用于远程存储库。

指针

如果你接触过直接处理内存的编程,比如 C 语言,你大概会知道“指针”是什么。
然而,对于一个编程新手来说,它似乎很模糊。

我说了提交对象是存储在仓库中的,
如果仓库中有很多提交对象,该如何选择自己想要的那个呢?

我们需要一个标签(地址)来定位特定的提交对象。

图片描述

“指针”是指向标签的宝贵数据,以便我们不会忘记它。

顺便说一下,标签通过 转换为一个神秘的字符串hash function
如果您好奇,请参阅Git 如何计算文件哈希值?

进一步了解 Git

这篇文章中有很多事情我没有提到。

  • Git 的核心是一个简单的键值类型数据存储
  • Git 对象的详细信息,即值
  • 如何与每个对象关联。

我希望有一天能够彻底探索这一点。

参考

文章来源:https://dev.to/nopenoshishi/understanding-git-through-images-4an1
PREV
前端生产力提升:Cypress 作为主要开发浏览器
NEXT
使用 Ollama、vLLM 或 Transformers 在本地安装 DeepSeek-R1 的分步指南