首页 > 解决方案 > 有什么方法可以直接检出 git 的“修改过的”或“未合并的路径”文件?

问题描述

我知道通过以下命令检查两个修改后的文件还有很长的路要走。

git add <filename>
git reset HEAD <filename>
git checkout <filename>

但是是否有一个命令可以检查所有“未合并路径”?

标签: git

解决方案


您列出的命令序列不会生成--theirs版本。相反,它会在工作树和索引中生成--ours版本。有几种方法可以用更少的命令和多个路径名来实现这一点。

第二种短路方法可能是您应该使用的方法:

git checkout --ours path1 path2 ... pathN

或者:

git checkout --theirs path1 path2 ... pathN

其次git add是那些相同的路径。 非常确定可以丢弃他们或您对合并基础的更改! 特别注意哪个提交合并基础,这样您就知道要保留什么以及丢弃什么。

要缩短这一步,您可以使用git checkout HEADorgit checkout MERGE_HEAD并跳过该git add步骤;但见下文。


这里要理解的关键是,每当 Git 检测到冲突时,Git都会将文件的所有三个版本存储在索引中。在这种情况下,您可以访问这三个版本中的任何一个或全部。例如,这是如何git mergetool获取它称为 BASE、LOCAL 和 REMOTE 的版本,Git 更普遍地将其称为合并 baseHEADor--ours版本,以及另一个 or--theirs版本。

请记住,索引是 Git 保存每个文件副本的地方,这些文件将进入您的下一次提交。(Git 将其称为index,或staging area,有时也称为cache,具体取决于 Git 文档的哪个部分进行调用。)在正常操作中,任何此类文件都只有一个副本。也就是说,如果您有一个名为的文件,该文件README.txt在您签出的提交中并且在您可以处理的工作树中,那么您README.txt在索引中也有一个相同的副本。

工作树中的副本是一个普通文件,格式与您计算机上的任何普通文件相同。存储在HEAD提交中的提交副本是一种特殊的 Git-only 格式,它不仅被压缩而且完全只读:提交的副本永远不会被更改。当然,您可以进行具有不同版本的新的不同提交README.txt,但您这样做的方式是首先将新的和不同的提交复制README.txt到索引中。

请注意,您可以随时查看HEAD提交中的副本,使用git show HEAD:README.txt. 同样,您可以使用 来查看索引中的副本git show :README.txt,其中前导冒号是索引中文件的特殊 Git 语法。

通常,您通过 将新版本复制到索引中git add README.txt,它只是将当前工作树中的任何内容复制到索引/暂存区域中,覆盖(单个)先前的副本。git merge但是,当您在orgit cherry-pickgit revertor或执行to 合并git stash apply操作的任何其他操作期间遇到合并冲突时(我喜欢将其称为merge 作为动词),索引将承担新的角色。

虽然索引通常保存一份. 请记住,合并通过将您的文件版本(您的)与同一文件的某些合并基础版本进行比较,然后将它们的版本与同一合并基础版本进行比较来工作。通过进行这两个单独的比较,Git 可以找出改变了什么,以及它们改变了什么。然后 Git 组合这两组更改,将这两组更改应用于文件合并基本版本。也可以看看README.txtREADME.txtREADME.txtREADME.txtVonC 的答案在这里

如果 Git 不能自己组合这两组更改,那么 Git 在这一点上所做的就是使用它在索引中所谓的阶段槽。README.txtGit 可以存储多达四个版本,而不仅仅是索引中的一个。但是,它最多使用三个,并对它们进行编号:

  • 暂存槽零保存不冲突的:README.txt.
  • 暂存槽 #1 包含合并基础 1:README.txt
  • 暂存槽 #2 包含我们的版本2:README.txt
  • 暂存槽 #3 保存他们的版本 `:3:README.txt。

git status,有或没有--short,显示未合并的文件时,这意味着在暂存槽 1、2 和 3 中有条目。在这种情况下,不使用槽零!

Git 将冲突的副本写入README.txt工作树,以便您可以查看冲突并自行解决,但它还将所有三个输入存储在索引中。您可以检查其中任何一个,但插槽 2 和 3 的语法使其变得简单:

git checkout --ours README.txt

从插槽 2 中提取,而:

git checkout --theirs README.txt

从插槽 3 中提取。所有三个版本都保留在索引中,但现在工作树保存我们的(HEAD版本)或他们的(MERGE_HEAD版本)。

Running会立即git add获取工作树中的任何内容,即使它仍然存在合并冲突,并将其写入插槽 0,从而清除插槽 1 到 3。 这使 Git 相信冲突已解决。

您当然可以一次签出或添加多个文件名,因此您可以使用:

git status --porcelain | grep '^UU ' | cut -f2-

生成所有未合并文件的列表(它们的简短状态是UU),并使用它来制作要提取的文件列表。这种方法不是很健壮(在名称中包含有趣字符或空格的文件存在时失败)但对于简单的情况就足够了:

files=$(git status --porcelain | grep '^UU ' | cut -f2-)
git checkout --theirs $files
git add $files

删除一个步骤

为了缩短这一步,我们可以利用一个关于git checkout. 使用或让 Git 从索引中提取文件(以其仅 Git 格式)到工作树(以正常格式),这不会更改索引本身的任何内容。但是使用Git 从提交中提取文件,而不是从索引中提取文件。当 Git 这样做时,它首先将该文件复制索引中。只有在文件在索引中之后才会将文件复制到工作树。git checkout --ours pathgit checkout --theirs pathgit checkout tree-ish pathgit checkout

将文件复制到索引中具有删除文件的任何阶段 1、2 和 3 槽副本的副作用,将文件写入暂存槽零。因此,如果该文件以前未合并,则它突然变为合并。

请注意,如果我们处于冲突合并中,我们正在合并的提交的哈希 ID 存储在MERGE_HEAD. 如果我们正在挑选樱桃,那么我们挑选的提交的哈希 ID 在CHERRY_PICK_HEAD. (有关它们存储哈希 ID 的位置,请参阅其他命令。)

因此,即使暂存槽 2 中HEAD的副本来自提交,而暂存槽 3 中的副本MERGE_HEAD来自到暂存槽零。效果是解决合并问题,同时还选择我们的 ( ) 或他们的 ( ) 版本的文件。HEADMERGE_HEADHEADMERGE_HEAD

因此,对于没有难命名的文件,命令:

git checkout HEAD $(git status --porcelain | grep '^UU ' | cut -f2-)

做选择的诀窍--ours,而:

git checkout MERGE_HEAD $(git status --porcelain | grep '^UU ' | cut -f2-)

做选择的诀窍--theirsgit merge仅限!)。 同样,除非您确切知道自己在做什么,否则不要使用它。


推荐阅读