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



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

我完全不清楚你想要什么样的结果——也就是说,你的真正目标是什么。 然而,无论这个目标是什么,这可能是实现它的错误方式。


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

这是真的,但错了。

更具体地说,让我们了解操作的各个部分以及您需要的命令:

  1. git checkout --orphan newbranch:这会修改您当前的 Git 状态,以便您的索引工作树保持不变,但您位于分支newbranch上,实际上尚不存在。

  2. git commit:这会从索引中的任何内容创建一个新提交。1你的工作树无关紧要。 如果您所在的分支不存在,例如步骤 1 中的newbranch,则具有创建分支的效果。 您所在的分支的提示现在是刚刚创建的提交,而您刚刚进行的新提交的父级是分支上的上一个提交 — 在步骤 1 出现的状态下,它没有提交,因此新提交没有父提交。

因此,当我说这是对的但错的时,我的意思是没有一个分支是空的。 分支(或更准确地说,分支名称)指向一个特定的提交。 该提交必须存在! 该提交有一些父提交,或者如果该提交是根提交,则没有父级。 如果您使用单词branch来表示松散指定的提交链,以一个特定的提交结尾,那么它也永远不会为空。 你需要做的是清理索引,因为 Git 从索引中的任何内容进行新的提交。

据推测,您想要的是创建一个没有文件的新提交。 那不是空枝! 这是一个具有一个提交的分支(新提交没有父提交),其中一个提交具有空。 参见 git 的半秘密空树对象可靠吗,为什么没有它的符号名称? 但是,这个空树对象或使用它的提交只有有限的用途。


1如果您不熟悉 index(也称为暂存区缓存)和工作树的这种用法,请参阅,例如,Git 中的 HEAD、工作树和索引有什么区别? 有关 Git 和 Git 用户倾向于将称为分支的东西混为一谈的方式的更多信息,请参阅">分支"到底是什么意思?


我希望能够在空分支上挑选另一个提交,但我收到一个错误,指出该文件已在一个分支上修改,但在另一个分支上被删除。

由于空分支毫无意义,我必须在这里猜测你的意思:你已经git rm -r .; git commit创建这个分支newbranch指向一个树是空树的提交。 现在,您有一个具有一个根提交的孤立分支。 如果运行:

git checkout newbranch

Git 会删除所有(跟踪的)文件,留下一个空索引。

如果你现在运行git cherry-pick <hash>你会得到很多关于你所描述形式的抱怨。 那是因为樱桃选择是一种合并。 让我们绘制 Git 提交图的一部分:

... <-F <-G <-H   <-- master
I   <-- newbranch (HEAD)

也就是说,名称master指的是提交H— 我们主分支的尖端。 提交H的父级(此处的每个大写字母代表实际哈希 ID)是提交GG的父级是F,其父级在...范围内。

提交I没有父级;它是一个根提交。 我们的HEAD附在名称newbranch上,它指向提交I

此时,运行git cherry-pick <hash-of-G>会产生这些抱怨,原因是它正在对提交进行三向合并,F作为合并基础,I作为--oursG作为--theirs。 三向合并包括运行两个git diff,以查看谁做了哪些工作:

  • git diff --find-renames <hash-of-F> <hash-of-G>这就是他们所做的。 Git 将F中的快照与G中的快照进行比较,以查看它们更改了哪些内容。

  • git diff --find-renames <hash-of-F> <hash-of-I>这就是我们所做的。 Git 将F中的快照与I中的快照进行比较,以查看我们更改了哪些内容。 但是我们使用空树做了I,所以不同之处在于我们删除了每个文件!

合并引擎现在的工作是将我们的更改(删除所有文件)与其更改(无论这些更改是什么)结合起来。 对于他们没有更改的任何文件,Git 会接受我们的更改并删除该文件。 对于他们更改的任何文件,Git 都会声明合并冲突。 除非FG中的树匹配,否则我们保证至少有一个合并冲突,并且樱桃采摘将停止并让您完成工作。

有没有办法自动解决冲突以接受文件的修改版本?

确定:使用git ls-files --stage在索引中的条目的各个暂存槽中查找条目。 对于存在于阶段 1 和阶段 3 中的任何文件(即在本例中同时位于合并基和--theirs提交、FG中),插槽 2 中将没有文件,因为我们删除了所有文件(在本例中为提交I)。 您的工作是将三个编号较高的暂存槽替换为单个阶段零条目。 您可能希望阶段 3 中的文件(来自--theirs提交)位于暂存槽 0,并且有一种简单的方法可以获取它:git add工作树中文件的副本。

在这种特殊的冲突情况下,"在我们的中删除"和"在他们的修改"中,Git 将修改后的文件保留在工作树中,以及暂存槽 3 中。 因此,git add会将工作树副本放入插槽 0 并删除插槽 1 和 3 中的条目 — 在这种情况下,插槽 2 中没有任何要删除的内容。 (如果有,git add会删除它,但如果有,现在工作树中的内容可能不合适。

由于每个文件都是这种情况,因此您只需运行git add .来准备索引,然后git cherry-pick --continuegit commit来完成挑选并获得新的提交:

... <-F <-G <-H   <-- master
I <-J   <-- newbranch (HEAD)

新提交J包含提交G中每个文件的副本,该副本与提交F中同一文件的副本不同。 提交I仍然持有空树,因此几乎无用。

如果提交J确实是想要的结果,有一种更简单的2种方法可以得到它:

  1. 创建一个新的空临时索引。
  2. 对于FG中的每个文件,如果文件匹配,则不执行任何操作,但如果它们不同,则更新临时索引以保存G的名称和哈希 ID。
  3. 从此临时索引进行新提交。

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

git diff-tree -r --find-renames -z | ...automation-program...

-r使git diff-tree递归到子目录中。--find-renames-z是可选的:--find-renames启用重命名检测器(否则重命名的文件将从第一次提交D删除,A以不同的名称DD到第二次提交),并-z安排差异数据以ASCII-NUL终止而不是换行符终止,并避免需要"取消引号"困难的文件名(请参阅原始输出格式部分)。 您必须自己编写自动化程序,但它可以使用从git diff-tree读取的模式和哈希信息来调用每个缓存信息行的git update-index --cacheinfo

步骤 1 包括导出环境变量GIT_INDEX_FILE指向不存在的临时文件的名称:Git 将随时创建它。 第 3 步是运行git commit-tree,相当于git commit的管道,然后使用git update-ref创建或更新引用名称以引用新提交。 您可以直接选择父项(或根本不选择父项)进行J。 有关用法,请参阅其文档。


2嗯,计算起来更容易。 显然要写更多的工作,而不是只是运行git add ..

相关内容

  • 没有找到相关文章

最新更新