0%

一件关于Git分支的神奇小事 :为什么我的 `master` 能推送到并且持续更新 `main`?

一件关于Git分支的神奇小事 :为什么我的 master 能推送到并且持续更新 main?


我:我在学习用 VSCode 使用 Git,用到我的 Hexo 博客的例子。可以看到,源代码管理里主要有三个地方,分别是存储库、更改和图表。图表里面显示了我的 commit 记录,这里可以很清楚的看到我每一次更改的东西。我不太懂的是存储库和更改。为什么我有两个存储库,my-blog01 存在 main 分支,而 .deploy_git 存在 master 里。这两个是什么时候,在哪个实验设置的?另外,这里的更改和我之前一直在 Git Bash 终端里用的命令对比,有什么相同和不同之处?

朋友:看到你的截图,我明白了。你看到的这两个仓库(my-blog01 和 .deploy_git),正是 Hexo 博客部署机制的核心。它们不是“两个仓库”,而是“两个独立的东西”,我来帮你理清。

  • **my-blog01 (源码仓库)**:这就是你在终端里运行 git init 建立的那个仓库。它存放的是你所有文章的 .md 源文件、主题配置、插件等。你手动写的所有 commit 都在这个仓库里,是你的工作核心。
  • **.deploy_git (部署仓库)**:你完全不需要手动操作它。它是当你第一次运行 hexo d 命令时,Hexo 在本地自动生成的一个“匿名”仓库。它的作用,是把 Hexo 编译生成的静态网页文件推送到 GitHub 的 你的用户名.github.io 仓库里。它通常固定使用 master 分支,因为这是 GitHub Pages 服务默认读取的分支。

至于和 Git Bash 命令的区别,它们本质上完全一样,只是操作的对象和目标不同。一个是图形界面,一个是文字命令。关键在于你在哪个目录下打开 VS Code。如果你打开的是 my-blog01 文件夹,那源代码管理界面的所有操作,都等同于在这个目录下执行 Git 命令。

我:但是我的 config 文件里现在是这样设置的:

1
2
3
4
deploy:
type: 'git'
repo: https://github.com/tristan1116/tristan1116.github.io.git
branch: main

朋友:看到你的配置了!这和你的截图对上了。你配置中写的 branch: main 并没有生效,.deploy_git 仓库目前实际使用的是默认的 master 分支。这通常是因为 .deploy_git 是在你第一次运行 hexo d 时被自动创建的,当时你电脑的 Git 全局配置可能默认使用 master 作为初始分支名。

我:我去 GitHub 上看了设置,显示:

Your GitHub Pages site is currently being built from the main branch

朋友:太好了!这个信息非常关键。你的 GitHub Pages 正在从 main 分支构建,这意味着你的 _config.yml 中写的 branch: main 是正确的。但是,你本地的 .deploy_git 仓库却在 master 分支上。

这会造成一个“版本错位”:虽然你配置想推送到 main,但 Hexo 实际推到了 master。而 GitHub Pages 只读取 main,所以你的博客现在很可能没有更新到最新内容。

我:但是神奇的是,我的本地版本和网页版本是同步的,我每次 push,都能顺利更新。这其中的具体原因是什么?

朋友:确实很神奇!你能成功更新,说明这个“错位”并没有影响你,这背后有具体的技术原因。

关键在于,你的 .deploy_git 仓库的 master 分支,其实和远程仓库的 main 分支是同一个东西。当你运行 hexo d 时,它执行的是 git push。由于你的本地分支是 master,而远程仓库的 main 分支和 master 分支在 Git 内部是通过提交历史(commit)关联的。因为你在本地 master 上的新提交,在远程 main 上并没有冲突,所以推送成功。

我:我之前并不了解 Git 分支这种知识,只会 git add、commit 和 push 三板斧。所以对你的解释,我有点晕了。你能否从更基础的东西开始讲?比如介绍一下 Git 分支的基本逻辑什么的?

朋友:没问题!我们从头开始,用最通俗的方式把 Git 分支讲清楚。

想象你在写一本日记,每天写一篇。Git 就是帮你管理这本日记的工具。

  • git add:相当于你把今天写好的草稿放到了书桌上。
  • git commit:相当于你正式把这篇日记誊写到了日记本上。
  • git push:相当于你把这本日记本的内容,复印了一份寄给了朋友(远程仓库)。

分支可以理解成时间线的“分叉”。假设你的日记写得很好,你想尝试一个新风格,但又不想影响原来的日记主线。这时候,你就可以复制一本新的日记本,在新本子上随便写。这本“新日记本”就是 Git 里的分支。

我:我对于前两件事基本理解了,但对于第三件事(为什么错位还能推送成功)还是感到很神奇。我想进一步了解一下这其中的原理。

朋友:好的,觉得“神奇”就对了,这说明你已经开始触碰到 Git 最核心的设计思想了。我们把这个“魔术”的底牌掀开。

其实原理并不复杂,但需要跳出“分支等于文件夹”这个直观印象。在 Git 的内部,分支其实非常轻量,它仅仅是一个“贴纸”或“书签”,贴在某次提交(commit)上面。

  • 提交(Commit):这才是 Git 真正保存内容的地方。每次 commit,Git 都会把当前所有文件的状态打包,生成一个独一无二的 ID。
  • 分支(Branch):它只是一个指向某个 commit ID 的指针。

所以,你的本地 master 和远程 main,虽然名字不同,但它们指向的是同一个提交历史。git push 操作的对象是提交(commit),而不是分支名。分支名只是为了让人们方便地找到这些提交。

我:我好像理解了一点,但又没完全理解。我是否可以这么说,我目前之所以每次从本地提交到 GitHub 上去都能顺利在网站上同步,是因为这完全是线性的,只有我一个人在提交,每一次只有一个新的 commit 从我的 master 出发,到达 GitHub 上的 main。所以它们二者恰好能够完美对上?如果是这样的话,关于你的问题,是不是会出现,main 和 master 不能再完美契合的情况?但具体是什么样,我说不出来。

朋友:完全正确! 你不仅理解了,而且抓住了最核心的一点。你的这个推断非常精准。

“我目前之所以每次都能顺利同步,是因为这完全是线性的,只有我一个人在提交,每一次只有一个新的 commit 从我的 master 出发,到达 github 上的 main。所以它们二者恰好能够完美对上?”

是的,就是这个原因!

“如果是这样的话,关于你的问题,是不是会出现,main 和 master 不能再完美契合的情况?”

是的,一定会! 只要远程仓库的 main 分支,在你上次推送之后,又有了新的、你本地没有的提交,那么“完美契合”就会被打破。这种“新的提交”可能来自:你自己在另一台电脑上提交并推送了,或者别人推送了新的提交。

当这种情况发生时,Git 会拒绝你的 push,并提示:

1
error: failed to push some refs to ...

你需要先 git pull 把远程的更新拉取到本地,解决可能的冲突,然后再推送。

我:哈哈,我终于理解了,Git 确实很神奇,也很好玩!

朋友:哈哈,太好了!能帮你把这个“神奇”的点打通,我也很开心!你总结得特别到位——Git 确实很神奇,也很好玩。它就像一个精密的时光机,不仅帮你记录每一次变化,还给了你穿梭于不同时间线(分支)的自由。一旦理解了它的核心逻辑,用起来会特别顺手,而且很有成就感。


后记:这次对话让我对 Git 分支有了本质的理解。从最初只会“三板斧”,到后来理解分支是指针而非复制,再到明白了为什么我的 master 能成功推送到 main,整个过程就像解开了一个有趣的谜题。记录下来,既是对这段学习经历的纪念,也希望能帮到其他遇到类似困惑的朋友。