git commit和git commit-tree有什么区别?

14
我正在阅读关于git对象的内容:blob、tree、commit、tag。为了更好地理解git是如何工作的,我尝试了一些低级命令,例如write-treecommit-tree
  1. mkdir test; cd test --> git init
  2. 创建一个文件并使用git add file。我可以看到在.git/objects中生成了一个blob和tree对象。
  3. git write-tree打印当前treeID。
  4. git commit-tree treeID -m "commit a tree"来提交这个tree。在此操作后,会生成一个commit对象,我可以看到它包含作者、日期等信息。然而,我无法使用git log查看我的提交,错误是:fatal: bad default revision 'HEAD'
在执行上述操作后,当我运行git status时,我发现该文件仍位于索引中等待提交。commit-tree的用途是什么?commit-tree和`commit'之间有什么区别?

2
你读过《Pro Git》书中的这一章节吗?它应该会对你有所帮助,因为它详细介绍了如何使用“Plumbing”(即低级别的Git)命令来创建提交。 - jub0bs
1
你可能应该编辑你的问题并添加你使用的确切命令序列;这会让这里的人更容易找出其中的问题所在。 - jub0bs
2个回答

8

git-commit - 记录对代码库的更改

将当前索引的内容与用户描述更改的日志消息一起存储在新提交中。

git commit "记录对代码库的更改"

下图是 git-commit 的示意图,可在此处查看。

git-commit-tree - 创建新的提交对象

基于提供的树对象创建新的提交对象,并在 stdout 上输出新的提交对象 ID。

通常情况下,这不是终端用户想要直接运行的。它会基于提供的树对象创建新的提交对象,并在 stdout 上输出新的提交对象 ID。除非给出 -m-F 选项,否则日志消息将从标准输入读取。


1
git commitgit commit-tree均可创建提交对象并接受用户消息,但是git commit会在索引HEAD上拍摄快照,而git commit-tree不能保证提交对象来自HEAD索引,它可以来自整个树结构的任何(子)树,我说得对吗?因此,在进行了git commit-tree之后,如果我有一个API将某个提交放在HEAD上,那么结果就像是git commit一样吗? - hakunami
3
这个回答在技术上是准确的,但我认为它没有强调一个非常重要的点。git-commit-tree 是一个底层命令,用于提交单个树对象,但不执行 git-commit 所执行的任何后续参考和 Head 工作。如果您手动执行 git commit-tree 命令而没有进行后续参考工作,那么很容易创建一个无法到达的树,这将被 Git 垃圾清理器删除。有关更多信息,请参见《从下往上的 Git》第 8-10 页。 - user2858650

1

git-commit(1)是您几乎所有时候都要使用的高级命令。 git-commit-tree(1)是一个较低级别的命令,不是日常使用的命令。

当您进行交互式提交新更改时,使用git-commit(1)。但我想我不必详细说明。

在以下情况下,git-commit-tree(1)非常有用:

  1. 想要明确选择提交的父项
  2. 已经为提交准备好了

假设我想有效地将存储库中的所有提交“压缩”成单个提交。但是,“压缩”是一种合并操作,因为它组合了更改,而我不需要组合任何更改,因为我已经知道我想要的快照-当前的快照。所以:[1] [2]

$ git commit-tree -m Init HEAD^{tree}
1a875b50b508c70e44a4e6300fd80f580aed3d99

(这变成了一个无父提交,因为我没有用-p指定任何父提交。)

那是提交的SHA1值。最好将它放在一个分支上,这样垃圾回收器在几个月后不会将其清除。

$ git branch new-start 1a875b50b508c70e44a4e6300fd80f580aed3d99

你可以通过使用已有的提交和它们的树来创建任何你想要的历史记录。例如,假设我想创建一个仅包含版本v1.0.0v2.0.0v2.40.0的Git存储库历史记录,并称其为small-git分支:

git clone https://github.com/git/git/ git-repo
cd git-repo
first=$(git commit-tree -m v1 v1.0.0^{tree})
second=$(git commit-tree -p $first -m v2 v2.0.0^{tree})
third=$(git commit-tree -p $second -m v2.40.0 v2.40.0^{tree})
git branch small-git $third

您可以使用以下方法进行验证(所有差异应为零):

$ # While on `small-git`
$ git diff v2.40.0
$ git diff @^ v2.0.0
$ git diff @^^ v1.0.0

注释

  1. 请参考 man gitrevisions 中的 ^{tree} 语法。
  2. 还有其他方法可以实现这个目标,例如 git checkout --orphan new-start && git commit -m Init 等。因此,并不是必须使用该命令来完成。

我最好检查一下并将其放在一个分支上。不,你不需要仅仅为了“将其放在一个分支上”而检出提交。 - matt
请停止使用 checkout,改用 switchcheckout 已经退役了。 - matt
好的,我已经删除了“检出分支”的部分。谢谢。 - Guildenstern
我现在已经放弃了常规的 git checkout(而不是 checkout --orphan)用法,以避免争议。 ;) - Guildenstern

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接