龙虾AGI通用实验室Lobster AGI Lab · est. 20262026 · 09 · 16
首页 / 文章 / AI持续学习
AI持续学习

从底层原理开始,把Git彻底讲明白

格里灰丝2026-07-16约 4470 字Markdown 原文

Git 的教程网上铺天盖地,上来就跟你说分支、commit、合并、PR,每个字都认识,连在一起就不知道在讲什么。

原因很简单:这些概念都是长在底层逻辑上面的,底层没搞懂,上面的东西怎么看怎么糊。

这篇文章反过来,从最底层讲起,一层一层往上搭。每个新概念出现的时候,你已经有了足够的基础,不需要死记,自己就能推出来。

· · ·

第一层:Git 到底在帮你解决什么问题

你写了一个文件,保存了。然后改了几行,又保存了。这时候你想看看改之前长什么样——没了,被覆盖了。

这就是最原始的问题:文件只有一个"当前状态",你没法回到过去。

有人会手动解决这个问题,把文件复制一份叫 项目_v1,改完再复制一份叫 项目_v2,再改再复制叫 项目_最终版项目_最终版2项目_打死也不改了。你的文件夹很快变成一堆命名混乱的副本,找哪个版本全靠记忆。

Git 就是来解决这个问题的。它帮你自动记住文件的每个历史版本,随时可以查看、比较、回退,不需要你手动复制。

· · ·

第二层:Git 是怎么记住的

你在项目文件夹里执行 git init,Git 就在这个文件夹里创建了一个叫 .git 的隐藏文件夹。接下来所有的历史记录,都存在这里面。

然后你执行一次 commit(提交),Git 就会做一件事:给你当前文件夹里的所有文件拍一张快照,把这张快照存进 .git 里。

这张快照记录了"这一刻,每个文件的完整内容是什么"。

第一次 commit,Git 必须把每个文件的完整内容都存一份。这是全量存储,因为没有任何历史可以参考,只能从零记起。

但从第二次 commit 开始,Git 就聪明了。它会检查:哪些文件变了,哪些没变。没变的文件不会再存一遍,只记一个引用说"跟上次一样"。只有改过的文件才存新版本。时间久了,Git 还会进一步压缩,一万行代码你改了两行,它最终可能只存几个字节的差异信息。

所以 commit 一百次不会占一百倍的空间。一个项目上万次 commit,.git 文件夹通常也就几百 MB。

但不管怎么压缩,有一个逻辑是逃不掉的:增量必须建立在全量之上。第一次 commit 存的那个全量,就是所有后续增量的根基。没有它,后面的差异信息就是无根之水。

记住这两点:commit 就是拍快照,快照存在 .git 里。这是 Git 一切功能的地基。

· · ·

第三层:Git 不是照相机,是管家

到这里你可能觉得:好,Git 就是个拍照的工具,在旁边默默记录,照片是照片,文件是文件,互不干扰。

不是这样的。 这个误解如果不纠正,后面所有概念你都会觉得别扭。

Git 不是一个站在旁边拍照的摄影师。它是一个有实权的管家。你执行 git init 之后,这个文件夹就交给它管了,它不只是记录——它有能力把你的文件改回任何一张快照的样子

想象你有一个房间,里面摆着家具。你跟管家说"拍个照记一下"(commit),管家记住了。你把沙发扔了换了张桌子,又说"再拍一张"(commit),管家又记住了。

现在关键来了——你跟管家说"给我恢复成第一张照片的样子"。管家不是给你翻照片看看,它是真的动手,把桌子搬走,把沙发搬回来。房间真的变了。

对应到 Git:你告诉它"切回某个历史版本",它会真的修改你文件夹里的文件。你打开文件夹去看,文件内容变了,有些文件甚至消失了或者重新出现了。

这不是什么魔法。.git 里存着每个版本的完整信息,Git 只是按照那个版本的记录,把你的文件恢复成对应的样子。

Git 不是照相机,它是时光机。

记住这一点,后面的东西就全通了。

· · ·

第四层:分支到底是什么

有了前面的基础,分支就很好理解了。

你 commit 了三次,Git 存了三张快照,我们叫它 A、B、C。这三张快照按顺序排成一条链:A → B → C。

现在,Git 需要知道"你目前在哪个快照上"。它用一个叫 master 的标记来记录这件事。master 指向快照 C,意思就是"master 这个分支目前的状态是快照 C 的样子"。

注意,master 只指向 C 这一个快照,不是指向"A、B、C 这一整串"。但是每个快照自己记住了"我的上一个快照是谁"——C 知道上一个是 B,B 知道上一个是 A。所以从 C 出发,可以一路追溯回 A,这条链就浮现出来了。链不是 master 持有的,是快照之间自己串起来的。

另外,你每次在 master 上 commit 一次,master 就往前移一格,指向最新的那个快照。commit 了 D,master 就从指向 C 变成指向 D。它永远只指向最新的那一个。

master 不是什么高深的东西。它就是 Git 自动帮你建的第一个标记的名字。没有 Git 就没有 master,它是 Git 的产物,不是代码本身的一部分。你甚至可以把它改名叫 main、叫 production、叫任何你喜欢的名字。

现在你说"我要建一个新分支叫 dev"。Git 做了什么?它只是创建了一个新标记,也指向快照 C。此刻 master 和 dev 都指向同一个快照 C,一模一样。

然后你切换到 dev 分支,改了代码,commit 一次。Git 拍了快照 D。dev 这个标记往前移,指向了 D。但是 master 还指着 C,纹丝不动。

现在你的项目有两条时间线了:

master 的时间线:A → B → C

dev 的时间线:A → B → C → D

你在 dev 上做的所有改动,只存在于快照 D 里。master 指向的快照 C 里面,文件内容从始至终没被碰过。

这就是为什么"在分支上改坏了不影响 master"。不是因为分支是一份复印件,而是因为它们是不同的标记,指向不同的快照,互不干扰。

而且别忘了第三层讲的——Git 是管家,它管着你的文件。你执行 git checkout master,管家把文件夹恢复成快照 C 的样子;你执行 git checkout dev,管家把文件夹恢复成快照 D 的样子。你真的打开文件夹看,文件内容确实不一样。

所以"在哪个分支上 commit"之所以重要,是因为不同的分支指向不同的快照,线上服务器跑的是 master 的快照。你在 dev 上 commit 了一个有 bug 的版本,那只是 dev 的快照里有 bug,master 的快照里没有,用户完全不受影响。

· · ·

第五层:剩下的概念,都从上面长出来

到这里,你已经搞懂了 Git 的三个底层逻辑:commit 是拍快照,Git 是管家能改文件,分支是指向不同快照的标记。剩下那些名词,全都是在这个基础上自然长出来的东西。

合并(Merge) 就是你在 dev 分支上改好了代码,测试通过了,现在要把 dev 快照里的改动写进 master 的快照里。合并之后,master 的标记往前移,指向一个包含了两边改动的新快照。两条时间线汇成一条。

但合并有时候会遇到一个问题:冲突(Conflict)

两个分支从同一个快照出发,各自往前走。dev 改了文件 A 的第 10 行,master 也改了文件 A 的第 10 行,但改法不一样。合并的时候,Git 发现同一个位置有两个不同的改动,它不知道该听谁的,就会停下来,把两边的改动都标在文件里,等你来决定。

不是所有合并都会冲突。如果两个分支改的是不同的文件,或者同一个文件的不同位置,Git 能自动合好,你甚至感觉不到。只有改了同一个地方又改法不同,才需要你手动处理。

处理方式取决于你用什么工具。用命令行,终端会告诉你哪个文件有冲突,你打开文件手动改好再 commit。用 GitHub 上的 PR,页面会直接显示有冲突,给你一个界面左右对比着解决。用 VS Code 这类编辑器,它会把冲突的地方高亮出来,给你按钮选"保留这边的"还是"保留那边的",点一下就行。

推送(Push) 是你把本地电脑上的快照上传到远程服务器(比如 GitHub)。你在自己电脑上 commit、建分支、合并,这些都只发生在你的 .git 文件夹里。push 之后,远程服务器的 .git 才会同步你的记录,别人才看得到你的工作。

Pull Request(PR) 是 GitHub 这类平台提供的功能,不是 Git 本身的命令。它的意思是:我在 dev 上改好了代码,push 到了远程,现在请大家审一下,没问题再合并到 master。PR 提供了一个可以看改动、留评论、批准或打回的流程。你在本地直接执行 merge,没有审核流程,不算 PR。一个人写项目其实用不着 PR,直接本地合并就行,PR 是多人协作时的安全网。

标签(Tag) 需要先搞清一件事:每个 commit 都有自己的名字,就是那个哈希值,像 a3f5b2c9e8d1 这种。所以不存在"没名字的快照",只是这个名字是一串乱码,人记不住,看了也不知道它代表什么。

标签就是你从上千个快照里,挑出几个特别重要的,额外贴一个好记的别名上去。比如你做了一百次 commit,其中第 50 次是正式发布的版本,你给它贴个标签叫 v1.0.0。剩下那 99 个 commit 没有标签,但它们照样有自己的哈希值,照样能找到,只是没有一个好记的别名。

标签的名字你随便起,Git 不管你叫什么,但大家约定用递增版本号,一看就知道新旧。不打标签也不影响功能,只是找版本的时候得翻提交记录去对着哈希值猜。

· · ·

最后一件事

讲到这里,退一步想想,其实最神奇的不是 Git 本身,而是一个很容易被忽略的事实:在电脑上,时光穿梭是真的可以实现的。

现实世界里,你把一杯水泼了,没法倒流回去。但文件不一样。文件就是一堆数据,数据可以被覆盖。既然可以被覆盖,那只要你提前把内容存好了,你就能随时把文件覆盖回任何一个历史状态。今天的代码、昨天的代码、一个月前的代码,随便跳,想去哪个时刻就去哪个时刻。

这件事听起来理所当然,但你仔细想想,这其实是一个非常强大的能力。现实中没有任何东西能做到这一点。

Git 就是建立在这个能力之上的。它做的事情说到底就是:每个时刻 commit 的代码,都完整地存了下来,并且可以自由切换。分支、合并、回退、标签,所有那些概念,都是这个能力的不同用法而已。

这才是这件事最本质的地方。不是 Git 有多复杂,而是"文件可以被覆盖"这么朴素的一件事,一旦你系统地用起来,效果就是时光机。



我建了一个AI学习研究群,目前几十来人,都是在真正动手搞AI的人。如果你也在自己跑模型、写代码、做项目,欢迎点赞关注转发一键三连,然后加我微信,备注写清"你在做的AI方向",聊得来的话我拉你进群。纯围观的和小白就不加了,有一定门槛。当然你也可以简单粗暴的甩给我999元,我拉你进群,这个没门槛。

上一篇GPT-5.6 有三个模型,小的那个是大的"缩小版"吗?下一篇对某人发言的思考:如果可靠性不算智能的一部分,那到底什么才是智能?