假设我在本地分支上有以下版本历史记录:
A -- B -- C
如何在A和B之间插入新版本X,使版本历史记录如下所示:
A -- X -- B -- C
请注意,在how to insert a commit in the past上也有类似的问题,但在我的案例中,情况有点不同:X引入的任何更改都不会传播到B,在版本X插入到A和B之间后,B应该保持不变。
假设我在本地分支上有以下版本历史记录:
A -- B -- C
如何在A和B之间插入新版本X,使版本历史记录如下所示:
A -- X -- B -- C
请注意,在how to insert a commit in the past上也有类似的问题,但在我的案例中,情况有点不同:X引入的任何更改都不会传播到B,在版本X插入到A和B之间后,B应该保持不变。
2条答案
按热度按时间kknvjkwl1#
如果你想添加提交,但使它对源代码没有影响,那么是的,你确实需要一个不同于交互式重定基的路径。
没有为此专门设计的代码,但是使用
git replace
相对容易:A
(作为分离的HEAD,或者在一个新的分支上,但是新的分支很快就没用了):git checkout <hash-of-A>
.1.提交
X
(无论您希望它以何种方式出现)。1.运行
git replace --graft <hash-of-B> HEAD
。您现在拥有以下实际历史记录:
这些“replace”对象的特殊之处在于,每当Git要对几乎任何对象执行任何操作时--例如“使用提交来实现任何目的”--Git都会查看是否存在一个带有对象哈希ID的
refs/replace/
名称。由于B
有一个替换,Git现在会转而查看B'
。因此,git log
和其他Git命令将按照如下方式执行:然而,请注意,真实的的历史仍然存在于这个仓库中。* 当且仅当 *
refs/replace/
名称在仓库中时,才使用替代历史--当然,它在 * 这个 * 仓库中--并且启用了替换(默认情况下是这样的)。但是,如果您将这个仓库克隆或推送到其他地方,获取或推送过程通常不会传输任何refs/replace/
名称;因此这个存储库的克隆将切换回旧的历史记录。(您还可以通过禁用替换来查看原始历史记录,例如使用git --no-replace-objects log ...
。)如果你愿意,现在可以运行
git filter-branch
,它只复制提交,默认情况下,git filter-branch
遵循替换规则,这意味着当它复制分支上的提交时,它将从A
开始,然后复制X
,再复制B'
,然后将C
复制到B'
。A
、X
和B'
的副本将与原始副本完全相同,因此实际上将重用原始副本;但是C
的副本将稍有不同:它将使用B'
作为它的真实的父节点,而不是作为替代的、嫁接的父节点。因此,提交C
将被复制到新的提交C'
,并且分支名称,无论它是什么,都将指向新的副本。这个过滤后的分支有 actual(不仅仅是嫁接的)历史记录,它从
B'
到X
再到A
,所以现在如果你克隆过滤后的仓库,克隆中看到的历史记录同样会从C'
开始,从B'
到X
再到A
。hc2pp10m2#
在@torek的回答的基础上,我在分支
C
的提交A
和B
之间插入了一个旧版本的文件F
(保存在F.verX
中):这将根据文件修改日期设置旧提交的作者和提交日期。如果没有这个设置,提交将具有今天的日期,并且如果按日期排序,将显示在日志的顶部
olddate
被设置为git正确格式的日期。当然,你也可以将其设置为任何其他的日期,例如date -d "23 Jan"
而不是date -r F
。我在提交消息中添加了一条带有当前日期的注解,但这当然不是必需的。
git filter-branch
会在插入的提交之后为分支上的所有内容创建新的提交哈希。这就是为什么你需要git push -f
。**警告:**这会打乱其他人在该分支上的更改。实际的提交将与之前相同。