Git分支分裂点

时间:2014-08-29 07:46:12

标签: git git-branch

我在清理我的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方法是什么? 即分支后,大多数提交将包含相同的更新,除了一些特定于分支的提交

3 个答案:

答案 0 :(得分:3)

首先,关于绘制提交图的问题的+1。 :-)其次,并不是非常重要,但您应该将其视为两个分支masterbranch1master没有什么特别的,它只是一个像其他任何一个分支。 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当我们看在masterF,当我们查看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'中的标记表示这些是提交JKL的副本。

虽然我一直在写这个答案,但有些人建议使用git cherry-pick制作这些副本。这很好 - 但事实上,我们可以使用git rebase来做这个技巧,因为cherry-pick是一个简单的斧头,你可以用它来砍伐一棵樱桃树,rebase是一个全自动链锯,您可以一次性完成所有这些操作。

我们首先告诉rebasebranch1进行操作;简单的方法是git checkout branch1(或者我们可以添加branch1作为git rebase的最终参数,但我会使用"简单方法")

接下来,我们需要知道我们希望提交分支的位置,即有一些方法来命名提交I。从上面我们可以说master~3:从master(名称提交Y)倒计时四次提交,然后逐步执行X,然后{{1然后到达Z。或者你可以通过真名SHA-1来实现,它总是有效,但通常需要剪切和粘贴才能正确。对于以下命令,我只需编写 I 。我们将rebase告诉基础I提交。

最后,我们需要--onto选择要提交的提交rebaseJK。有很多方法可以做到这一点。也许最简单的方法是使用L,它会在git rebase -i上修改所有内容,然后您可以删除前四次提交的branch1行。或者,我们可以告诉pick排除特定提交,但我假设您将使用交互式方法。

因此,这里的最终命令是rebase(执行git rebase -i --onto I master命令后)。 git checkout branch1使这个交互式,以便您可以删除提交; -i选择--onto将挑选的新系列提交的目标;并且rebase部分告诉master哪些提交要排除:具体来说,任何可以从名称rebase访问的提交。 (这不包括提交masterABC,但不包括复制的版本DF',{{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)。这与以前基本相同,有三处修改:

  1. 我们在最后添加master以使branch1作为第一步检查出来
  2. 我们使用branch1代替rebase作为参数说明"排除这些内容,只改变那些无法从此处获得的内容"
  3. 我们放弃branch1~3因为我们不需要编辑掉#34;选择"这次命令
  4. 这需要更仔细地计算提交,以确保所有master表达式都正确(或者,您可以通过原始SHA-1 ID来处理它)。


    1 嗯,关于-i有一些特殊的事情:对于一个,它是你执行{{1}时所带的分支。它创建了一个新的存储库。另一个是~略微调整合并消息。但这两者都非常微不足道。

    2 在git存储库中找到的所有四种对象实际上都是如此(提交,树,注释标记和" blob" - 最后一个存储文件内容)。每个都存储在存储库中,然后给出一个由其自己的加密校验和组成的名称。

    3 更准确地说,每个提交都有零个或多个master行,每个git init给出相应父提交的SHA-1 ID。第一个父母通常是"主线"一个分支,以及任何其他父项表明这是一个合并提交。没有父母的提交,例如上面的git merge,是" root commit"。

答案 1 :(得分:2)

您可以在Icherry pick处创建一个新分支branch1所做的所有更改基本上都是

 git branch -b temp I
 git cherry-pick J..branch1

然后,您将在temp处拆分新的分支I,而不更改E并包含branch1的所有提交,如果有J如果工作正常,您可以重命名这两个以使temp分支成为您的新branch1。在挑选樱桃之前,如果你不想在branch1

中,你可以恢复提交E.

答案 2 :(得分:1)

执行此操作的一种方法是创建一个新分支作为master的副本,然后执行git reset --hard I-commit然后cherry-pick您想要的提交(希望没有太多)