一种无需删除文件即可创建孤立分支的方法

时间:2019-02-14 22:19:06

标签: git git-branch cherry-pick

当我创建一个孤立分支时,我必须删除所有文件,以便该分支完全为空。我希望能够对空分支进行另一次提交,但是我收到一个错误消息,指出该文件已在一个分支上被修改,但在另一个分支上已被删除。我希望避免手动解决这些冲突,因此最终可以使此过程自动化。有没有一种方法可以自动解决冲突以接受文件的修改版本?

1 个答案:

答案 0 :(得分:1)

我还不清楚您想要什么样的结果,即您真正的目标是什么。但是,无论目标是什么,这可能都是错误的解决方法。


  

当创建一个孤立分支时,我必须删除所有文件,以便该分支完全为空。

这是正确的,但是是错误的。 :-)

更具体地说,让我们来看一下操作的各个部分以及所需的命令:

  1. git checkout --orphan newbranch:这会修改您当前的Git状态,以使您的 index work-tree 保持不变,但是您位于分支{{ 1}},实际上还不存在。

  2. newbranch:这将根据您的 index 中的内容创建一个新的提交。 1 您的工作树无关紧要。如果您所在的分支不存在,例如步骤1中的git commit,则具有创建分支的作用。现在,您所在的分支的尖端是刚刚创建的提交,而您刚进行的新提交的父级是分支上的先前提交-在步骤1产生的状态下,该提交为 no 提交,这样新的提交就没有 父级。

因此,当我说的是对的却是错误的时,我的意思是没有分支是空的。分支(或更准确地说,分支 name )指向一个特定的提交。该提交必须存在!该提交具有某些父项,如果该提交是根提交,则没有父项。如果您使用单词 branch 来表示松散指定的提交链,并以一个特定的提交结尾,则也永远不会是 empty 。您需要做的是清除 index ,因为Git会根据索引中的内容进行新的提交。

大概,您想要的是创建一个其中没有个文件的新提交。那不是一个空的分支!那是一个具有一个提交的分支(新的提交没有父级),其中一个提交具有空的 tree 。另请参见Is git's semi-secret empty tree object reliable, and why is there not a symbolic name for it?,但是对于此空树对象或使用该对象的提交只有有限的用途。


1 如果您不熟悉单词索引(也称为登台区域缓存)的用法,并且工作树,请参见What's the difference between HEAD, working tree and index, in Git?。有关Git和Git用户倾向于合并称为 branch 的内容的更多信息,另请参见What exactly do we mean by "branch"?


  

我希望能够对空分支进行另一次提交,但是我收到一个错误消息,说该文件在一个分支上已被修改,而在另一个分支上已被删除。

由于空分支是没有意义的,因此我必须在这里猜测您的意思:您已经完成newbranch来创建该分支git rm -r .; git commit,该分支指向其树的提交是空树。现在,您有了一个带有一个根提交的孤立分支。如果您运行:

newbranch

Git会删除所有(跟踪的)文件,而索引为空。

如果您现在运行git checkout newbranch ,则会收到很多关于您所描述形式的投诉。那是因为 。让我们绘制Git提交图的一部分:

git cherry-pick <hash>

也就是说,名称... <-F <-G <-H <-- master I <-- newbranch (HEAD) 表示提交master,这是我们的master分支的尖端。提交H的父项(此处每个大写字母代表一个实际的哈希ID)是提交HG的父级是G,其父级在F范围内。

提交...具有 no 父级;这是一个根提交。我们的I附加在名称HEAD上,该名称指向提交newbranch

在这一点上,运行I会产生这些抱怨,原因是它正在将提交git cherry-pick <hash-of-G>作为合并基础,F作为{{1}进行三步合并},而I--ours。三向合并包括运行两个 G,以查看谁做了哪些工作:

  • --theirs:这是他们所做的。 Git将git diff中的快照与git diff --find-renames <hash-of-F> <hash-of-G>中的快照进行比较,以查看它们发生了什么变化。

  • F:这是我们 所做的。 Git将G中的快照与git diff --find-renames <hash-of-F> <hash-of-I>中的快照进行比较,以查看我们发生了什么变化。但是我们使用空树制作了F,所以区别在于我们删除了每个文件!

现在,合并引擎的工作是将我们的更改(删除所有文件)与它们的更改进行合并,无论这些更改是什么。对于他们没有更改的任何文件,Git接受我们的更改并删除了该文件。对于它们 did 更改的任何文件,Git声明合并冲突。除非II中的树匹配,否则我们将确保至少有一个合并冲突,并且自动拾取将停止并让您完成工作。

  

是否有一种自动解决冲突的方法来接受文件的修改版本?

确定:使用F在索引中的各个暂存槽中找到条目。对于第1阶段和第3阶段中存在的任何文件(即在合并基础和G提交中,在此示例中为git ls-files --stage--theirs),插槽中都没有文件2,因为我们删除了所有文件(在此示例中提交F)。您的工作是用单个零级条目替换三个编号较高的登台插槽。您大概想要阶段3(从G提交)到阶段0的文件,并且有一种简单的方法来获得该文件:I工作树中文件的副本。

在这种特殊的冲突情况下,“被我们删除”并被“修改”,Git将修改后的文件保留在工作树以及暂存槽3中。因此--theirs将把工作-tree复制到插槽0中,然后删除插槽1和3中的条目-在这种情况下,插槽2中没有任何要删除的内容。 (如果有的话,git add会删除它,但是如果有的话,当前工作树中的内容可能不合适。)

由于每个文件都是这种情况,因此您可以简单地运行git add来准备索引,然后运行git addgit add .来完成樱桃。 -pick并重新提交:

git cherry-pick --continue

新提交git commit包含提交... <-F <-G <-H <-- master I <-J <-- newbranch (HEAD) 中每个文件的副本,该副本不同于提交J中相同文件的副本。提交G保持着空树,因此几乎没有用。

如果提交F确实是理想的结果,则有一种更简单的 2 方式来获得它:

  1. 创建一个新的空临时索引。
  2. 对于IJ中的每个文件,如果文件匹配,则什么也不做,但是如果它们不同,则更新临时索引以保存F的名称和哈希ID。
  3. 从此临时索引进行新提交。

第2步是唯一困难的部分,但很容易实现自动化:

G

G使git diff-tree -r --find-renames -z | ...automation-program... 递归到子目录中。 -rgit diff-tree是可选的:--find-renames启用重命名检测器(否则,重命名文件从第一次提交中被-z删除,--find-renames被下移到第二个提交)和不同的名称),并且D将差异数据安排为ASCII-NUL终止而不是换行符终止,并避免了“解引用”困难的文件名的需要(请参见the RAW OUTPUT FORMAT section of the git diff-tree documentation )。您必须自己编写自动化程序,但它只能使用从A读取的模式和哈希信息,对每条缓存信息行调用-z

第1步包括导出一个环境变量git update-index --cacheinfo,该变量指向一个不存在的临时文件的名称:Git随其创建。步骤3是运行git diff-tree(相当于GIT_INDEX_FILE的管道),然后使用git commit-tree创建或更新引用名称以引用新的提交。您可以直接为git commit选择父母,也可以完全不选择父母。有关用法,请参阅其文档。


2 好吧,计算上更容易。与仅运行git update-ref相比,显然要进行更多工作。