在 git rebase 期间如何引用当前编辑的提交的原件?

How can I reference the original of a currently edited commit during git rebase?

我想在 git rebase -i 期间获取有关当前编辑提交的原始信息的一些信息,但 HEAD 引用了不同的提交哈希。如何引用原始提交?

示例:

$ git rebase -i upstream

(选择 "edit" 进行提交)

[detached HEAD befa32f] Previous commit message
 1 file changed, 121 insertions(+), 1 deletion(-)
 rewrite README.md (100%)
Stopped at 9a1e25391e5a53965c80dc69e1285dba7c59f893... Current commit message
You can amend the commit now, with

        git commit --amend

Once you are satisfied with your changes, run

        git rebase --continue

现在我想引用 9a1e25391e5a53965c80dc69e1285dba7c59f893 但 HEAD 具有不同的哈希值和不同的属性,尽管它似乎是相同的提交:

$ cat .git/HEAD
1414539371fe6b122d5f326bc7b344eea761dc50

这是从哪里来的,我怎样才能引用原始提交?

就其价值而言,根据定义 HEAD 总是 "current commit"——或者说 "the current commit is whatever HEAD says it is" 可能更准确.

至多 "normal" 次,HEAD 包含对分支名称的引用:例如,如果您 cat .git/HEAD(或在文件查看器或编辑器中打开它),您您会看到 ref: refs/heads/masterref: refs/heads/develop,具体取决于您所在的分支。在这种情况下,您位于指定的分支上,当前提交是该分支的尖端。1

如果您使用 git checkout --detach 或给 git checkout 原始提交 SHA-1,另一方面,git 会让您进入 "detached HEAD" 模式。虽然这听起来像是 18 世纪法国大革命的场景,但它只是意味着 HEAD 直接指代原始 SHA-1。在这种情况下,SHA-1 是当前提交。

当你 运行 git rebase -i 并让它在提交时停止(通过将处置设置为 edit),rebase 使你处于 "detached HEAD" 模式,当前提交—a.k.a。 HEAD—作为您现在可以编辑的提交。此时,您可以对树进行一些更改并执行 git add,例如:您对树所做的更改然后 add 现在位于临时区域中。如果您接下来要做的是 git commit --amend,git 进行新提交,其父级与当前提交的父级相同:

       old   <-- HEAD before `git commit --amend`
      /
...--o
      \
       new   <-- HEAD after `git commit --amend`

请注意,在 git commit --amend 之后,HEAD 仍然是 "the current commit"(根据定义),它不再是您要更改的提交(上面的 old ).

在这一点上,没有一种正确的方法可以通过名称引用 old 的 SHA-1,但是有几种方法可以找到它:例如 git log --graph --decorate --oneline HEAD ORIG_HEAD,将显示可以从 HEAD(你现在所在的位置,提交 new)和 ORIG_HEAD(记录你第一次启动 rebase 时的位置:这不一定是提交 old 但它将在其历史记录中提交 old)。

例如,当停在 rebase -i HEAD~2 时,我添加了一个新文件并使用 git commit --amend 提交了它,结果是:

$ git log --graph --decorate --oneline HEAD ORIG_HEAD
* 9daa414 (HEAD) mod1, amended
| * bd49ea7 (master) mod2
| * f071da2 mod1
|/  
* 28c4ee8 initial

或者您可以依赖 git 内部结构(随着时间的推移发生了变化,因此不能保证这对您有用):

$ ls .git/rebase-merge
amend                   git-rebase-todo.backup  onto
author-script           head-name               orig-head
done                    interactive             patch
end                     message                 quiet
git-rebase-todo         msgnum                  stopped-sha

在这里,rebase-merge/stopped-sha 具有我停止编辑时当前提交的 SHA-1,因此:

$ git rev-parse rebase-merge/stopped-sha
f071da26a90f14945d89ccdeed3c278e9b499a2e

一样,查找 old 的最简单方法可能是使用 git 的引用日志。由于我们刚刚创建了提交 new 并移动了 HEAD,旧提交 old 的 SHA-1 只是 HEAD@{1}。当然,如果您进行多次提交(例如,多次 --amend 试图使其完美),2 old 可能低于 HEAD{2}HEAD@{3},或更高。

请注意,此时,当我执行 git rebase --continue 时,git 所做的是应用剩余的项目(在上面的 ls 输出中的 rebase-merge/git-rebase-todo ) 到当前 HEAD。如果一切顺利,rebase 做的最后一件事是将当前分支(在 rebase-merge/head-name 中,在本例中包含 refs/heads/master)重新指向最尖端的提交made,然后恢复HEAD的间接引用状态,这样我现在就在分支的新提示master:

$ git rebase --continue
Successfully rebased and updated refs/heads/master.

1或者,作为一种特殊情况,如果 HEAD 包含对分支的引用但该分支尚不存在,则表示您在一个"unborn branch"。在这种情况下,没有 no 当前提交,但是当您进行新提交时,它将是新的、现在诞生的分支上的初始提交。通常人们只会在 git init 之后在新存储库中创建第一个提交时看到这一点。但是,在 git 版本 1.7.2 及更高版本中,使用 git checkout --orphan <branchname> 创建一个新的未出生分支,以便您可以创建一个新的历史记录,使其与现有存储库中的现有历史记录无关。

2我不能代表任何人说话,但我一直这样做。 :-)