如何从Git存储库中删除所选提交日志条目,同时保留其更改?

248

我想从线性提交树中删除所选的提交日志条目,使得这些条目不会显示在提交日志中。

我的提交树看起来像这样:

R--A--B--C--D--E--HEAD

我想要删除B和C条目,以便它们不在提交日志中显示,但是从A到D的更改应保留。也许通过引入单个提交,使得B和C变成BC,并且树形结构看起来像:

R--A--BC--D--E--HEAD

或者更理想的是,A后面直接跟着D。其中D'表示从A到B、从B到C和从C到D的变化。

R--A--D'--E--HEAD

这可行吗?如果是,怎样实现?

这是一个相当新的项目,目前没有任何分支,因此也没有合并。


“Commits” 是这里的正确术语。rebase 可能会删除旧的/创建新的提交。我不知道“commit log entries”是什么意思。 - jfs
@J.F.Sebastian 我认为“提交日志”没有问题 - 记录所有提交。我想从日志中删除一些条目 - 同时保留实际更改(提交)。 - xk0der
@xk0der:git提交是内容可寻址的,也就是说,如果您更改提交中的任何内容,例如其日志消息,则会创建一个新的提交。您可以在没有git的情况下阅读git的提交并自行查看 - jfs
@J.F.Sebastian - 感谢提供的链接 - 我知道这些 - 但是这个技术细节真的改变了我所面临的问题以及我如何表达它吗?我想不是。最终:我想要删除“提交日志消息” - 而不是删除“提交更改” - 请重新阅读我的问题 - 特别是第二段。另外,git log 显示“提交日志”http://git-scm.com/docs/git-log。我想从该日志中删除两个条目 - 而不是更改。 - xk0der
9个回答

276

git-rebase(1) 正是这样做的。

$ git rebase -i HEAD~5

Git 的巨大优势 [git rebase --interactive] 包含一个例子。

  1. 不要对公共(远程)提交使用 git-rebase
  2. 确保你的工作目录是干净的 (commitstash 当前的更改)。
  3. 运行上述命令。它将启动你的 $EDITOR
  4. CD 前用 squash 替换 pick ,这将使 C 和 D 融合成 B。如果你想删除一个提交,只需删除它的行。

如果你不知道该怎么做,请输入:

$ git rebase --abort  

6
@slhck:http://replay.web.archive.org/20090627051320/http://blog.madism.org/index.php/2007/09/09/138-git-awsome-ness-git-rebase-interactive - Benjol
3
我们如何在远程代码库上操作? - Eray
7
@Eray:只需使用push -f推送您的更改即可。如果您不是独自工作,请勿这样做。 - jfs
2
@ripper234:我已经修复了博客文章中指向git-rebase手册和wayback机器的链接。 - jfs
<funfact> 运行 "git rebase -i HEAD~5" 命令,然后在 vim 中按下 :q 可以移除 --no-ff "Merge from..." 提交。 </funfact> - billrichards
显示剩余8条评论

76
# detach head and move to D commit
git checkout <SHA1-for-D>

# move HEAD to A, but leave the index and working tree as for D
git reset --soft <SHA1-for-A>

# Redo the D commit re-using the commit message, but now on top of A
git commit -C <SHA1-for-D>

# Re-apply everything from the old D onwards onto this new place 
git rebase --onto HEAD <SHA1-for-D> master

这也可以运行,并帮助我理解什么是软重置。尽管“顶部”答案也是正确的且更短,但还是感谢这个答案。 - cgp

41

以下方法可以根据知道的提交记录ID来删除特定的提交记录。

git rebase --onto commit-id^ commit-id

请注意,这实际上移除了由提交引入的更改。


7
这个命令中多余的 HEAD 参数会导致变基操作以 'detached HEAD' 的方式结束,这是不希望看到的。应该省略它。 - Frosty
3
这会撤销提交ID引入的更改,OP希望保留这些更改,只需压缩提交记录即可。 - CB Bailey
1
-1 是因为它没有做 OP 要求的事情(相反,它破坏了他明确想要保留的东西)。 - Emil Styrke
1
虽然它没有做到 OP 所要求的,但正是我所需要的,因此对于一个有用的答案 +1。 - Edvins

20

进一步解释J.F. Sebastian的答案:

您可以使用git-rebase轻松地对提交历史进行各种更改。

运行git rebase --interactive后,您会在$EDITOR中看到以下内容:

pick 366eca1 This has a huge file
pick d975b30 delete foo
pick 121802a delete bar
# Rebase 57d0b28..121802a onto 57d0b28
#
# Commands:
#  p, pick = use commit
#  r, reword = use commit, but edit the commit message
#  e, edit = use commit, but stop for amending
#  s, squash = use commit, but meld into previous commit

你可以移动行来改变提交的顺序,删除行以移除相应的提交。或者你可以添加一个命令将两个提交(前面的提交)合并成一个单独的提交(squash),编辑提交(修改内容)或重新编辑提交消息。

我认为"pick"只是表示你想保留那个提交。

(示例来自这里


14

您可以使用以下方法在非交互式情况下从您的示例中删除 B 和 C:

git rebase --onto HEAD~5 HEAD~3 HEAD

或者用符号表示:

git rebase --onto A C HEAD

请注意,B和C的更改将不会在D中出现;它们将被删除


更多信息请参见此处:https://sethrobertson.github.io/GitFixUm/fixup.html#remove_deep - Max

3

我发现通过从A的SHA1创建另一个分支,并精选所需更改,可以使这个过程更安全、更易理解,这样我就可以确保满意新分支的外观。之后,轻松地移除旧分支并重命名新分支。

git checkout <SHA1 of A>
git log #verify looks good
git checkout -b rework
git cherry-pick <SHA1 of D>
....
git log #verify looks good
git branch -D <oldbranch>
git branch -m rework <oldbranch>

如果这样做,您将失去E提交记录,不是吗?我的理解是,您要删除master分支,并将rework分支重命名为master(考虑ABCDE流是主分支)。 - Renan Bandeira

3

还有一种方法,

git rebase -i ad0389efc1a79b1f9c4dd6061dca6edc1d5bb78a (C's hash)
and
git push origin master  -f

选择你想要用作基础的哈希值,然后使用上面的命令使其交互式,这样你就可以压缩所有顶部消息(需要保留最老的消息)。


1

我会尽力帮助您翻译以下内容,涉及编程相关内容。

使用git rebase删除任何提交

只是收集了所有人的答案:(我对git很新,请将其用作参考)

git日志

-first check from which commit you want to rebase

git rebase -i HEAD~1

-Here i want to rebase on the second last commit- commit count starts from '1')
-this will open the command line editor (called vim editor i guess)

然后屏幕将会看起来像这样:

然后屏幕将会看起来像这样:

pick 0c2236d 新增一行。

将 2a1cd65..0c2236d 变基到 2a1cd65 (1 个命令)

#

命令:

p, pick = 使用该提交

r, reword = 使用该提交,但编辑提交消息

e, edit = 使用该提交,但停止以便修改

s, squash = 使用该提交,但合并到前一个提交中

f, fixup = 类似于 "squash",但丢弃此提交的日志消息

x, exec = 使用 shell 运行命令(行的其余部分)

d, drop = 移除提交

#

这些行可以重新排序;它们从上到下执行。

#

如果您在此处删除一行,该提交将丢失。

#

但是,如果您删除所有内容,则变基将被中止。

#

请注意,空提交已注释掉 ~ ~

~
~
~
~
~
~
~
~
~

根据您的需求更改第一行(使用上面列出的命令,例如'drop'删除提交等)。

编辑完成后,按 ':x' 保存并退出编辑器(仅适用于vim编辑器)。

然后执行git push。

如果出现问题,则需要强制将更改推送到远程(非常重要:如果您正在团队中工作,请勿强制推送)。

git push -f origin


-1
你可以使用git cherry-pick来实现这个功能。'cherry-pick'命令会将一个提交应用到你现在所在的分支中。
然后执行:
git rebase --hard <SHA1 of A>

然后应用D和E提交。

git cherry-pick <SHA1 of D>
git cherry-pick <SHA1 of E>

这将跳过 B 和 C 提交。话虽如此,如果没有 B,将无法将 D 提交应用于分支,因此可能会因人而异。


2
OP想要合并B、C、D的提交,而不是删除它们的更改。 - jfs
3
我认为你的意思是“重置 --hard”,而不是“变基 --hard”(后者不存在)。 - Mauricio Scheffer

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