Git 将删除和添加文件的两次提交转换为文件移动的一次提交
我用Subgit导入了SVN存储库,但在git方面遇到了问题。 每个源文件最早的版本是2014年的版本,尽管该项目始于2008年 这是由从Ant切换到Maven(它是Java项目)引起的,它将源目录结构从Git 将删除和添加文件的两次提交转换为文件移动的一次提交,git,rebase,Git,Rebase,我用Subgit导入了SVN存储库,但在git方面遇到了问题。 每个源文件最早的版本是2014年的版本,尽管该项目始于2008年 这是由从Ant切换到Maven(它是Java项目)引起的,它将源目录结构从/src/package/更改为/src/main/Java/package。通过svn log我可以看到有两次这样的革命: 在第一个示例中,删除了所有源代码 在第二个示例中,源文件被添加为遵循Maven目录结构 这就是为什么git只能显示Ant->Maven迁移当天的最早版本 我能否以某种
/src/package/
更改为/src/main/Java/package
。通过svn log
我可以看到有两次这样的革命:
- 在第一个示例中,删除了所有源代码
- 在第二个示例中,源文件被添加为遵循Maven目录结构
git
只能显示Ant->Maven迁移当天的最早版本
我能否以某种方式重写git历史记录,使git了解所有文件实际上都已移动,但未删除并重新添加?使用
git筛选器分支
删除删除所有源文件的提交:
git filter-branch --commit-filter '
if [ "$GIT_COMMIT" = insert_SHA1_to_remove_here ];
then
skip_commit "$@";
else
git commit-tree "$@";
fi' --tag-name-filter cat --all
有关更多详细信息,请参阅(搜索“Darl”)。使用
git过滤器分支
删除删除所有源文件的提交:
git filter-branch --commit-filter '
if [ "$GIT_COMMIT" = insert_SHA1_to_remove_here ];
then
skip_commit "$@";
else
git commit-tree "$@";
fi' --tag-name-filter cat --all
有关更多详细信息,请参阅(搜索“Darl”)。您有三个选项
就Git而言,不存在使用文件移动进行提交的情况。提交只是一个快照:“这就是所包含的内容。”就是这样:不多也不少。在其他VCSE中,旧提交a之后的新提交B不仅仅是“what's In”的快照,它也是“what's changed”,可能包括“重命名的路径/到/文件到不同的/路径/到/新名称”之类的内容。然而,Git却选择(尝试)在稍后,通过比较commit B的新内容和commit A的旧内容,来重构您在查看它时所更改的内容
一般来说,Git一次后退一次提交:比较Y和Z,然后比较X和Y,然后比较W和X,依此类推。例如,git日志
和git日志
就是这样做的。注意,我在这里给出了提交的单字母名称,并假设了一个线性序列:a--B--C--Z
。实际上,我们需要更长的ID,并且不是所有的序列都是线性的(但幸运的是,这个问题附近的序列都是线性的)
这对您意味着,您必须说服Git不要将commit H(“commit that,vs G,有新名称下的文件”)与commit G(“commit that,当与F比较时,删除旧名称下的文件”)进行比较,而是将commit H与commit F进行比较,跳过G。事实上,也许我们也想通过比较commit I来跳过commit H(H之后的一个)提交F(G之前的一个)。这比跳过已删除文件的提交更重要
对于所有选项,我们需要知道(或找到)几个Git的提交ID。四个“特别有趣”的提交是:
- “再次添加所有文件”的提交:上面是H,但我们称之为
(实际上是一个潜在有效的Git哈希ID)。您需要找到真实的IDadda
- “删除所有文件”的提交。这是上面的父项,因此我们可以使用Git提供的有趣的后缀hat(
)语法命名它,方法是编写^
。但我们只需假设原始编号为addadda^
de7ede1e7ede1e7ede1e7ede1e7ede1e7ede1e
- 我们可能还需要知道
之后的提交。这就是我们称之为“I”的提交上图:由于Git反向遍历历史,commitadda
GoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGoodGood
- 在所有删除之前提交。同样,我们可以使用hat语法,事实上,知道“好”提交ID,我们可以只使用
goodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgoodgo
git bull
跳过提交
有几种方法可以做到这一点,但有一种方法在其他Git命令中无法直接使用:
-S使用revs文件中的修订,而不是调用 此选项的文档很差(在我看来):
-S
文件参数不是修订列表,而是移植列表
这意味着,您可以运行以下命令,而不是git dull
:
echo addaddaddaddaddaddaddaddaddaddaddaddadda \
$(git rev-parse de1e7ede1e7ede1e7ede1e7ede1e7ede1e7ede1e^) > \
/tmp/graft
git blame -S /tmp/graft file-you-are-concerned-with
(或类似,取决于您的操作系统)。有关其他技巧,请参见下文,因为您可能也想跳过“添加”提交。当然,这里的两个原始提交ID必须是正确的
(如果您在“delete”提交之前拥有提交的原始ID,您可以使用它来代替调用git rev parse
。调用rev parse
的好处是,您可以使用缩写提交,从而获得完整的提交,当然还有所有常见的git revisions语法。“echo”确保两个ID位于同一行,因为-S
文件的处理方式与旧的Git grafts hack相同。)
选项2:更一般地隐藏提交
如果要对大多数Git命令隐藏提交,可以使用以下方法在一个存储库中更永久地执行该操作(以不在其他位置传播的方式):
我们在这里所做的是告诉Git,每当它要查看commitadda
时,它应该将目光转向新的“替换”提交。Git replace命令主要通过复制来提交新的替换
git replace --graft goodgoodgoodgoodgoodgoodgoodgoodgoodgood \
de1e7ede1e7ede1e7ede1e7ede1e7ede1e7ede1e^
-------I' <-- replacement for I
/
A--...--E--F--G--H--I--J--...--Z <-- HEAD