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

发布于 2024-10-05 05:19:35 字数 169 浏览 6 评论 0原文

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

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

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 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

笑着哭最痛 2024-10-12 05:19:35

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. Use commit -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 (or stash --keep-index before you even commit it), test, then git 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.

我一向站在原地 2024-10-12 05:19:35

以下是如何使用 Magit 进行此操作。

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

initial log

然后点击 r 打开变基弹出窗口

rebase popup

m< /code> 修改此时的提交。

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

修改提交

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

log after resetting

现在,点击 q 进入常规 Magit 状态,然后使用常规 unstage u 命令取消暂存状态。不进入第一次提交,像往常一样提交 c 其余部分,然后 stage 和 c 提交第二次提交中的内容,并且完成后:按 r 打开变基弹出窗口

rebase popup

和另一个 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:

initial log

Then hit r to open the rebase popup

rebase popup

and 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:

modifying a commit

We want to move HEAD to the parent, so navigate to the parent (47e18b3) and hit x (magit-reset-quickly, bound to o if you're using evil-magit) and enter to say "yes I meant commit at point". Your log should now look like:

log after resetting

Now, hit q to go to the regular Magit status, then use the regular unstage u command to unstage what doesn't go in the first commit, commit c the rest as usual, then stage and commit what goes in the second commit, and when done: hit r to open the rebase popup

rebase popup

and another r to continue, and you're done! ll now shows:

all done log

情深缘浅 2024-10-12 05:19:35

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

  1. 之前编辑提交

    git rebase -i ^^
    

    注意:也许还需要编辑

  2. 挑选到索引中

    gitcherry-pick -n ;
    
  3. 以交互方式重置索引中不需要的更改并重置工作树

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

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

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

    git commit -m "upd some" 。
    

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

  5. 继续变基

    git rebase --继续
    

To split a commit <commit> and add the new commit before this one, and save the author date of <commit>, — the steps are following:

  1. Edit the commit before <commit>

    git rebase -i <commit>^^
    

    NB: perhaps it will be also needed to edit <commit> as well.

  2. Cherry pick <commit> into the index

    git cherry-pick -n <commit>
    
  3. Interactively reset unneeded changes from the index and reset the working tree

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

    As alternative, just stash unneeded changes interactively: git stash push -p -m "tmp other changes"

  4. Make other changes (if any) and create the new commit

    git commit -m "upd something" .
    

    Optionally, repeat the items 2-4 to add more intermediate commits.

  5. Continue rebasing

    git rebase --continue
    
青丝拂面 2024-10-12 05:19:35

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

  1. 使用您的编辑器并删除您想要从 the_file。关闭the_file。这是您唯一需要的版本,其余的只是 git 命令。
  2. 在索引中暂存该删除:

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

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

    git commit -m '修复! SHA1' 
    
  5. 创建第二个全新的提交,其中包含通过步骤 3 恢复的要提取的内容:

    git commit -m '第二次和新提交' 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 步是一个操作:提交、即时修复

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)

  1. Use your editor and delete the lines you want to extract from the_file. Close the_file. That's the only edition you need, all the rest is just git commands.
  2. Stage that deletion in the index:

    git  add  the_file
    
  3. Restore the lines you just deleted back into the file without affecting the index!

    git show HEAD:./the_file > the_file
    
  4. "SHA1" is the commit you want to extract the lines from:

    git commit -m 'fixup! SHA1' 
    
  5. Create the second, brand new commit with the content to extract restored by step 3:

    git commit -m 'second and new commit' the_file 
    
  6. Don't edit, don't stop/continue - just accept everything:

    git rebase --autosquash -i SHA1~1
    

Of course even faster when the commit to extract from is the last commit:

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

If you use magit then step 4, 5 and 6 are a single action: Commit, instant Fixup

酒解孤独 2024-10-12 05:19:35

通过挑选来手动更正历史记录也适用于某些情况。

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

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

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

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):

  1. Create a tag on the current state so that it isn't lost.
  2. Now move your actual local branch pointer to the messy commit.
  3. Reset (mixed) to the previous one, so that the files from the commit in (2) are kept.
  4. You can now split the commit in two or more by staging files that are needed and commiting with the correct message, until there's no unstaged files left.
  5. Cherry pick the next commit in line (from the history you've tagged). You do this by right clicking the desired commit and choosing "cherry pick". Goto (4), do until no more unaccounted commits left.
  6. Don't worry if as a result you have some commits that would be best squashed into one. You can squash them with an optional interactive rebase in GUI. It's as simple right-clicking the commit before the mess & clicking "Interactive rebase" then dragging commits onto each other to squash (fix the commit message to keep it simple), or move them up or down as desired.
  7. Remove the tag created in (1).
夜深人未静 2024-10-12 05:19:35

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

If you haven't pushed yet, just use git rebase. Even better, use git 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).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文