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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4307095/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-10 09:33:24  来源:igfitidea点击:

How can I split up a Git commit buried in history?

gitsplitcommitrevision-history

提问by Ben

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?

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

回答by Cascabel

There is a guide to splitting commits in the rebase manpage. The quick summary is:

在 rebase 联机帮助页中有一个拆分提交的指南。快速总结是:

  • 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 -pcan be useful to add only some of the changes in a given file. Use commit -c ORIG_HEADif 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 stashto hide away the part you haven't committed (or stash --keep-indexbefore you even commit it), test, then git stash popto 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 --continueto proceed applying the commits after the now-split commit.

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

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

  • 增量添加更改并提交它们,根据需要进行尽可能多的提交。add -p在给定文件中仅添加一些更改可能很有用。使用commit -c ORIG_HEAD,如果你想重新使用原来的提交信息有一定承诺。

  • 如果您想测试您正在提交的内容(好主意!)用于git stash隐藏您尚未提交的部分(或stash --keep-index在您提交之前),请进行测试,然后git stash pop将其余部分返回到工作树。继续提交,直到提交所有修改,即有一个干净的工作树。

  • 运行git rebase --continue以在 now-split 提交后继续应用提交。

回答by unhammer

Here's how to do it with Magit.

以下是如何使用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 llto show the log, and navigate to ed417ae:

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

initial log

初始日志

Then hit rto open the rebase popup

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

rebase popup

变基弹出

and mto modify the commit at point.

m在此时修改提交。

Notice how the @there is now on the commit you want to split – that means HEAD is now at that commit:

请注意@现在要拆分的提交是如何存在的 - 这意味着 HEAD 现在处于该提交:

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 oif you're using evil-magit) and enter to say "yes I meant commit at point". Your log should now look like:

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

log after resetting

重置后登录

Now, hit qto go to the regular Magit status, then use the regular unstage ucommand to unstage what doesn't go in the first commit, commit cthe rest as usual, then stage and commit what goes in the second commit, and when done: hit rto open the rebase popup

现在,点击q进入常规 Magit 状态,然后使用常规 unstageu命令取消暂存第一次提交中没有发生的内容,c照常提交其余部分,然后标记sc忽略第二次提交中发生的内容,以及完成后:点击r打开 rebase 弹出窗口

rebase popup

变基弹出

and another rto continue, and you're done! llnow shows:

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

all done log

所有完成的日志

回答by ruvim

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

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

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

回答by MarcH

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

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 magitthen step 4, 5 and 6 are a single action: Commit, instant Fixup

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

回答by Gintautas Miliauskas

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

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