能否提供一个(所有或最常见的)可能会危及 git 历史记录的操作或命令列表?
什么是绝对应该避免的?
- 在推送之后修改提交(
git commit
/git push
/git commit --amend
) - 向已经推送的内容进行变基(rebase)
如果此问题尚未在其他地方提出过,我希望它成为有关 git 常见可避免操作的参考。
此外,我经常使用 git reset
,但并不完全了解可能会对仓库(或其他贡献者的副本)造成的潜在破坏。 git reset
是否存在风险?
能否提供一个(所有或最常见的)可能会危及 git 历史记录的操作或命令列表?
什么是绝对应该避免的?
git commit
/git push
/git commit --amend
)如果此问题尚未在其他地方提出过,我希望它成为有关 git 常见可避免操作的参考。
此外,我经常使用 git reset
,但并不完全了解可能会对仓库(或其他贡献者的副本)造成的潜在破坏。 git reset
是否存在风险?
请注意,从Git 2.24(2019年第四季度)开始,上面的列表可能不再需要包括git filter-branch
。
git filter-branch
正在被弃用(BFG也是)请查看commit 483e861, commit 9df53c5, commit 7b6ad97 (2019年9月4日)由Elijah Newren (newren
)提交的内容。
(于2019年9月30日由Junio C Hamano -- gitster
--合并至commit 91243b0)
git-filter-repo
代替git-filter-branch
。filter-branch
存在许多伪装的危险,这些危险会使历史重写出现偏差。很多问题不显眼,并且很容易在新仓库中发现,从更混乱的历史记录到数据丢失或损坏等问题。这些问题无法向后兼容地解决,因此应向filter-branch
及其手册添加警告,推荐使用其他工具(如filter-repo
)。filter-branch
的其他手册。其中一些需要更新,即使我们可以继续推荐filter-branch
,因为它们暗示某些内容是特定于filter-branch
,而实际上适用于所有历史重写工具(例如BFG
、reposurgeon
、fast-import
、filter-repo
),或者因为某些关于filter-branch
的内容被用作示例,尽管现在已经有其他更常见的示例存在。BFG Repo Cleaner
作为filter-branch
替代方案的部分。虽然我对此感到有些内疚,特别是因为我觉得我从BFG中学到了很多东西,并将其运用到filter-repo
中(这比我能说出的关于filter-branch
的多得多),但保留该部分会出现一些问题:filter-branch
,我们需要向他们提供另一个可以处理所有相同重写类型的工具建议。据我所知,filter-repo
是唯一的这样的工具。因此,需要提到它。BFG
和filter-repo
的性能类似。BFG
可以执行的所有过滤类型,filter-repo
也可以执行。实际上,filter-repo
带有一个名为bfg-ish
的重新实现BFG
,提供与BFG
相同的用户界面,但具有几个难以在BFG
中实现的错误修复和新功能。filter-repo
可以做到BFG
所能做的一切,因此最终似乎最好完全删除该部分。
git filter-repo
如文档中提到的所述,大致通过运行以下命令来工作:
git fast-export <options> | filter | git fast-import <options>
而 git fast-export
/ git fast-import
在 git 2.24 (2019年第四季度) 中有一些改进。
newren
)。gitster
--合并于commit 16d9d71,2019年10月15日)
例如:
fast-import
:允许通过标记标签来识别标签签名:Elijah Newren
在
fast-export
和fast-import
中,标记标识符用于提供一个标签以引用先前的内容。由于需要在第一次出现给定文件名的提交中引用Blob,因此为Blob赋予标签,并且由于提交可以是其他提交的父项,因此为提交赋予标签。
标签从未被赋予标签,可能是因为它们被视为不必要的,但这会带来两个问题:
- 如果我们想创建标记的标记(或更高嵌套),则没有办法引用先前的标记。
- 当使用
--export-marks
和--import-marks
时,我们无法记录已导入标记的方式。通过允许标签的可选标记标签来解决这些问题。
以下是我记得的:
git commit --amend
可以重写上一个提交git rebase
可以重写多个提交(使用 git pull
命令时加上 --rebase
标志或在 branch.$name.rebase
配置选项中也会执行 rebase 操作)git filter-branch
可以重写多个提交git push -f
可以更改分支指向的提交(git push origin +branch
语法也是一样的)git reset
可以更改分支指向的提交git branch -f
可以更改分支指向的提交(通过使用相同名称重新创建分支)git checkout -B
可以更改分支指向的提交(通过使用相同名称重新创建分支)git pull --rebase
,也是。 - VonCgit reset
的情况?任何 git reset
(带/不带 --soft
或 --hard
)都可能会产生风险吗?可以举个实际例子吗? - Kamafeathergit reset $commit
会更改当前分支的尖端,无论使用哪个标志(soft/hard/mixed/keep/merge/…)。例如:git reset HEAD^; git push -f
——这将从发布历史中删除最后一次提交(在服务器上的结果与git push -f origin HEAD^:HEAD
无法区分)。 - knittlgit svn dcommit
还会更改历史记录中尚未提交到 Subversion 存储库的每个提交。 - FSMaxBknittl 已经整理了一份关于重写 Git 历史的命令列表,但我想在此基础上进一步补充。
你能提供一份操作或命令列表,说明哪些操作可能会破坏 Git 历史吗?绝对应该避免哪些操作?
首先,就本身而言,重写/删除历史并没有什么问题;毕竟,你可能经常创建功能分支,将其严格保留在本地,然后在不加思考地删除(在合并它们或意识到它们没有用处后)。
然而,当你在本地重写/删除其他人已经访问过的历史记录,并将其推送到共享的远程库时,你肯定会遇到问题。
当然,有些愚蠢的方式可能会破坏或删除历史记录(例如篡改 .git/objects/
的内容),但这些超出了我的回答范围。
git amend --commit
git rebase
git filter-branch
可以说,还有更多的方法。任何可能会改变或移动非符号引用(分支或标签)并使其指向一个不是该分支当前指向的提交的操作都应被视为重写本地历史记录。这包括:
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
通常会更改分支引用的指向位置。这个命令可能很危险,因为它可能会使可达提交变得不可达。因为一张图片胜过千言万语,考虑下面的情况:
D
的 master
分支。假设你运行以下命令:git reset master~2
软重置被认为是最温和的重置形式,因为它“仅”改变当前分支指向的位置,但不影响暂存区或工作树。尽管如此,在该方式下仅改变分支指向位置也会产生影响:在软重置之后,你将得到
在重置之前从主分支master
可达的提交C
和D
现在已经变得不可达了;换句话说,它们不是任何引用(分支、标签或HEAD)的祖先。你可以说它们处于"存储库地狱"中;它们仍然存在于Git仓库的对象数据库中,但将不再列在git log
的输出中。
如果您在重置之前确实发现这些提交有价值,您应该通过使某些引用(例如另一个分支)再次指向提交D
来使它们再次可达。否则,当Git运行其自动垃圾收集并删除不可访问的对象时,提交C
和D
将最终消失。
理论上,您可以从reflog中找回提交D
,但您始终存在着忘记那些不可访问的提交或无法识别哪个reflog条目对应于提交D
的风险。
总之,是的,git-reset
可能会很危险,因此最好确保在重置之后,你要重置的分支的当前末尾仍然可以到达。如果需要,在重置之前在那里创建另一个分支作为备份;如果你确定要忘记这些提交,随时可以稍后删除该分支。
git reset --soft/--hard
(如果您不重置已经推送的提交)就没有风险。我通常在本地分支上开发新功能,然后在git push origin
之前将它们合并到_master_分支中,或者使用git cherry-pick
命令。只要您留在本地分支上(并且不触及已经推送的提交),您可以安全地进行任何操作(我猜)。 - Kamafeather从经验来看,最危险的命令之一是
git push -f mirror
这会将您的本地repo镜像到远程服务器,同时删除所有除本地repo中以外的其他分支。
"Original Answer"翻译为"最初的回答"。
Error: need a version of git whose diff-tree command has the --combined-all-paths option
,使用的是git版本2.17.1
。 - J.M. Janzen