从历史记录中删除合并并重新定序非顺序提交 [英] Removing merges from history and rebasing non sequential commits
问题描述
我有以下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
输出中最早的(最低的行)提交,而标记为A
至G
的提交来自前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--C
或D--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 .原始的B
以A
作为其父提交.但是,如果不更改B
,则必须将D
复制到D'
. D
和D'
之间的明显区别是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
作为其父对象)来完全保留D
和E
.
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
之后出现,但这并不重要:B
,C
,D
的全部,E
和G
将以某种顺序出现在其中,其中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
无论它们显示的顺序如何,重新排列现在都是您的工作,以便80e2fa1
在6077ae7
之后出现. (在第二种情况下它已经执行了,在第一种情况下,您必须将其向上移动或重新排列其他pick
行.)然后,您可以将80e2fa1
的pick
更改为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
之后复制B
和C
,rebase现在也将复制B
,C
,D
和E
的所有内容.看来这可能与我们上面的图纸相矛盾,在图纸中,如果需要,我们只能得到副本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屋!