在不创建分支以及在新分支上进行大量工作的情况下,是否有可能在将提交保存到本地仓库后将单个提交分成几个不同的提交?
如果没有交互式变基,最简单的方法可能是从要拆分的提交之前的提交开始创建一个新分支,然后使用cherry-pick -n命令来挑选该提交,重置、存储、提交文件移动,重新应用存储并提交更改,然后与原先的分支合并或挑选后续提交。 (然后将原先的分支名称切换到当前头部。)(最好遵循MBO的建议进行交互式变基。)
rebase -i
建议都容易得多。我认为这没有得到很多关注是因为缺乏任何格式,不过既然您已经有了126k分,并且可能知道如何使用SO,也许您可以重新审查一下它。;) - erikbstackgit rebase -i
在功能上等同于一系列的 git cherry-pick
操作。 - undefinedreset --hard
并手动修复问题。 - undefined如果您只想从现有提交中提取某些内容并保留原始提交,则可以使用
git reset --patch HEAD^
这条命令可以让你只重置你需要的代码块,用法是git restore --staged <file>
,代替git reset HEAD^
。
当你选择好要重置的代码块后,就会有一些修改被暂存,这些修改将会在下一次提交时被撤销。现在你可以更改最近的提交信息,将这些修改从其中删除。
git commit --amend --no-edit
如果您有未提交的代码块,可以通过以下方式将其添加到单独的提交中:
git add .
git commit -m "new commit"
当然可以像上面建议的那样使用git rebase --interactive
命令来回到之前的某些提交。
在Mercurial中,他们有hg split
- 这是继hg absorb
之后我希望在Git中看到的第二个功能。
以下是在IntelliJ IDEA, PyCharm, PhpStorm等工具中如何拆分提交的方法:
在版本控制日志窗口中,选择您想要拆分的提交,右键单击并选择从这里交互式重写提交历史
将您想要拆分的提交标记为edit
,然后单击开始重写提交历史
您应该会看到一个黄色标签,表示 HEAD 设置为该提交。右键单击该提交,然后选择撤销提交
现在这些提交已经回到了暂存区,您可以将它们分别提交。在所有更改已提交后,旧提交变为无效。
虽然已经过去8年了,但也许有人仍会发现这很有帮助。
我可以在不使用rebase -i
的情况下完成这个技巧。
关键是要使git回到在执行git commit
之前的状态:
# first rewind back (mind the dot,
# though it can be any valid path,
# for instance if you want to apply only a subset of the commit)
git reset --hard <previous-commit> .
# apply the changes
git checkout <commit-you-want-to-split>
# we're almost there, but the changes are in the index at the moment,
# hence one more step (exactly as git gently suggests):
# (use "git reset HEAD <file>..." to unstage)
git reset
操作完成后,您将看到类似于 Unstaged changes after reset:
这样的提示文字,此时您的仓库状态就像您要提交所有这些文件一样。从现在开始,您可以像往常一样轻松地再次提交它。希望这有所帮助。
reset --hard
命令时要非常非常小心。它会毫不留情地摧毁未被追踪的更改,而不会给出任何警告或确认提示。 - undefinedgit rebase -i
或类似的方法。对于像我这样害怕“交互式”方法并且在下楼时喜欢抓手栏的人,这里提供一种替代方法。…—>P–>Q–>R–>…–>Z=mybranch
,你想将P–>Q
拆分为两个提交,以得到P–>Q1–>Q'–>R'–>…Z'=mybranch
,其中Q'
、R'
等处的代码状态与Q
、R
等处相同。mybranch
,以免丢失历史记录。git checkout mybranch
git checkout -b mybranch-backup
首先,查看P
(你想要拆分的提交之前的提交),并创建一个新分支进行工作
git checkout P
git checkout -b mybranch-splitting
git checkout Q file1.txt file2.txt
[…edit, stage commit with “git add”, etc…]
git commit -m "Refactored the widgets"
Q1
。现在检出Q
的完整状态,在分离的HEAD
上检出Q1
,提交此更改(创建Q'
),并将工作分支拉取到它:git checkout Q
git reset --soft Q1
git commit -m "Added unit tests for widgets"
git branch -f mybranch-splitting
mybranch-splitting
分支的Q'
状态,它应该与Q
完全相同。现在将原始分支(从Q
到Z
)变基到此分支上:git rebase --onto HEAD Q mybranch
mybranch
应该看起来像 ... P -> Q1 –> Q' –> R' –> ... Z'
,就像你想要的那样。因此,在检查一切是否正常工作之后,您可以删除您的工作和备份分支,并(如果适用)将重写的 mybranch
推送到上游。如果它已经被推送,您需要强制推送,并且所有关于强制推送的常规警告都适用。git push --force mybranch
git branch -d mybranch-splitting mybranch-backup
git diff mybranch-backup
来确保没有意外遗漏。如果它显示差异-您可以使用 git reset --hard mybranch-backup
重新开始。另外,我认为 git checkout Q file1.txt file2.txt
比 reset HEAD^
和 commit -p
更脆弱。 - nponeccopgit cherry-pick --no-commit
的功能。 - undefinedgit rebase -i
。我制作了一个视频来展示分割提交的步骤:https://www.youtube.com/watch?v=3EzOz7e1ADI
git-split
的文件中,放入您的$PATH
目录下并使其可执行。然后您就可以使用git split
来运行此脚本。请参阅脚本中的注释以了解如何使用:#!/usr/bin/env zsh
# Use `git split` to split a commit into two commits.
#
# First, use `git checkout --patch HEAD~` to stage changes that you
# want to split from the HEAD commit into a separate commit. Then run
# `git split` (this script) and enter a commit message for the new commit.
set -e
git commit --fixup=HEAD --quiet
git revert --no-commit HEAD
git revert --quit
if ! git commit $@; then
git cherry-pick --no-commit HEAD
git reset --soft HEAD~
exit 0
fi
git -c sequence.editor=: rebase --autosquash --autostash --interactive HEAD~3
我使用了rebase。编辑提交并不适用于我,因为它已经选择了提交文件并允许您对其进行修改,但我想将所有文件作为未跟踪的文件添加,这样我就可以只选择其中一些文件。步骤如下:
git rebase -i HEAD~5
(我想要拆分历史记录中的倒数第5个提交)d
标记要删除的提交;在提交后添加b
行以停止重定位进程并稍后继续。即使这是最后一个提交,这也给了您一些余地,以防万一出现问题,只需git rebase --abort
并重置所有内容。git cherry-pick -n <COMMIT ID>
。这将挑选提交更改而不选择提交本身,将其保留为未跟踪的更改。git add -i
和补丁,以便您可以添加特定的块)git stash
,但您也可以直接提交它们。git rebase --continue
以选择其他更改如果你有这个:
A - B <- mybranch
当您在提交B中提交了一些内容:
/modules/a/file1
/modules/a/file2
/modules/b/file3
/modules/b/file4
A - C - D <-mybranch
Reset the branch back to the commit before the one to split:
```git checkout mybranch
git reset --hard A
git checkout B /modules/a
git add -u
git commit -m "content of /modules/a"
git checkout B /modules/b
git add -u
git commit -m "content of /modules/b"
如果你的更改主要是添加新内容,那么这种方法最有用。
有时候你不想失去与正在分割的提交相关联的提交信息。如果您已经提交了一些要拆分的更改,可以执行以下操作:
git checkout -p HEAD^ -- path/to/file
的组合将一些更改还原到当前树中。git add .; git commit -m 'removal of things that should be changed later'
,因此您将在历史记录中保留原始提交,并且您还将拥有另一个提交,其中包含您所做的更改,因此在拆分后第一个提交中,当前HEAD上的文件看起来像您想要的样子。000aaa Original commit
000bbb removal of things that should be changed later
git revert HEAD
撤销编辑,这将创建一个撤销提交。文件将看起来像原始提交,并且您的历史记录现在将如下所示:000aaa Original commit
000bbb removal of things that should be changed later
000ccc Revert "removal of things that should be changed later" (assuming you didn't edit commit message immediately)
git rebase -i
将前两个提交压缩/合并为一个,如果之前没有为还原提交提供有意义的提交消息,则可以选择修改还原提交。最终您应该只剩下一个提交。000ddd Original commit, but without some content that is changed later
000eee Things that should be changed later
git autorebase split COMMIT_ID
命令来拆分提交。 - Jérôme Pouillergit reset HEAD~
、git stash
,接着使用git cherry-pick
来挑选压缩中的第一个提交,最后再执行git stash pop
。虽然我的cherry-pick情况比较特殊,但是git stash
和git stash pop
对于其他情况也非常方便。 - SOFe