我在清理我的git历史记录树时遇到问题..
基本上我的git存储库有1个分支:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
F--G--H--I--J--K--L... branch1
提交E是一些不重要的变化,可以变得多余。 这里正确的分裂点应该是我不是D. 提交E可以删除或添加到两个分支..
有什么办法可以实现吗?
----- 更新
哇非常感谢你的帮助! 我尝试过torek建议,它就像一个魅力!现在..我有另一个要清理的存储库,但它更复杂..
看起来像:
1--2--3--4--*A--5--6--7--8--*B--9--10--11--*C--*D--12--13--14 master
\
5--6--7--8--9--10--11--12--13--14 branch1
note1:字母代表提交的内容
note2:* letter 提交仅适用于主分支
有没有办法清理这样的历史树?
另外,对于像这样结构化的项目,推荐的git方法是什么? 即分支后,大多数提交将包含相同的更新,除了一些特定于分支的提交
答案 0 :(得分:3)
首先,关于绘制提交图的问题的+1。 :-)其次,并不是非常重要,但您应该将其视为两个分支master
和branch1
。 master
没有什么特别的,它只是一个像其他任何一个分支。 1
现在,git中所有内容的核心是你永远不能改变提交。这是因为git实际命名每个提交的方式, 2 使用丑陋的40个字符的SHA-1:SHA-1是通过计算内容的加密校验和来实现的。提交的em>。所以,如果你(尝试)改变任何东西,那么"真正的名字"更改,您有一个新的不同的提交。
相反,您可以做的是复制旧的提交到新的提交,在提交最终确定之前进行所需的更改(因此给出了"真名称& #34)。我们在这里做的是复制。
您绘制了此图表:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
F--G--H--I--J--K--L... branch1
但这可能是正确的,因为每次提交都指向其父提交, 3 ,此处F
指向E
当我们看在master
,F
,当我们查看D
时,branch1
点回branch1
。 (我在这里假设每个单个字母代表SHA-1"真实姓名"给定提交。)我将假设当前在{{1虽然它并不重要,但只要您对与这些提交相关联的树感到满意(即,当您git checkout
其中一个时获得的结果)
你想要的是这样的:
A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
这里是" prime" J'
,K'
和L'
中的标记表示这些是提交J
,K
和L
的副本。
虽然我一直在写这个答案,但有些人建议使用git cherry-pick
制作这些副本。这很好 - 但事实上,我们可以使用git rebase
来做这个技巧,因为cherry-pick
是一个简单的斧头,你可以用它来砍伐一棵樱桃树,rebase
是一个全自动链锯,您可以一次性完成所有这些操作。
我们首先告诉rebase
对branch1
进行操作;简单的方法是git checkout branch1
(或者我们可以添加branch1
作为git rebase
的最终参数,但我会使用"简单方法")
接下来,我们需要知道我们希望提交分支的位置,即有一些方法来命名提交I
。从上面我们可以说master~3
:从master
(名称提交Y
)倒计时四次提交,然后逐步执行X
,然后{{1然后到达Z
。或者你可以通过真名SHA-1来实现,它总是有效,但通常需要剪切和粘贴才能正确。对于以下命令,我只需编写 I
。我们将rebase告诉基础I
提交。
最后,我们需要--onto
选择要提交的提交rebase
,J
和K
。有很多方法可以做到这一点。也许最简单的方法是使用L
,它会在git rebase -i
上修改所有内容,然后您可以删除前四次提交的branch1
行。或者,我们可以告诉pick
排除特定提交,但我假设您将使用交互式方法。
因此,这里的最终命令是rebase
(执行git rebase -i --onto I master
命令后)。 git checkout branch1
使这个交互式,以便您可以删除提交; -i
选择--onto
将挑选的新系列提交的目标;并且rebase
部分告诉master
哪些提交要排除:具体来说,任何可以从名称rebase
访问的提交。 (这不包括提交master
,A
,B
和C
,但不包括复制的版本D
,F'
,{{1} } {和G'
出现在H'
上:您只需删除"选择"行。
在I'
完成其一系列branch
命令后,它所做的最后一件事就是将当前分支(git rebase
)的分支标签移动到最尖端的新提交。所以这确实产生了:
git cherry-pick
前提是一切顺利。 (如果情况严重,你可以branch1
停止这个过程并把所有东西都恢复到你开始前的状态。)
我们可能会更加出色并且(假设A--B--C--D--E--F--G--H--I--Z--X--Y... master
\
J'-K'-L'... branch1
为git rebase --abort
)):
I'
甚至没有任何初始branch1~3
,但这假设这里的倒数3是正确的(对于git rebase --onto master~3 branch1~3 branch1
和git checkout
)。这与以前基本相同,有三处修改:
master
以使branch1
作为第一步检查出来branch1
代替rebase
作为参数说明"排除这些内容,只改变那些无法从此处获得的内容" branch1~3
因为我们不需要编辑掉#34;选择"这次命令这需要更仔细地计算提交,以确保所有master
表达式都正确(或者,您可以通过原始SHA-1 ID来处理它)。
1 嗯,关于-i
有一些特殊的事情:对于一个,它是你执行{{1}时所带的分支。它创建了一个新的存储库。另一个是~
略微调整合并消息。但这两者都非常微不足道。
2 在git存储库中找到的所有四种对象实际上都是如此(提交,树,注释标记和" blob" - 最后一个存储文件内容)。每个都存储在存储库中,然后给出一个由其自己的加密校验和组成的名称。
3 更准确地说,每个提交都有零个或多个master
行,每个git init
给出相应父提交的SHA-1 ID。第一个父母通常是"主线"一个分支,以及任何其他父项表明这是一个合并提交。没有父母的提交,例如上面的git merge
,是" root commit"。
答案 1 :(得分:2)
您可以在I
和cherry pick处创建一个新分支branch1
所做的所有更改基本上都是
git branch -b temp I
git cherry-pick J..branch1
然后,您将在temp
处拆分新的分支I
,而不更改E
并包含branch1
的所有提交,如果有J
如果工作正常,您可以重命名这两个以使temp
分支成为您的新branch1
。在挑选樱桃之前,如果你不想在branch1
答案 2 :(得分:1)
执行此操作的一种方法是创建一个新分支作为master的副本,然后执行git reset --hard I-commit
然后cherry-pick
您想要的提交(希望没有太多)