Git命令可以破坏/重写历史记录

Kam*_*her 6 git github git-rewrite-history

你能提供一个(全部或最常见的)操作或命令列表,这些操作或命令会危害git中的历史记录吗?

什么应该绝对避免?

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

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

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

Von*_*onC 10

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

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

提交483e861提交9df53c5提交7b6ad97(2019年9月4日)通过以利亚Newren( )newren
(由Junio C gitsterHamano合并-- --提交 91243b0 中,2019 年 9 月 30 日)

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

filter-branch遭受大量伪装的危险,这些危险使历史改写(即偏离刻意的变化)。

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

此外,更新引用filter-branch.
即使我们可以继续推荐filter-branch其中一些需要的更新 ,要么是因为暗示某些东西在filter-branch更普遍地应用于所有历史重写工具(例如BFG, reposurgeon, fast-import, filter-repo)时是独一无二的,要么是因为 filter-branch尽管有其他更多信息,但仍将about用作示例。现在存在众所周知的例子。
重新编写这些部分以解决这些问题并避免推荐filter-branch.

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

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

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


哪些操作或命令会破坏 git 中的历史记录?

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

在其既定目标中:

更智能的安全

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

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


git filter-repo正如文档中提到的,通过运行大致可以工作:

git fast-export <options> | filter | git fast-import <options>
Run Code Online (Sandbox Code Playgroud)

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

提交941790d提交8d7d33c提交a1638cf提交208d692提交b8f50e5提交f73b2ab提交3164e6b(2019年10月3日),并提交af2abd8(2019年9月25日)通过以利亚Newren( )newren
(由Junio C gitsterHamano合并-- --commit 16d9d71,2019 年 10 月 15 日)

例如:

fast-import: 允许通过标记标签来识别标签

签字人: Elijah Newren

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

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

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

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

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


kni*_*ttl 6

脱离我的头顶:

  • git commit --amend 将重写前一次提交
  • git rebase可以重写多次提交(衍合使用时也被称为git pull--rebase标志或branch.$name.rebase配置选项)
  • git filter-branch 可以重写多个提交
  • git push -f可以更改分支指向的提交(git push origin +branch语法相同)
  • git reset 可以更改分支指向的提交
  • git branch -f 可以更改分支指向的提交(通过重新创建具有相同名称的分支)
  • git checkout -B 可以更改分支指向的提交(通过重新创建具有相同名称的分支)


jub*_*0bs 5

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

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

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

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

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

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

您可以通过各种方式重写本地存储库的历史记录。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
Run Code Online (Sandbox Code Playgroud)

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

在此处输入图片说明

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

如果您确实在重置之前发现这些提交有价值,您应该通过使一些引用(例如另一个分支)指向D再次提交来使它们再次可访问。否则,承诺CD最终会死于真正的死亡时的Git运行其自动垃圾收集并删除不可达的对象。

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

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