当我创建一个孤立分支时,我必须删除所有文件,以便分支完全为空。我希望能够在空分支上挑选另一个提交,但我收到一个错误,指出该文件已在一个分支上修改,但在另一个分支上被删除。我希望避免手动解决这些冲突,因此我最终可以自动执行此过程。有没有办法自动解决冲突以接受文件的修改版本?
我完全不清楚你想要什么样的结果——也就是说,你的真正目标是什么。 然而,无论这个目标是什么,这可能是实现它的错误方式。
当我创建一个孤立分支时,我必须删除所有文件,以便分支完全为空。
这是真的,但错了。
更具体地说,让我们了解操作的各个部分以及您需要的命令:
-
git checkout --orphan newbranch
:这会修改您当前的 Git 状态,以便您的索引和工作树保持不变,但您位于分支newbranch
上,实际上尚不存在。 -
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)是提交G
。G
的父级是F
,其父级在...
范围内。
提交I
没有父级;它是一个根提交。 我们的HEAD
附在名称newbranch
上,它指向提交I
。
此时,运行git cherry-pick <hash-of-G>
会产生这些抱怨,原因是它正在对提交进行三向合并,F
作为合并基础,I
作为--ours
,G
作为--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 都会声明合并冲突。 除非F
和G
中的树匹配,否则我们保证至少有一个合并冲突,并且樱桃采摘将停止并让您完成工作。
有没有办法自动解决冲突以接受文件的修改版本?
确定:使用git ls-files --stage
在索引中的条目的各个暂存槽中查找条目。 对于存在于阶段 1 和阶段 3 中的任何文件(即在本例中同时位于合并基和--theirs
提交、F
和G
中),插槽 2 中将没有文件,因为我们删除了所有文件(在本例中为提交I
)。 您的工作是将三个编号较高的暂存槽替换为单个阶段零条目。 您可能希望阶段 3 中的文件(来自--theirs
提交)位于暂存槽 0,并且有一种简单的方法可以获取它:git add
工作树中文件的副本。
在这种特殊的冲突情况下,"在我们的中删除"和"在他们的修改"中,Git 将修改后的文件保留在工作树中,以及暂存槽 3 中。 因此,git add
会将工作树副本放入插槽 0 并删除插槽 1 和 3 中的条目 — 在这种情况下,插槽 2 中没有任何要删除的内容。 (如果有,git add
会删除它,但如果有,现在工作树中的内容可能不合适。
由于每个文件都是这种情况,因此您只需运行git add .
来准备索引,然后git cherry-pick --continue
或git commit
来完成挑选并获得新的提交:
... <-F <-G <-H <-- master
I <-J <-- newbranch (HEAD)
新提交J
包含提交G
中每个文件的副本,该副本与提交F
中同一文件的副本不同。 提交I
仍然持有空树,因此几乎无用。
如果提交J
确实是想要的结果,有一种更简单的2种方法可以得到它:
- 创建一个新的空临时索引。
- 对于
F
和G
中的每个文件,如果文件匹配,则不执行任何操作,但如果它们不同,则更新临时索引以保存G
的名称和哈希 ID。 - 从此临时索引进行新提交。
第 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 .
.