编辑MERGE_HEAD伪造合并是否安全?

时间:2017-08-01 09:13:36

标签: git git-merge

我有两个分支,由于某些原因,它们的合并非常繁琐。有时候我想添加一个合并提交来表示我已经从一个分支到另一个分支获取了一些信息,例如我检查了一个文件。

由于使用git merge会产生很多合并冲突,我唯一感兴趣的是创建一个包含两个父级的提交,编辑是否安全{{1}并提交?我的意思是,如果将来使用MERGE_HEADgit rebase,我是否会面临奇怪的错误?

2 个答案:

答案 0 :(得分:3)

我不会使用MERGE_HEAD这样做,因为没有关于它将来如何继续发挥作用的承诺。

但是,您可以使用git commit-tree构建任何您想要的任意历史记录,这是一个需要的管道命令:

  • 树形哈希ID(要为新提交记录的源树),
  • 零个或多个父哈希ID(新提交的父级)和
  • 提交消息(用于新提交)。

结果是具有给定父项的提交对象。例如,给定一个部分看起来像树的树

        o--o--o   <-- br1
       /
o--o--o--o   <-- br2
    \
     o--o--o   <-- br3

我们可以选择我们喜欢的任何两个,三个,四个或任意数量的提交哈希值,并进行新的合并提交。让我们选择最上面的中间提交和最下面的行最右边的提交。我们将使用br2小费的树作为实际树:

$ hash=$(git commit-tree -m 'mystery merge' -p br1~1 -p br3 br2)

我们现在在$hash中拥有此新合并提交的哈希ID。没有分支名称指向它,所以让我们创建一个新名称:

$ git branch br4 $hash

,并提供:

        o--o--o   <-- br1
       /    \
o--o--o--o   o   <-- br4
    \       /
     o--o--o   <-- br3

(分支br2仍在那里指向中间的一个节点,我只是没有空间来绘制它。)

由于我们使用br2作为(快照)的来源进行保存,如果我们现在运行:

$ git diff br2 br4

我们什么都得不到:两个提交具有相同的树(但是不同的父哈希ID,当然br4提示的提交消息是&#34;神秘合并&#34;)

这影响未来操作的方式是,当Git去计算新提交的合并基础或新提交的下游任何提交时,Git将遵循我们刚刚作为新提交的一部分做出的父链接。对于大多数的挑选操作而言,这几乎没有什么效果,因为挑选一个提交与其直接父级相反的提交。

请注意,当您进行正常的,普通的,每天合并时,Git正在做的是:

  • 进行两次特定提交,一次是您当前的分支提示 L (本地或左侧或--ours),另一次是您提到的提交,通常是另一个分支提示, R (偏远或右或--theirs);
  • 通过提交图找到他们的合并基础 B ,就像我们上面提到的那样;
  • 运行两个git diffsgit diff B Lgit diff B R
  • 合并两个差异结果并将其应用于 B 以获取新的(Git将该树以扁平形式存储在索引中,然后使用用于编写树对象的C代码等效于git write-tree;
  • 使用上一步中的树形哈希和两个父哈希 L R 按顺序编写合并提交,就像git commit-tree一样;最后
  • 移动当前分支名称,使其指向新的合并提交。

如果你通过任何方式制作自己的树,Git认为这是合并这两个其他提交的完整而正确的结果。未来git merge会看到合并并使用它来通知它下一个合并的合并基础提交 B 应该是。如果我们回到上面的图纸,而不是创建 new 分支名称br4,而是移动分支名称br3以适应新的提交,我们得到:< / p>

        o--o--o   <-- br1
       /    \
o--o--o--o   \   <-- br2
    \         \
     o--o--o---o   <-- br3

这意味着就Git而言,正确的结果是将左上角中间提交(br1~1)合并到br3的上一个提示中(现在{ {1}} - 这有点奇怪;请参阅下面的注释)是位于br3^2尖端的源树。这意味着如果我们现在运行:

br2

git merge br1 上时,Git将通过从br3的提示向后移动来找到合并基础,以找到两个分支上的第一个提交。这是我们的br3。从那时起有一个提交,即br1~1的提示,因此我们将br1br1~1区分开来(产生这两个提交之间的任何不同),作为&#34;什么&#39;自从合并基础&#34;以来,在br3中,然后将br3br1~1区分为&#34;自合并基数&#34;以来,br1中的内容是什么?然后Git尝试像往常一样组合这些,然后在br1上进行新的合并提交。

简而言之,您需要知道自己在做什么:您告诉Git使用您喜欢的任何树作为您构建的合并的正确结果,这会影响将来的合并。

注意:当我们进行合并时,我们选择br3作为第一个父级和br1~1(之前的值)作为第二个 。如果我们要合并这样的合并br3,这是一个不寻常的顺序:它有点倒退。它实际上声明br3是&#34;主线&#34;有人应该稍后关注。这没有什么根本的错误&#34;向后合并&#34; -the 是我们选择的任何东西,无论哪个&#34;方向&#34;合并需要 - 但如果您计划稍后使用br1~1查看此主要工作,则您会看到--first-parent作为此br1~1链接。

--first-parent命令进行这些&#34;反转&#34;合并,有些人称之为foxtrot merge。当然它的合并使用的是正常的合并基础和合并过程,而不是一些任意选择的树。但这意味着git pull所做的合并是#34;向后&#34;当试图跟随git pull链接时,这是人们可能希望避免--first-parent的另一个原因。)

答案 1 :(得分:1)

据我了解,你想从另一个分支“挑选”一些信息,是吗?例如:我在功能中更改了文件 foo bar baz ,并希望“合并” foo 进入主人而不需要 bar baz 。我认为有更好的可能性,然后搞乱MERGE_HEAD

使用git cherry-pick

git cherry-pick --no-commit -x feature
git reset -p # Interactive reset similar to git add -p
Unstage foo? N
Unstage bar? Y
Unstage baz? Y
git commit

你最终会提交一个 foo 的提交,上面写着从...中挑选出来的消息然而,git reset -p允许取消任何任何一块文件。您也可以从另一个分支中选择一行。

使用git checkout

git checkout feature -- path/to/foo
git commit

这将检查分支功能中可用版本中的foo。 在这种情况下,您必须编写自己的提交消息。但是您可以通过将以下内容添加到.git/config文件中来为其创建git别名。

[alias]
    pick-file = !git checkout $1 -- $2 && git commit -i --edit -m \"Picked $2 from $1\"

用法:git pick file branch file

使用git show

例如:

git show feature:path/to/file > file

然后,您可以使用git add暂存整个文件,或git add -p来决定您需要哪些行。同样,你必须在那之后提交。您还可以为此任务创建别名。