从历史记录中删除合并并重新定序非顺序提交 [英] Removing merges from history and rebasing non sequential commits

查看:51
本文介绍了从历史记录中删除合并并重新定序非顺序提交的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有以下git历史记录,我想压缩一个提交并删除几个合并

I have the following git history, where I want to squash a commit and remove several merges

git log --graph --oneline --all
* 80e2fa1     I want to squash this commit
*   7850013   Merge branch 'master'  want to get rid of these branch merges
|\  
| * b645616 
| * 4aee207 
* | 6077ae7   Into this one
* | bc882a2  
|/  
* 7607733  
*   82643ed   Merge branch 'master'  
|\  
| * ead8b48  
* | 5fadbff  
|/  
* 2e0797c  
*   3552f8f   Merge branch 'master'  
|\  
| *  
* |  
|/  
* e1defac  
* 912c802  

但是当我尝试进行git rebase --interactive 6077ae7时,我最终得到了

But when I try to do a git rebase --interactive 6077ae7 I end up getting

  The previous cherry-pick is now empty, possibly due to conflict resolution.

在进行合并之前,我也没有得到合并,这是删除它们的正确方法吗?这对我不起作用 git从历史记录中删除合并提交.

I also don't get the merges when I'm doing the rebase going before the merge is this the correct way to remove them? this didn't work for me git remove merge commit from history.

推荐答案

通常以变基为基础忽略合并.在您的情况下,这实际上会有所帮助!

Rebase normally ignores merges. In your case this is actually going to be helpful!

首先,请记住,还原副本会提交.实际落实的任何提交都将被重新设置基础,被复制. (原始文件保留在您的存储库中,只是被遗弃了",这样,如果您不喜欢变基的结果,便可以将其挽救.在一段时间(默认为30天)之后,随着垃圾收集器的消失,遗弃将变得更加永久.现在可以将其删除.)

First, remember that rebase copies commits. Whichever commits actually do get rebased, get copied. (The originals remain in your repository, just "abandoned", so that you can rescue them if you do not like the result of the rebase. After some time, by default 30 days, the abandonment becomes much more permanent, as the garbage collector is now allowed to remove them for real.)

让我们开始以水平格式重画git log --oneline显示的图形(它很有用,并且具有很多有用的信息),该图形会丢弃一些信息.通常这可能没什么用,但是它应该可以更容易地看到森林" (由忽略某些树木,就像过去一样).我不确定您实际上在哪个分支上,所以我将只使用标签somebranch(您的当前分支很可能是master,这是我的猜测,但是我无法从您的日志输出中证明这一点) ve引用.)

Let's start by re-drawing the graph that git log --oneline shows (which is useful and has lots of good information) in a horizontal format that discards some information. This might ordinarily be less useful, but it should make it easier to "see the forest" (by ignoring some of the trees, as it were). I am not sure which branch you are actually on, so I will just use the label somebranch (your current branch may well be master, which is my guess, but I can't prove it from the log output you've quoted).

     o      o      B--C
    / \    / \    /    \
o--o   o--o   o--A      F--G   <-- somebranch
    \ /    \ /    \    /
     o      o      D--E

所有o提交都是git log输出中最早的(最低的行)提交,而标记为AG的提交来自前7行.提交G(这是我们代表80e2fa1的一个字母代码)是您要压缩"到提交C(实际上是6077ae7)的代码.提交F是您说要舍弃的合并(确实是7850013).

All the o commits are the earliest (lowest rows) ones in your git log output, while the labeled commits A through G are are from the top 7 rows. Commit G (which is our one-letter code standing in for 80e2fa1) is the one you want to "squash away", into commit C (really 6077ae7). Commit F is the merge you say you would like to discard (really 7850013).

现在,git rebase的最简单形式就是git rebase,根本没有任何参数,但这可能不是我们想要的.取而代之的是,我们需要复制一些内容,然后将其展平,以B--CD--E开头,以查看此结果:

Now, the simplest form of git rebase is just git rebase with no arguments at all but that's probably not what we want. Instead, we need to copy—and hence flatten away—some commits starting with either B--C or with D--E, so as to see this result:

     o      o
    / \    / \
o--o   o--o   o--A--D--E--B'-C'  <-- somebranch
    \ /    \ /
     o      o

或这个:

     o      o
    / \    / \
o--o   o--o   o--A--B--C'-D'-E'  <-- somebranch
    \ /    \ /
     o      o

每个带有勾号的提交(例如C')都意味着该提交是通过复制和更改原始文件来完成的.

Each of the commits with a tick-mark (like C') implies that the commit was made by copying-and-changing the original.

我们可能根本不需要更改 B .原始的BA作为其父提交.但是,如果不更改B,则必须将D复制到D'. DD'之间的明显区别是D'C'作为其父级.这意味着我们还必须将E复制到E',这与E非常相似,但是将D'作为其父级.

We might not need to change B at all. The original B has A as its parent commit. If we don't change B, though, we'll have to copy D to D'. The obvious difference between D and D' is that D' has C' as its parent. This means we also have to copy E to E', which is a lot like E but has D' as its parent.

或者,我们可能可以通过将B复制到B'(以E作为其父对象)来完全保留DE.

Alternatively, we might be able to leave D and E alone entirely, by copying B to B' (with E as its parent).

在任何情况下,我们都必须将C复制到C',就像C一样,但是(1)可能具有不同的父级,而(2)压缩了G.

In any case we definitely have to copy C to C', which is like C but (1) maybe has a different parent and (2) has G squashed-in.

实际结果将是以下更复杂的图形之一,类似于我们期望的结果,但更加混乱,因此更难以查看:

The actual result will be one of these more complicated graphs, which is like our desired result, but more cluttered up and thus harder to view:

     o      o      B--C
    / \    / \    /    \
o--o   o--o   o--A      F--G    [abandoned]
    \ /    \ /    \    /
     o      o      D--E--B'--C'  <-- somebranch

(将其与第一个可能的预期结果"进行仔细比较),或者:

(compare this carefully to the first "possible desired result"), or:

                     C'-D'-E'  <-- somebranch
                    /
     o      o      B--C
    / \    / \    /    \
o--o   o--o   o--A      F--G   [abandoned]
    \ /    \ /    \    /
     o      o      D--E

(将其与第二个仔细比较).

(compare this carefully to the second one).

我们将实际获得哪一个取决于我们对git rebase --interactive给出的指示.

Which one we will actually get depends on what instructions we give to git rebase --interactive.

您提供的命令git rebase --interactive 6077ae7不太正确,因为6077ae7是我们在此处绘制的"commit C"提交.您传递给git rebase的参数是应该排除的提交,默认情况下,新提交会在该点之后 出现.我们必须在说明表中具有提交C(实际上是6077ae7),以便我们可以将提交G移到其后并将其设置为squash. (第一个操作永远不能是squash.)我们希望我们的基准排除在外的提交,也要保留我们的副本,实际上是提交A(实际上是7607733).

The command you're supplying, git rebase --interactive 6077ae7, is not quite right, because 6077ae7 is the commit we've been drawing here as "commit C". The argument you pass to git rebase is a commit it should exclude, and by default, the new commits go after that point. We must have commit C (really 6077ae7) in the instruction sheet, so that we can move commit G after it and make it a squash. (The first operation can never be squash.) The commit we want our rebase to exclude, and also have our copies go after, is actually commit A (really 7607733).

因此,所需的命令是:

git rebase -i 7607733

告诉Git为每次提交后 A直到当前提交(G)的每个提交组成一个由pick命令组成的指令表,不包括合并提交().

which tells Git to make up an instruction sheet consisting of pick commands for every commit after A, up to the current commit (G), excluding merge commits (F).

我尚不清楚B--C是在说明书中首先出现还是在D--E之后出现,但这并不重要:BCD的全部,EG将以某种顺序出现在其中,其中B绝对在C之前,D绝对在E之前,而G最后.所以可以是:

It's not totally clear to me whether B--C will show up first, or after D--E, in the instruction sheet, but it doesn't really matter: all of B, C, D, E, and G will be in there, in some order, with B definitely before C, D definitely before E, and G last. So this will be either:

pick bc882a2 yada yada yada
pick 6077ae7 Into this one
pick 4aee207 yede yede yede
pick b645616 yide yide yide
pick 80e2fa1 I want to squash this commit

或者:

pick 4aee207 yede yede yede
pick b645616 yide yide yide
pick bc882a2 yada yada yada
pick 6077ae7 Into this one
pick 80e2fa1 I want to squash this commit

无论它们显示的顺序如何,重新排列现在都是您的工作,以便80e2fa16077ae7之后出现. (在第二种情况下它已经执行了,在第一种情况下,您必须将其向上移动或重新排列其他pick行.)然后,您可以将80e2fa1pick更改为squash,写出指令表格,然后退出编辑器,然后变基将开始其复制(和压扁)操作.

No matter which order they show in, it is now your job to re-arrange them so that 80e2fa1 comes right after 6077ae7. (In the second case it already does, in the first you must either move it up, or reshuffle the other pick lines.) Then you can change the pick for 80e2fa1 to squash, write out the instruction sheet, and exit your editor, and rebase will begin its copy (and squash) operations.

请注意,即使我们要在E之后复制BC,rebase现在也将复制BCDE的所有内容.看来这可能与我们上面的图纸相矛盾,在图纸中,如果需要,我们只能得到副本D'E',以便复制到C'之后.

Note that rebase will now copy all of B, C, D, and E even if we're going to copy B and C after E. This seems like it might contradict our drawings above, where we only got copies D' and E' if they had to, in order to come after C'.

这里的技巧实际上是Git的关键属性:如果将提交复制到自身的逐位相同版本中,则新提交的ID与原始ID相同.犯罪.即使副本中的单个内容都不相同,尽管新提交的ID 不同.

The trick here is actually a key Git property: If a commit is copied to a bit-for-bit identical version of itself, the new commit has the same ID as the original commit. If even a single thing in the copy is different, though the new commit has a different ID.

提交的数据包括作者和提交者,以及一些时间戳. 现在"时间与几秒钟前"的时间不同,因此似乎总是会更改副本–实际上,它总是会被更改,但是代码通过仅按原样重用来检查它是否可以保留完整的原始提交.如果新结果变为A--D--E,几乎没有什么可以阻止它保持原始D--E链完整的 .

The data that go into a commit include the author and committer, and some time stamps. The time "now" is different from the time "a few seconds ago", so it would seem like a copy would always be changed—and in fact, it would always be changed, but the git rebase code checks to see if it can get away with preserving the original commit intact, by just re-using it as is. There's almost certainly nothing to stop it from leaving the original D--E chain intact if the new result goes A--D--E.

在某种程度上,这无关紧要:出于您的目的,您可能不关心是否得到A-D'-E'-B'-C'A-D-E-B'-C'.如果您选择的是A-B-C'-D'-E'版本,您可能不会在乎Git是否仍生产A-B'-C'-D'-E'.但是rebase代码确实会尝试保留原始代码,因为在某些情况下,哈希ID确实很重要.

To some extent, this doesn't matter: for your purposes, you probably don't care if you get A-D'-E'-B'-C' vs A-D-E-B'-C'. If you go for the A-B-C'-D'-E' version you probably would not care if Git produced A-B'-C'-D'-E' anyway. But the rebase code does try to preserve the original if it can, because for some cases, the hash IDs really do matter.

(如果我们非常需要保留A-D-E ID,并且git rebase坚持通过复制到新的时间戳来更改它们,我们可以通过使用git cherry-pick来在提交之上构建新的提交链来完成我们想要的操作E.实际上,我们只能使用git rebase使用其特例的尝试保存ID"代码.实际上,如果有的话,有时是相反的:有时(尽管很少),我们迫切需要 avoid 保留ID,这就是git rebase -i具有--no-ff标志的地方:告诉它 avoid 快速转发分支指针,即避免保留ID.)

(If we desperately needed to preserve the A-D-E IDs, and git rebase insisted on changing them by copying to new timestamps, we could do what we wanted by using git cherry-pick to build our new chain of commits atop commit E. But in fact we can just get git rebase use its special-case "try to preserve IDs" code. In fact, if anything, it is sometimes the opposite: sometimes—albeit rarely—we desperately need to avoid preserving IDs, and this is where git rebase -i has a --no-ff flag: to tell it to avoid fast-forwarding the branch pointer, i.e., to avoid preserving IDs.)

这篇关于从历史记录中删除合并并重新定序非顺序提交的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆