我正在尝试从另一个分支合并(如果重要的话,它是一个孤立的分支)。但是,当我执行以下操作时:
git merge <branch-name>
它似乎正确合并。但是,如果我执行以下操作:
git checkout --merge <branch-name> -- <file-names>
当前分支上的大多数(如果不是全部)更改都会被清除。无论我使用--merge
,--ours
还是--theirs
,结果都是一样的。
我原以为checkout
使用--merge
标志的时候会做同样的事情merge
,除了指定的文件。
这是怎么回事?有什么我不明白的吗?
见的git merge-file
命令,这确实让你做你想要的。
的-m
或--merge
以标志git checkout
具有多种不同的含义。
与以下一起使用时:
git checkout -m <commit-specifier>
它或多或少具有您想要的含义;问题是它适用于所有路径。
与以下一起使用时:
git checkout -m [--] <paths>
它有不同的含义:这意味着 Git 应该,对于 中的每个命名路径<paths>
,在具有(或具有)多个更高阶段索引条目的文件的工作树副本中重新创建合并冲突。
这里有一个更基本的问题。其中一部分只是棘手的用语——例如,我们都说“工作树中的变化”——但另一部分在于如何思考 Git 的作用:
...当前分支上的大部分更改(如果不是全部)都被清除了
这表明您正在考虑每个文件的工作树副本中的内容作为更改,而实际情况并非如此。Git 不会在任何地方存储更改,1并且文件的工作树副本主要供您根据需要使用:Git 主要使用快照,文件以我喜欢称之为冻结干燥的格式存储在blob 对象中与提交相关联并在索引中。
这里是一个概念,当前分支,也当前提交,但分支仅仅是一个名称(存储在HEAD
),而提交的是一个commit对象,通过它的哈希ID标识(存储在分支名),永久(大部分)和不可变(完全)。提交包含——间接地——每个源文件的完整快照。索引也是 Git 中的关键,它也存储快照,但与提交不同的是,索引中的内容是可变的。
同时,每个提交都存储了一组父提交的哈希ID——通常就是一个这样的提交。当您让 Git 向您展示一些提交时,Git 实际上会从父项和提交本身中提取所有文件,然后2比较(其中的所有文件)两次提交并显示不同之处。因此,当您查看提交时,它似乎发生了变化。
Git 对索引执行相同的技巧:它比较当前提交与索引,显示差异并调用为 commit 暂存的更改。然后,它比较指数基本上是快照,你求婚会是下一个承诺,如果你跑了git commit
,现在到工作树。无论索引和工作树之间有何不同,Git 都会显示这些差异,称这些更改未暂存为 commit。但是在所有三组文件中——提交的文件、索引中的文件和工作树中的文件——实际上没有变化,而是快照。
什么git checkout
通常做的那样-有一堆的例外,因为git checkout
实在是多个不同的命令都塞进一个面向用户的动词是提取文件从提交快照,写这些文件到指数(使指数和提交匹配)然后将索引副本写入工作树(以便索引和工作树匹配)。但在执行任何操作之前,它首先检查以确保您不会丢失任何未保存的工作,方法是将当前提交与索引以及索引与工作树进行比较:如果这两者不匹配,则有有什么东西git checkout
会破坏。
git checkout -- <paths>
但是,一旦您使用该模式,您实际上就会切换到完全不同的后端操作。此操作不是从提交开始,而是从索引开始。这些文件在过去的某个时间从提交复制到索引,因此索引具有一些文件集。该集合可能自上次正常检出或硬重置或其他任何内容以来已更新:everygit add
意味着将文件从工作树复制到 index,如果工作树文件与索引副本不匹配,那么现在它这样做索引中的文件集已更改。索引甚至可能有非零阶段条目,表示来自不完整git merge
. 在这种情况下,索引本质上存储的不是一个而是三个一些文件的冻干副本,从三个输入到更早的git merge
操作。3但是,无论如何,这种类型git checkout
根本不会回到提交:它只是从索引中获取文件并写入它们,或者-m
重新合并它们,并破坏工作树中的任何内容。它在没有首先询问是否可以的情况下这样做。4
(编辑:还有git checkout --patch
,但这实际上调用了第三种模式。补丁操作,它比较文件的两个版本,并让您选择此差异的一部分以应用于两个版本之一,实际上是由 Perl 程序处理的运行git diff
在两个版本之间。这个工具git checkout --patch
,git add --patch
,git stash --patch
,和git reset --patch
)。
无论如何,底线是git checkout -m -- path
不做你想做的。你可以得到你想要的,但不能使用git checkout
. 相反,您需要做的是提取您想要传递给的三个输入文件——git merge
将这三个文件放在任何地方;他们甚至不需要在工作树库本身,然后运行该git merge-file
命令在他们身上。
1好吧,除非您存储 的输出git diff
,或者作为特殊情况,保存的合并的每个部分都与 发生冲突git rerere
,但所有这些都低于正常的可见性级别。
2由于内部冻干文件格式,Git 实际上不必费心提取相同的文件,只提取那些至少有一点不同的文件。
3从技术上讲,每个文件最多三个条目。例如,在修改/删除冲突等情况下,对于某个文件,您将只有两个条目。此外,当您完成合并冲突和git add
文件的解决后,更高阶段的条目将消失。但是,在您提交之前,那些更高阶段的条目会存储在一个秘密的、不可见的“REUC”类型的索引条目中,以便您可以使用它git checkout -m
来恢复冲突。无法查看或保存这个不可见条目,这是当前索引格式的几个缺陷之一。
4从人性化设计的角度来说,这个特别不好,因为其他形式的git checkout
很小心,不丢工作。
本文收集自互联网,转载请注明来源。
如有侵权,请联系[email protected] 删除。
我来说两句