8

您能否提供(所有或最常见的)可能破坏 git 历史记录的操作或命令的列表?

应该绝对避免什么?

  1. 在推送此提交后修改提交 ( git commit/ git push/ git commit --amend)
  2. 向已经推动的东西重新定位

我希望这个问题(如果它之前没有在其他地方问过)成为对git上常见的可避免操作的某种参考。

此外,我使用git reset了很多,但我并不完全意识到我可能对存储库(或其他贡献者的副本)造成的损害。git reset会不会很危险?

4

4 回答 4

12

请注意,从 Git 2.24(2019 年第四季度)开始,可能不再需要包含上面的列表git filter-branch

git filter-branch正在被弃用(BFG也是)

请参阅Elijah Newren ( ) 的commit 483e861commit 9df53c5commit 7b6ad97(2019 年 9 月 4 日(由Junio C Hamano 合并 -- --提交 91243b0中,2019 年 9 月 30 日)newren
gitster

推荐git-filter-repo而不是git-filter-branch

filter-branch遭受大量变相的危险,使历史被改写(即偏离故意的变化)。

其中许多问题并不引人注目,并且在使用新存储库之前很容易被发现。
这可能会导致一系列问题,从比最初导致人们遇到的更混乱的历史filter-branch,到数据丢失或损坏。这些问题无法向后兼容地修复,因此在两者及其联机帮助页中添加警告,建议改用filter-branch其他工具(例如)。filter-repo

此外,更新引用的其他手册页filter-branch
即使我们可以继续推荐 filter-branch,其中一些也需要更新,或者是因为暗示某些东西在 filter-branch更普遍地应用于所有历史重写工具(例如,、、、)时BFG是独一无二的reposurgeon,或者是因为某些东西被用作示例,尽管其他更多众所周知的例子现在存在。 改写这些部分以解决这些问题并避免推荐.fast-importfilter-repofilter-branch
filter-branch

最后,删除解释BFG Repo Cleaner作为替代filter-branch的部分。
我对此感到有些不好,特别是因为我觉得我从 BFG 学到了很多东西,我很好地利用了filter-repo(这比我能说的要多得多 filter-branch),但是保留该部分会带来一些问题:

  • 为了建议人们停止使用filter-branch,我们需要向他们推荐可以处理所有相同类型重写的其他东西。
    据我所知,filter-repo是唯一这样的工具。所以需要提一下。
  • 我不想向用户提供相互矛盾的建议
  • 如果我们推荐两种工具,我们不应该期望用户同时学习并选择使用哪一种;我们应该解释一个人可以解决哪些问题而另一个人不能解决,或者什么时候一个人比另一个人快得多。
  • BFGfilter-repo具有相似的性能
  • 所有可以做的过滤类型,BFGfilter-repo可以做。
    事实上,filter-repo它附带了BFGnamed的重新实现,bfg-ish它提供了相同的用户界面,但有几个错误修复和新功能,由于其技术基础而 BFG难以实现。BFG

虽然我仍然可以提到这两种工具,但似乎我需要提供某种比较,我最终只会说filter-repo可以做任何事情BFG,所以最终似乎最好完全删除该部分。


可能破坏 git 历史记录的操作或命令?

至少,它newren/git-filter-repo可以从任何因其使用而受损的历史中恢复。

在其既定目标中:

更智能的安全

将原始 refs 的副本写入 repo 中的特殊命名空间不提供用户友好的恢复机制。许多人将难以使用它来恢复。

几乎我见过的每个执行存储库过滤操作的人都使用新克隆来执行此操作,因为在出​​现错误时清除克隆是一种非常容易的恢复机制。
如果我们不在新克隆中,则通过检测和放弃来强烈鼓励该工作流程,除非用户使用--force.


git filter-repo文档中所述,大致可以通过运行:

git fast-export <options> | filter | git fast-import <options>

git fast-export/git fast-import对 git 2.24 有一些改进(2019 年第四季度)

请参阅Elijah Newren ( ) 的提交 941790d 、提交8d7d33c提交 a1638cf提交 208d692提交 b8f50e5提交 f73b2ab提交 3164e6b(2019 年 10 月 3 日)和提交 af2abd8 newren 2019 年 9 月 25 日
(由Junio C Hamano 合并gitster——提交 16d9d71中,2019 年 10 月 15 日)

例如:

fast-import: 允许标签被标记标签识别

签字人:以利亚·纽伦

标记标识符用于fast-export提供fast-import标签以引用较早的内容。

Blob 被赋予标签是因为它们需要在提交中以给定文件名首次出现时被引用,而提交被赋予标签是因为它们可以是其他提交的父级。

标签从未被赋予标签,可能是因为它们被认为是不必要的,但这带来了两个问题:

  1. 如果我们想创建一个标签的标签(或更高的嵌套),它让我们无法引用以前的标签。
  2. --export-marks它让我们无法记录在使用and时已经导入了标签--import-marks

通过允许标签的可选标记标签来解决这些问题。

于 2019-10-05T19:28:12.023 回答
7

Off the top of my head:

  • git commit --amend will re-write the previous commit
  • git rebase can rewrite multiple commits (rebase is also called when using git pull with the --rebase flag or the branch.$name.rebase config option)
  • git filter-branch can rewrite multiple commits
  • git push -f can change the commit a branch points to (same goes for the git push origin +branch syntax)
  • git reset can change the commit a branch points to
  • git branch -f can change the commit a branch points to (by recreating a branch with the same name)
  • git checkout -B can change the commit a branch points to (by recreating a branch with the same name)
于 2014-09-08T08:42:18.930 回答
5

knittl已经编制了一个很好的重写历史命令的列表,但我想在他的回答的基础上再接再厉。

您能否提供一份 [...] 可能破坏 git 历史记录的操作或命令的列表?应该绝对避免什么?

首先,重写/删除历史本身并没有错;毕竟,您可能经常创建功能分支,将它们严格保留在本地,然后删除(在合并它们或意识到它们无处可去之后),而不会三思而后行。

但是,当您在本地重写/删除其他人已经访问的历史记录然后将其推送到共享远程时,您可以而且肯定会遇到问题。

应该算作重写/删除本地仓库历史的操作

当然,有一些愚蠢的方法可以破坏或删除历史记录(例如篡改 的内容.git/objects/),但这些超出了我的回答范围。

您可以通过各种方式重写本地 repo 的历史记录。Pro Git 书中题为Rewriting history的部分提到了一些

  • git amend --commit
  • git rebase
  • git filter-branch
  • Roberto Tyley 的 BFG Repo Cleaner(第 3 方工具)

可以说,还有更多。任何有可能改变或以其他方式移动非符号引用(分支或标记)并使其指向不是分支当前提示的后代的提交的操作都应计为重写本地历史记录。这包括:

  • git commit --amend: 替换最后一次提交;
  • 所有形式的变基(包括git pull --rebase);
  • git reset(见下面的例子);
  • git checkout -Bgit branch -f:将现有分支重置为不同的提交;
  • git tag --force: 重新创建一个具有相同名称但可能指向另一个提交的标签。

任何非符号引用(分支或标签)的删除也可能被视为历史删除:

  • git branch -d或者git branch -D
  • git tag -d

可以说,删除一个已经完全合并到另一个分支中的分支应该被认为只是一种温和的历史删除形式,如果有的话。

但是标签是不同的。删除轻量级标签并不是什么大不了的事,但删除一个带注释的标签,这是一个真正的 Git 对象,应该算作删除本地历史。

重写/删除远程仓库历史的操作

据我所知,只有git push -f( 相当于git push --force) 有可能重写/删除远程存储库中的历史记录。

也就是说,可以

  • receive.denyNonFastForwards通过在服务器上进行设置,禁用将远程分支强制更新为非快进引用的功能。
  • receive.denyDeletes通过在服务器上进行设置,禁用删除远程存储库上的分支的功能。

此外,我使用git reset了很多,但我并不完全意识到我可能对存储库(或其他贡献者的副本)造成的损害。git reset会不会很危险?

git-reset,如knittl 所述,通常会更改分支参考点的位置。这个命令可能很危险,因为它可以使可访问的提交变得不可访问。因为一张图片说一千个单词,请考虑以下情况:

在此处输入图像描述

你在master分支上,它指向 commit D。现在,假设你跑步,例如,

git reset master~2

软重置被认为是最良性的重置形式,因为它“仅”更改当前分支指向的位置,但不会影响暂存区域或您的工作树。也就是说,仅以这种方式更改分支指向的位置会产生影响:在那次软重置之后,您最终会得到

在此处输入图像描述

从重置之前可以访问的提交C和,现在变得无法访问;换句话说,它们不是任何引用(分支、标签或 HEAD)的祖先。您可以说它们处于“存储库边缘”中;它们仍然存在于您的 Git 存储库的对象数据库中,但它们将不再列在.Dmastergit log

如果您在重置之前确实发现这些提交很有价值,您应该通过使一些参考(例如另一个分支)点再次提交来使它们再次可访问D。否则,当 Git 运行其自动垃圾收集并删除无法访问的对象时,提交CD最终将真正死亡。

从理论上讲,您可以Dreflog中提取提交,但始终存在您会忘记那些无法访问的提交或无法识别 reflog 的哪个条目对应于 commit 的风险D

总之,是的,git-reset这可能很危险,最好确保您要重置的分支的当前尖端在重置后仍然可以访问。如果需要,在重置之前在那里创建另一个分支,以防万一,作为备份;如果您确定要忘记这些提交,您可以随时删除该分支。

于 2014-09-08T10:56:03.577 回答
2

根据经验,最危险的命令之一是

git push -f mirror

这会将您的本地存储库镜像到远程进程中,删除除本地存储库中的分支之外的所有其他分支。

于 2019-01-22T07:15:19.907 回答