「Git 内核」提交实质

「这是我参与11月更文挑战的第 12 天,活动详情查看:2021最后一次更文挑战


提交在哪

几乎所有的 git 命令都需要知道当前准备提交是什么。例如,git commit 需要知道当前的提交在哪,因为它将成为马上新提交的父本。

举个例子,在我某一个仓库中,我们正处于 af64eba 提交。git 是怎么知道的?

你可能知道 HEAD 可以用来指代当前的提交。

例如,git show HEAD 显示了当前提交的提交信息和它所引入的修改。所有的 git 状态都存储在 .git 目录中,这包括告诉我们当前位置的文件:.git/HEAD

写段代码,让我们看一下它的内容。(注意:这些例子使用同步I/O,以便关注git内部。有很多机会可以并行地执行文件系统操作,所以使用异步I/O可以显著提高性能)

use std::fs;
use std::io;

const HEAD_FILE: &str = ".git/HEAD";

fn get_head() -> io::Result<String> {
  fs::read_to_string(HEAD_FILE)
}

fn main() -> io::Result<()> {
  let head = get_head()?;
  println!("Head file: {:?}", head);
  Ok(())
}
复制代码

运行该代码,会得到以下结果:

Head file: "ref: refs/heads/main\n"
复制代码

.git/HEAD 文件结果告诉我们,我们已经切出了主分支。

这是我们想知道的部分内容,但我们仍然没有弄清楚git如何知道main指的是什么提交。

事实证明,refs/heads/main 实际上是.git目录下一个文件的名字。来读读它的内容:

use std::path::Path;

const BRANCH_REFS_DIRECTORY: &str = ".git/refs/heads";

fn get_branch_head(branch: &str) -> io::Result<String> {
  let ref_file = Path::new(BRANCH_REFS_DIRECTORY).join(branch);
  fs::read_to_string(ref_file)
}

fn main() -> io::Result<()> {
  let main_head = get_branch_head("main")?;
  println!("main: {:?}", main_head);
  Ok(())
}
复制代码

结果显示:

main: "af64eba00e3cfccc058403c4a110bb49b938af2f\n"
复制代码

如果你还记得,我们创建的提交被称为:af64eba.git/refs/heads/main 以这些十六进制数字开头并不是巧合。

这个由40个十六进制数字(代表20个字节)组成的完整字符串被称为 "提交hash"。

我们很快就会看到它是如何计算的(以及它为什么被称为 "hash"),但现在你可以把它看作是提交文件的唯一标识符。

check out 操作也有可能(虽然不太常见)切出一个特定的提交,而不是一个分支。例如,我们可以运行 git checkout af64eba 来切出当前的提交。

Note: switching to 'af64eba'.

You are in 'detached HEAD' state. You can look around, make experimental
changes and commit them, and you can discard any commits you make in this
state without impacting any branches by switching back to a branch.

If you want to create a new branch to retain commits you create, you may
do so (now or later) by using -c with the switch command. Example:

  git switch -c <new-branch-name>

Or undo this operation with:

  git switch -

Turn off this advice by setting config variable advice.detachedHead to false

HEAD is now at af64eba Initial commit
复制代码

在这种情况下,.git/HEAD文件会直接指定提交hash:

Head file: "af64eba00e3cfccc058403c4a110bb49b938af2f\n"
复制代码

猜你喜欢

转载自juejin.im/post/7032267502356791309