如何拆分埋藏在历史中的 Git 提交?
我搞砸了我的历史并想对其进行一些更改。问题是,我有一个包含两个不相关更改的提交,并且该提交被我的本地(非推送)历史记录中的一些其他更改包围。
我想在推出之前拆分此提交,但我看到的大多数指南都与拆分您最近的提交或未提交的本地更改有关。对埋藏在历史中的提交执行此操作是否可行,而不必从那时起“重新执行”我的提交?
I flubbed up my history and want to do some changes to it. Problem is, I have a commit with two unrelated changes, and this commit is surrounded by some other changes in my local (non-pushed) history.
I want to split up this commit before I push it out, but most of the guides I'm seeing have to do with splitting up your most recent commit, or uncommitted local changes. Is it feasible to do this to a commit that is buried in history a bit, without having to "re-do" my commits since then?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
rebase 联机帮助页中有一份拆分提交的指南。快速总结是:
执行包括目标提交的交互式变基(例如
git rebase -i^分支
)并将其标记为要编辑。当变基到达该提交时,使用 git reset HEAD^ 重置到提交之前,但保持工作树完整。
增量添加更改并提交它们,根据需要进行尽可能多的提交。
add -p
对于仅添加给定文件中的部分更改非常有用。如果您想重复使用原始提交消息,请使用commit -c ORIG_HEAD
增量添加更改如果您想测试您正在提交的内容(好主意!),请使用
git stash
隐藏您尚未提交的部分(或stash --keep-index< /code> 在提交之前),测试,然后 git stash pop
将其余部分返回到工作树。继续提交,直到提交所有修改,即拥有一个干净的工作树。运行
git rebase --continue
以在现在拆分提交后继续应用提交。There is a guide to splitting commits in the rebase manpage. The quick summary is:
Perform an interactive rebase including the target commit (e.g.
git rebase -i <commit-to-split>^ branch
) and mark it to be edited.When the rebase reaches that commit, use
git reset HEAD^
to reset to before the commit, but keep your work tree intact.Incrementally add changes and commit them, making as many commits as desired.
add -p
can be useful to add only some of the changes in a given file. Usecommit -c ORIG_HEAD
if you want to re-use the original commit message for a certain commit.If you want to test what you're committing (good idea!) use
git stash
to hide away the part you haven't committed (orstash --keep-index
before you even commit it), test, thengit stash pop
to return the rest to the work tree. Keep making commits until you get all modifications committed, i.e. have a clean work tree.Run
git rebase --continue
to proceed applying the commits after the now-split commit.以下是如何使用 Magit 进行此操作。
假设提交 ed417ae 是您要更改的提交;它包含两个不相关的更改,并隐藏在一个或多个提交之下。点击
ll
显示日志,然后导航到 ed417ae:然后点击
r
打开变基弹出窗口和
m< /code> 修改此时的提交。
请注意您想要拆分的提交上的
@
现在如何 - 这意味着 HEAD 现在位于该提交处:我们想要将 HEAD 移动到父级,因此导航到父级 (47e18b3) 并点击
x
(magit-reset-quickly
,如果您使用evil-magit<,则绑定到
o
/code>) 并输入说“是的,我的意思是在点提交”。您的日志现在应如下所示:现在,点击
q
进入常规 Magit 状态,然后使用常规 unstageu
命令取消暂存状态。不进入第一次提交,像往常一样提交c
其余部分,然后s
tage 和c
提交第二次提交中的内容,并且完成后:按r
打开变基弹出窗口和另一个
r
继续,你就完成了!ll
现在显示:Here's how to do it with Magit.
Say commit ed417ae is the one you want to change; it contains two unrelated changes and is buried under one or more commits. Hit
ll
to show the log, and navigate to ed417ae:Then hit
r
to open the rebase popupand
m
to modify the commit at point.Notice how the
@
there is now on the commit you want to split – that means HEAD is now at that commit:We want to move HEAD to the parent, so navigate to the parent (47e18b3) and hit
x
(magit-reset-quickly
, bound too
if you're usingevil-magit
) and enter to say "yes I meant commit at point". Your log should now look like:Now, hit
q
to go to the regular Magit status, then use the regular unstageu
command to unstage what doesn't go in the first commit, commitc
the rest as usual, thens
tage andc
ommit what goes in the second commit, and when done: hitr
to open the rebase popupand another
r
to continue, and you're done!ll
now shows:要拆分提交
并在此提交之前添加新提交,并保存
的作者日期,-步骤如下:在之前编辑提交
注意:也许还需要编辑
。将
挑选到索引中以交互方式重置索引中不需要的更改并重置工作树
作为替代方案,只需以交互方式存储不需要的更改:
git stash push -p -m "tmp otherchanges"
进行其他更改(如果有)并创建新的提交
(可选)重复项目 2-4 以添加更多中间提交。
继续变基
To split a commit
<commit>
and add the new commit before this one, and save the author date of<commit>
, — the steps are following:Edit the commit before
<commit>
NB: perhaps it will be also needed to edit
<commit>
as well.Cherry pick
<commit>
into the indexInteractively reset unneeded changes from the index and reset the working tree
As alternative, just stash unneeded changes interactively:
git stash push -p -m "tmp other changes"
Make other changes (if any) and create the new commit
Optionally, repeat the items 2-4 to add more intermediate commits.
Continue rebasing
如果您只想从一个文件中提取内容,还有一个更快的版本。它更快,因为交互式变基实际上不再是交互式的(如果您想从上次提交中提取,那么它当然更快,然后根本不需要变基)
the_file
。关闭the_file
。这是您唯一需要的版本,其余的只是 git 命令。在索引中暂存该删除:
将刚刚删除的行恢复到文件中而不影响索引!
“SHA1”是您要从中提取行的提交:
创建第二个全新的提交,其中包含通过步骤 3 恢复的要提取的内容:
不要编辑,不要停止/继续 - 只需接受所有内容:
当然,当要从中提取的提交是最后一次提交时,速度会更快:
如果您使用
magit
那么第 4、5 和 6 步是一个操作:提交、即时修复There's a faster version if you only want to extract content from just one file. It's faster because the interactive rebase is not actually interactive anymore (and it's of course even faster if you want to extract from the last commit, then no need to rebase at all)
the_file
. Closethe_file
. That's the only edition you need, all the rest is just git commands.Stage that deletion in the index:
Restore the lines you just deleted back into the file without affecting the index!
"SHA1" is the commit you want to extract the lines from:
Create the second, brand new commit with the content to extract restored by step 3:
Don't edit, don't stop/continue - just accept everything:
Of course even faster when the commit to extract from is the last commit:
If you use
magit
then step 4, 5 and 6 are a single action: Commit, instant Fixup通过挑选来手动更正历史记录也适用于某些情况。
我更喜欢使用我的 git GUI(而不是命令行),我的有问题的提交只有 3 次提交,我还没有推送任何,下面的也不是完全整洁,所以我选择完全重建所有通过挑选它们,它比通过命令行使用交互式变基编辑更快,但方法相似。
以下是我在我最喜欢的 git GUI 中的做法(我个人使用 SourceTree):
Manually correcting the history via cherry picking can also work for some cases.
I perfer to use my git GUI (instead of the command line), my problematic commit was only 3 commits down, I haven't yet pushed any, and the following ones weren't exactly tidy either, so I opted for completely reconstructing all of them by cherry-picking, and it was faster than using the interactive rebase edits via the command line, but similar in approach.
Here's how I did it in my favorite git GUI (I personally use SourceTree):
如果您还没有推送,只需使用 git rebase 即可。更好的是,使用 git rebase -i 以交互方式移动提交。您可以将有问题的提交移到前面,然后根据需要将其拆分,并将补丁移回后面(如果需要)。
If you haven't pushed yet, just use
git rebase
. Even better, usegit rebase -i
to move commits around interactively. You can move the offending commit to the front, then split it up as you like and move the patches back (if needed).