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

时间:2014-09-08 08:38:09

标签: git github git-rewrite-history

您能否提供可能危害git中历史记录的(全部或最常见)操作或命令的列表?

应该绝对避免什么?

  1. 推送此提交后修改提交(git commit / git push / git commit --amend
  2. 重新启动已推送的内容
  3. 我希望这个问题(如果它还没有在其他地方提出过)在 git 上的常见的可避免操作上成为某种参考。

    此外,我经常使用git reset,但我并不完全了解我可以对存储库(或其他贡献者副本)造成的损害。 git reset可能有危险吗?

4 个答案:

答案 0 :(得分:5)

脱离我的头顶:

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

答案 1 :(得分:3)

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

  

您能否提供可能危害git历史记录的操作或命令列表?什么应该绝对避免?

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

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

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

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

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

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

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

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

任何删除非符号引用(分支或标记)也可视为删除历史记录:

  • git branch -dgit branch -D
  • git tag -d

可以说,删除已完全合并到另一个分支的分支应该只被视为一种温和的历史记录删除形式(如果有的话)。

但是,标签不同。删除轻量级标签并不是什么大问题,但删除带注释标签(真正的Git对象)应该算作删除本地历史记录。

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

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

那说,有可能

  • 通过在服务器上设置receive.denyNonFastForwards,禁用强制更新远程分支到非快进参考的功能。
  • 通过在服务器上设置receive.denyDeletes,禁用删除生活在远程存储库中的分支的功能。

  

此外,我经常使用git reset,但我并不完全了解我可以对存储库(或其他贡献者副本)造成的损害。 git reset可能有危险吗?

knittl所述,

git-reset通常会更改分支参考所指向的位置。此命令可能很危险,因为它可以使可达提交变得无法访问。因为图片说千言万语,请考虑以下情况:

enter image description here

您位于master分支上,该分支指向提交D。现在,让我们说你跑了,例如,

git reset master~2

软复位被认为是最良性的复位形式,因为它只是"只有"更改当前分支指向的位置,但不会影响暂存区域或工作树。也就是说,仅仅改变一个分支指向的方式就会产生影响:在软复位之后,你最终会得到

enter image description here

提交CD,在重置之前可以从master到达,现在已无法访问;换句话说,它们不是任何引用的祖先(分支,标记或HEAD)。你可以说他们已经在" 知识库中设置&#34 ;;它们仍然存在于您的Git repo对象数据库中,但它们将不再列在git log的输出中。

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

理论上,您可以从reflog中提交D,但是您总是有可能忘记那些无法访问的提交或者无法确定哪些提交reflog的条目对应于commit D

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

答案 2 :(得分:2)

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

git filter-branch也已被弃用(BFG也是如此)

请参见commit 483e861commit 9df53c5commit 7b6ad97Elijah Newren (newren)(2019年9月4日)。
(由Junio C Hamano -- gitster --commit 91243b0中合并,2019年9月30日)

  

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

     

filter-branch遭受大量变相的隐匿危险   历史记录会重写(即与故意更改有所不同)。

     

许多这些问题并不引人注目,并且可以很容易地被发现,直到   新的存储库正在使用。
  这可能会导致各种问题,从比最初导致filter-branch的人们更混乱的历史到数据丢失或损坏。这些问题无法向后兼容解决,因此请向filter-branch及其手册页添加警告   建议使用其他工具(例如 filter-repo )。

     

此外,更新引用了filter-branch的其他手册页。
  即使我们可以继续推荐一些这样的更新,   filter-branch,可能是由于暗示某事是唯一的   filter-branch在更广泛地应用于所有历史记录重写时   工具(例如BFGreposurgeonfast-importfilter-repo)或   以filter-branch开头的内容为例,   现在存在众所周知的例子。
  重命名这些部分以解决这些问题并避免推荐使用filter-branch

     

最后,删除将BFG Repo Cleaner解释为   替代filter-branch
  我对此感到有些难过,尤其是因为我觉得自己从BFG学到了很多东西,因此在filter-repo中得到了很好的利用(这远远超出了我能说的范围)   filter-branch),但保留该部分存在一些问题:

     
      
  • 为了建议人们使用filter-branch退出,我们需要为他们提供其他可以处理所有相同类型重写的建议。
      据我所知,filter-repo是唯一这样的工具。因此需要提及。
  •   
  • 我不想向用户提供有冲突的建议
  •   
  • 如果我们推荐两种工具,那么我们不应该指望用户学习两种工具并选择使用哪一种。我们应该解释一个问题可以解决另一个问题不能解决的问题,或者一个问题比另一个问题快得多的问题。
  •   
  • BFGfilter-repo的性能相似
  •   
  • BFG可以执行的所有过滤类型,filter-repo也可以执行的所有过滤类型。
      实际上,filter-repo带有名为BFG的{​​{1}}的重新实现,它提供了与bfg-ish相同的用户界面,但具有一些错误修复和新功能,这些功能难以在其中实现BFG由于其技术基础。
  •   
     

尽管我仍然可以提及这两种工具,但似乎我需要提供某种比较,而我最终只是说BFG可以filter-repo所能做的一切,因此最终看来最好将其全部删除。


  

会破坏git历史记录的操作或命令吗?

至少,newren/git-filter-repo可以恢复任何受其使用影响的历史记录。

在其既定目标中:

  

更智能的安全性

     

将原始引用的副本写入存储库中的特殊名称空间不会提供用户友好的恢复机制。许多人将很难恢复使用它。

     

我见过的几乎每个人都对存储库进行过滤操作,因为它是全新的克隆,因为万一出现错误,清除克隆是一种非常容易的恢复机制。
  如果我们不在新的克隆中,则除非用户用BFG覆盖,否则请通过检测并释放它来强烈鼓励该工作流。


--forcementioned in the documentation的运行方式大致相同:

git filter-repo

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

请参见commit 941790dcommit 8d7d33ccommit a1638cfcommit 208d692commit b8f50e5commit f73b2abcommit 3164e6b(2019年10月3日) ,以及commit af2abd8Elijah Newren (newren)(2019年9月25日)。
(由Junio C Hamano -- gitster --commit 16d9d71中合并,2019年10月15日)

例如:

  

fast-import:允许标签由标记标签标识

     

签名人:伊利亚·纽伦

     

标记标识符用于git fast-export <options> | filter | git fast-import <options> fast-export中,以提供引用早期内容的标签。

     

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

     

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

     
      
  1. 如果我们要创建标签的标签(或较高的嵌套),那么就无法引用以前的标签。
  2.   
  3. 使用fast-import--export-marks时,我们无法记录已导入标签的情况。
  4.   
     

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

答案 3 :(得分:1)

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

git push -f mirror

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