如何拆分隐藏在历史中的 Git 提交?

Posted

技术标签:

【中文标题】如何拆分隐藏在历史中的 Git 提交?【英文标题】:How can I split up a Git commit buried in history? 【发布时间】:2011-05-17 11:09:52 【问题描述】:

我弄乱了我的历史记录并想对其进行一些更改。问题是,我有一个包含两个不相关更改的提交,并且这个提交被我的本地(非推送)历史记录中的一些其他更改所包围。

我想在推出之前拆分此提交,但我看到的大多数指南都与拆分您最近的提交或未提交的本地更改有关。对一个有点被历史埋没的提交执行此操作是否可行,而无需从那时起“重做”我的提交?

【问题讨论】:

Split a commit of a copy+modify into a copy commit and a modify commit using git的可能重复 【参考方案1】:

如果您还没有推送,请使用git rebase。更好的是,使用git rebase -i 以交互方式移动提交。您可以将有问题的提交移到前面,然后根据需要将其拆分并将补丁移回(如果需要)。

【讨论】:

无需将其移动到任何地方。将其拆分到原来的位置。 不幸的是,这对我不起作用,因为提交后的一些历史记录依赖于它,所以我有点受限。但是,这将是我的第一选择。 @Ben:没关系 - 之后的提交根本不需要更改(假设您保留所有更改,而不是丢弃其中一些)。更多信息在这里 - ***.com/questions/1440050/…【参考方案2】:

有一个拆分提交的指南in the rebase manpage。快速总结是:

执行包含目标提交(例如git rebase -i <commit-to-split>^ branch)的交互式变基并将其标记为要编辑。

当变基到达那个提交时,使用git reset HEAD^ 重置到提交之前,但保持你的工作树完好无损。

增量添加更改并提交它们,根据需要进行尽可能多的提交。 add -p 可用于仅在给定文件中添加一些更改。如果您想为某个提交重复使用原始提交消息,请使用 commit -c ORIG_HEAD

如果你想测试你正在提交的内容(好主意!)使用git stash 隐藏你没有提交的部分(或者在你提交之前使用stash --keep-index),测试,然后git stash pop 将其余部分返回到工作树。继续提交,直到提交所有修改,即拥有干净的工作树。

运行 git rebase --continue 以在现在拆分提交之后继续应用提交。

【讨论】:

...但如果您已经推送了自提交拆分以来的历史记录,请不要这样做。 @wilhelmtell:我省略了我通常的“潜在危险;请参阅'从上游 rebase 恢复'”样板,因为 OP 明确表示他没有推动这段历史。 你读得很完美。当我指定它尚未共享历史记录时,我试图避免使用“样板”:) 无论如何,我的建议已经成功。不过,事后做这些事情是一件很痛苦的事情。我在这里学到了一个教训,那就是确保从一开始就正确地进行提交! 第一步最好表述为git rebase -i <sha1_of_the_commit_to_split>^ branch。而git gui 是一个很好的拆分任务工具,可以用来将文件的不同部分添加到不同的提交中。 @QiangXu:第一个是合理的建议。第二个正是我建议git add -p 的原因,它可以比git gui 在这个部门做更多的事情(特别是编辑帅哥,从当前帅哥开始分期所有内容,以及通过正则表达式搜索帅哥)。【参考方案3】:

拆分提交<commit>并在此之前添加新提交,并保存<commit>的作者日期,步骤如下:

    编辑提交之前 <commit>

    git rebase -i <commit>^^
    

    注意:也许还需要编辑&lt;commit&gt;

    樱桃采摘&lt;commit&gt;进入索引

    git cherry-pick -n <commit>
    

    以交互方式从索引中重置不需要的更改并重置工作树

    git reset -p && git checkout-index -f -a
    

    作为替代方案,只需以交互方式存储不需要的更改:git stash push -p -m "tmp other changes"

    进行其他更改(如果有)并创建新的提交

    git commit -m "upd something" .
    

    或者,重复第 2-4 条以添加更多中间提交。

    继续变基

    git rebase --continue
    

【讨论】:

【参考方案4】:

下面是使用Magit 的方法。

说 commit ed417ae 是你想要改变的;它包含两个不相关的更改,并隐藏在一个或多个提交下。点击 ll 显示日志,然后导航到 ed417ae:

然后点击r打开rebase弹出窗口

m 修改提交点。

注意@ 现在在您要拆分的提交上 - 这意味着 HEAD 现在位于该提交上:

我们想将 HEAD 移动到父级,因此导航到父级 (47e18b3) 并点击 xmagit-reset-quickly,如果您使用的是 o,则绑定到 o)并输入“是”我的意思是立即提交”。您的日志现在应该如下所示:

现在,点击 q 进入常规 Magit 状态,然后使用常规 unstage u 命令取消暂存第一次提交中没有进入的内容,然后像往常一样提交 c 其余部分,然后 @987654345 @tage 和 commit 在第二次提交中发生了什么,完成后:点击 r 打开 rebase 弹出窗口

和另一个r 继续,你就完成了! ll 现在显示:

【讨论】:

【参考方案5】:

如果您只想从一个文件中提取内容,有一个更快的版本。它更快,因为交互式 rebase 实际上不再是交互式的(如果你想从最后一次提交中提取它当然更快,那么根本不需要 rebase)

    使用您的编辑器并删除要从the_file 中提取的行。关闭the_file。这是您唯一需要的版本,其余的只是 git 命令。

    在索引中分阶段删除:

    git  add  the_file
    

    将刚刚删除的行恢复到文件中不影响索引

    git show HEAD:./the_file > the_file
    

    “SHA1”是您要从中提取行的提交:

    git commit -m 'fixup! SHA1' 
    

    使用第 3 步恢复的内容创建第二个全新的提交:

    git commit -m 'second and new commit' the_file 
    

    不要编辑,不要停止/继续 - 接受一切:

    git rebase --autosquash -i SHA1~1
    

当要提取的提交是最后一次提交时,当然更快:

4. git commit -C HEAD --amend
5. git commit -m 'second and new commit' thefile
6. no rebase, nothing

如果您使用magit,则步骤 4、5 和 6 是一个操作:提交,即时修复

【讨论】:

【参考方案6】:

通过樱桃采摘手动更正历史记录也适用于某些情况。

我更喜欢使用我的 git GUI(而不是命令行),我的有问题的提交只有 3 次提交,我还没有推送任何,以下的也不是很整洁,所以我选择了通过樱桃采摘完全重建所有这些,并且比通过命令行使用交互式变基编辑更快,但方法相似。

这是我在我最喜欢的 git GUI 中的做法(我个人使用 SourceTree):

    在当前状态上创建一个标签,这样它就不会丢失。 现在移动你的实际本地分支指针到混乱的提交。 重置(混合)到前一个,以便保留(2)中提交的文件。 您现在可以将提交拆分为两个或更多,方法是暂存所需的文件并使用正确的消息进行提交,直到没有未暂存的文件。 Cherry pick 行中的下一个提交(来自您标记的历史记录)。您可以通过右键单击所需的提交并选择“cherry pick”来执行此操作。转到 (4),直到没有更多未计入的提交为止。 如果您有一些提交最好合并为一个,请不要担心。您可以在 GUI 中使用可选的 interactive rebase 来压缩它们。就像在混乱之前右键单击提交并单击“交互式变基”然后将提交相互拖动以压缩(修复提交消息以使其简单),或者根据需要向上或向下移动它们一样简单。 删除在 (1) 中创建的标签

【讨论】:

以上是关于如何拆分隐藏在历史中的 Git 提交?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 Git 历史记录中永久删除提交?

IDEA隐藏.iml文件防止git误提交,包含如何生产iml文件

在 git commit 之前隐藏文件中的字符串

Git 重写历史

Git 重写历史

Git常用命令