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

应绝对避免什么?


在推送此内容后(git commit / git push / git commit --amend),修改提交内容
对已经推送的内容进行修改

我想这个问题(如果之前没有问过

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

#1 楼

knittl已经编制了一份很好的重写历史记录的命令列表,但是我想在他的回答基础上。


您可以提供操作或命令的列表吗?可以破坏git中的历史?



首先,重写/删除历史记录本身没有什么问题;毕竟,您可能会常规创建要素分支,严格将其保留在本地,然后删除(在合并它们或意识到它们使您无所适从之后),而无需三思而后行。

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

应视为重写/删除本地存储库历史记录的操作

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

您可以重写以各种方式进行本地回购。 Pro Git书中名为“重写历史”的部分提到了一些


git amend --commit
git rebase
git filter-branch
罗伯托·泰利(Roberto Tyley)的BFG回购清洁剂(第三派对工具)

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



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,但是我并没有完全意识到我可能会对存储库造成的损害(或其他贡献者副本)。如knittl所述,git reset会危险吗?


git-reset通常会在分支引用指向的位置发生更改。该命令可能会很危险,因为它可以使可到达的提交变得不可到达。由于图片说出一千个单词,请考虑以下情况:



您在master分支上,该分支指向commit D。现在,假设您运行了

git reset master~2


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



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

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

理论上,您可以从reflog中将fish提交D,但存在总会有一个风险,您会忘记那些无法到达的提交,或者无法识别reflog的哪个条目对应于提交D

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

评论


还向那些用户澄清(就像我一样,几个月前)仍然是git的新手:这个问题与对远程存储库进行更改有关。只要提交/分支只在本地,使用git reset --soft /-hard(如果您不重置为已推送的提交)就没有风险。我通常在本地分支上处理新功能,然后在git push原始之前git merge或git cherry-pick它们到master分支。只要您留在本地分支机构(并且如前所述,不要触摸已推送的提交),就可以安全地做任何事情。

–卡马羽
2015年2月23日在21:04



如果有人知道您可以重写历史记录并且在本地分支机构也做错了事的情况,请写下来! (@Jubobs:无论如何,感谢您的完整回答。)

–卡马羽
2015年2月23日在21:07

#2 楼

git commit --amend会重写以前的提交

git rebase可以重写多个提交(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可以更改分支指向的提交(通过重新创建具有相同名称的分支)


评论


git pull --rebase也是如此。

–VonC
2014年9月8日在8:47

嗯,不错的清单。我应该学习很多命令。您能否至少更深入地解释git reset的情况?任何git reset(带有/不带有--soft或--hard)都可以危害它吗?一个实际的例子?

–卡马羽
2014年9月8日在8:48

@Kamafeather:git reset $ commit将更改当前分支的尖端,而不管使用的标志(软/硬/混合/保留/合并/…)如何。例如:git reset HEAD ^; git push -f –这将从发布的历史记录中删除最后一次提交(服务器上的结果与git push -f origin HEAD ^:HEAD没区别)

– knittl
2014年9月8日在8:54

@Kamafeather git svn dcommit还会更改历史记录中尚未提交到Subversion存储库的每个提交。

–FSMaxB
2014年9月9日在21:53

#3 楼

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


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

参见Elijah Newren(newren)提交的commit 483e861,commit 9df53c5,commit 7b6ad97(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的替代方法的部分。
我对此感到有些不满,尤其是因为我觉得自己学会了我在BF中充分利用了BFG 4312079q(远远超过我对
filter-repo所能说的),但保留该部分存在一些问题:


为了建议人们使用filter-branch退出,我们需要为他们提供其他可以处理所有相同类型的重写的建议。
据我所知,filter-branch是唯一的此类工具。因此,需要提及。
我不想向用户提供相互矛盾的建议。
如果我们推荐两种工具,则我们不应该期望用户同时学习并选择使用哪一种工具。我们应该说明一个问题可以解决另一个问题,或者一个问题比另一个问题快得多。

filter-repoBFG具有相似的性能
filter-repo可以完成的所有过滤类型, BFG也可以。
实际上,filter-repo带有名为filter-repoBFG的重新实现,它提供与bfg-ish相同的用户界面,但是由于其技术基础而很难在BFG中实现。

虽然我仍然可以提及这两个工具,但似乎我需要提供某种比较,而我最终只是说BFG可以完成filter-repo可以做的所有事情,因此最终看来最好完全删除该部分。




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


至少,BFG可以从其使用受到损害的任何历史中恢复。

在其既定目标中:


更智能的安全性

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

我见过的几乎每个人都对存储库进行了过滤操作,因为它是全新的克隆,因为万一出现错误,将克隆清除就可以轻松得多。
除非用户用newren/git-filter-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(newren)提交的commit 941790d,commit 8d7d33c,commit a1638cf,commit 208d692,commit b8f50e5,commit f73b2ab,commit 3164e6b(commit 3164e6b(2019年9月25日)和commit af2abd8(2019年9月25日)。(由Junio C Hamano合并-q4312079 -在2019年10月15日提交的16d9d71中)

例如:



gitster:允许标签由标记标签标识

签名人:Elijah Newren

fast-importfast-export中使用标记标识符来提供引用以前内容的标签。

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

从来没有给标签加上标签,可能是因为它们被认为是不必要的,但这带来了两个问题:


它使我们无法引用以前的方法标签,如果我们要创建标签(或更高嵌套)的标签。
使用fast-import--export-marks时,我们无法记录已导入标签的方法。

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


评论


在纸上看起来不错,但是任何时候我得到以下错误错误:需要使用git版本2.17.1的git版本,其diff-tree命令具有--combined-all-paths选项。

–J.M. Janzen
19-10-16在16:12

@ J.M。Janzen Strange,我没有得到(但是我确实使用Git 2.23)

–VonC
19-10-16在18:18

#4 楼

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

git push -f mirror

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