我如何拆分历史上埋藏的Git提交?

时间:2010-11-29 19:06:13

标签: git split commit revision-history

我记录了我的历史,并想对它做一些改变。问题是,我提交了两个不相关的更改,并且此提交被我的本地(非推送)历史记录中的其他一些更改所包围。

我想在推送之前拆分此提交,但我看到的大多数指南都与拆分最近的提交或未提交的本地更改有关。是否可以对历史记录中的提交执行此操作,而不必在此后“重新执行”我的提交?

6 个答案:

答案 0 :(得分:440)

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

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

  • 当rebase到达该提交时,使用git reset HEAD^在提交之前重置为,但保持工作树完整。

  • 逐步添加更改并提交它们,根据需要进行尽可能多的提交。 add -p对于仅添加给定文件中的某些更改非常有用。如果要为特定提交重新使用原始提交消息,请使用commit -c ORIG_HEAD

  • 如果你想测试你提交的内容(好主意!)使用git stash隐藏你尚未提交的部分(或stash --keep-index,然后再提交它) ,测试,然后git stash pop将其余部分返回工作树。继续提交,直到你完成所有修改,即有一个干净的工作树。

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

答案 1 :(得分:2)

这是使用Magit的方法。

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

initial log

然后按r打开重新设置弹出窗口

rebase popup

m来修改提交。

请注意,@现在要拆分的提交有多大,这意味着HEAD现在位于该提交:

modifying a commit

我们想将HEAD移到父级,因此导航到父级(47e18b3),然后按xmagit-reset-quickly,如果您使用的是{{1} }),然后说“是的,我的意思是在点上提交”。您的日志现在应如下所示:

log after resetting

现在,点击o进入常规Magit状态,然后使用常规unstage evil-magit命令取消对第一次提交中未执行的操作的处理,其余q提交像往常一样,然后u阶段和c忽略第二次提交中的操作,完成后:按下s以打开重新设置弹出窗口

rebase popup

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

all done log

答案 2 :(得分:1)

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

  1. 之前 <commit>

    编辑提交
    git rebase -i <commit>^^
    

    注意:也许还需要编辑<commit>

  2. 樱桃将<commit>选入索引

    git cherry-pick -n <commit>
    
  3. 交互式地从索引中重置不需要的更改并重置工作树

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

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

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

    git commit -m "upd something" .
    

    (可选)重复第2-4项以添加更多中间提交。

  5. 继续变基

    git rebase --continue
    

答案 3 :(得分:0)

如果您只想从一个文件中提取内容,则有一个更快的版本。之所以更快,是因为交互式变基实际上不再是交互式的(而且,如果您想从上一次提交中提取内容,那么它甚至更快,那么根本就不需要变基)

  1. 使用编辑器并删除要从the_file中提取的行。关闭the_file。那是您唯一需要的版本,其余的只是git命令。
  2. 在索引中删除该阶段:

    git  add  the_file
    
  3. 将刚删除的行恢复回文件,而不会影响索引

    git show HEAD:./the_file > the_file
    
  4. “ SHA1”是您要从中提取行的提交:

    git commit -m 'fixup! SHA1' 
    
  5. 创建第二个全新提交,其内容要提取到步骤3中已还原的

    git commit -m 'second and new commit' the_file 
    
  6. 请勿编辑,请勿停止/继续-只接受所有内容:

    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步是单个操作:提交即时Fixup

答案 4 :(得分:0)

在某些情况下,通过摘樱桃手动更正历史记录也可以。

我倾向于使用git GUI(而不是命令行),我有问题的提交仅下降了3个提交,我还没有推送任何内容,以下两个也不是很整洁,所以我选择了完全可以通过挑选来完全重建它们,并且比通过命令行使用交互式资源库编辑要快,但是方法相似。

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

  1. 在当前状态上创建一个标签,以免丢失。
  2. 现在移动您实际的本地分支指针到混乱的提交。
  3. 重置(混合)到上一个,以便保留(2)中提交的文件。
  4. 您现在可以通过暂存所需的文件并以正确的消息进行提交,来将提交分为两份或更多,直到没有剩余的未暂存文件为止。
  5. 樱桃选择(根据您标记的历史记录)进行下一次提交。您可以通过右键单击所需的提交并选择“樱桃精选”来实现。转到(4),直到没有更多未确认的提交为止。
  6. 不要担心,如果结果是您将某些提交最好压缩为一个。您可以在GUI中使用可选的交互式资源库来压缩它们。就像在混乱之前右键单击提交,然后单击“交互式变基”,然后将提交彼此拖拉以压榨(修复提交消息以使其保持简单)那样简单,或者根据需要将它们上下移动即可。
  7. 删除在(1)中创建的标签

答案 5 :(得分:-2)

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