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 -B 和 git 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 分支上,它指向提交D。现在,假设你跑步,例如,
git reset master~2
软重置被认为是最良性的重置形式,因为它“仅”更改当前分支指向的位置,但不会影响暂存区域或您的工作树。也就是说,仅以这种方式更改分支指向的位置会产生影响:在软重置之后,您最终会得到
在重置之前可以从master 访问的提交C 和D 现在变得无法访问;换句话说,它们不是任何引用(分支、标签或 HEAD)的祖先。你可以说他们在“repository libo”;它们仍然存在于您的 Git 存储库的对象数据库中,但它们将不再列在 git log 的输出中。
如果您在重置之前确实发现这些提交很有价值,您应该通过引用(例如另一个分支)再次指向提交 D 来使它们再次可访问。否则,当 Git 运行其自动垃圾收集并删除无法访问的对象时,提交 C 和 D 最终将死于真正的死亡。
理论上,您可以从reflog 中找出提交D,但始终存在这样的风险,即您会忘记那些无法访问的提交,或者无法识别reflog 的哪个条目对应于哪个条目提交D。
总之,是的,git-reset 可能很危险,最好确保您要重置的分支的当前尖端在重置后仍然可以访问。如果需要,在重置之前在那里创建另一个分支,以防万一,作为备份;如果您确定要忘记这些提交,您可以随时删除该分支。