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