简体   繁体   English

将 mergetool 设置为仅打开某些扩展名并为其他人使用本地文件

[英]Set mergetool to only open certain extensions and use local files for others

I'm trying to merge two branches that have .py and .png files.我正在尝试合并两个具有.py.png文件的分支。 Is there a way to set all the .png files to be local and only open with mergetool the .py files?有没有办法将所有.png文件设置为本地文件,并且只使用 mergetool 打开.py文件? I already did git checkout --ours / --theirs to choose the correct .png files to conserve but every time I open the mergetool these files keep popping up.我已经执行了git checkout --ours / --theirs以选择要保存的正确.png文件,但每次我打开合并工具时,这些文件都会不断弹出。 What am I missing here?我在这里错过了什么?

The git mergetool command runs over the unresolved files by default, so the simplest thing is to mark the .png files as "correctly resolved" first . git mergetool命令默认在未解析的文件上运行,所以最简单的方法是首先.png文件标记为“正确解析”。 See the note below, in the longer DETAILS section, for more about this.有关详细信息,请参阅下面较长的“详细信息”部分中的注释。

That said, you can also supply pathnames to git mergetool , so you can list precisely those unresolved .py files.也就是说,您还可以提供git mergetool的路径名,这样您就可以准确列出那些未解析的.py文件。 That's just a small matter of shell scripting:这只是 shell 脚本的一个小问题:

git ls-files --unmerged

produces the list of such files (with, alas, all the --stage data as well), which you can then filter using, eg, awk :生成此类文件的列表(唉,还有所有--stage数据),然后您可以使用例如awk进行过滤:

git ls-files --unmerged | awk '$4 ~ /\.py$/ { print $4 } ' | uniq

(it's definitely possible to put the uniq into the awk code, but simpler just to run uniq ). (绝对可以将uniq放入awk代码中,但仅运行uniq更简单)。 Verify that this produces the right list of files;验证这会生成正确的文件列表; it's then trivial to have the shell expand this into place:然后将 shell 扩展到位就很简单了:

git mergetool $(git ls-files --unmerged | awk '$4 ~ /\.py$/ { print $4 } ' | uniq)

Details细节

When you run git merge —or indeed, any of the various commands that use Git's merge engine, including git cherry-pick for instance—and there is a conflict, Git leaves extra information in what Git calls the index or staging area .当您运行git merge — 或者实际上是使用 Git 合并引擎的各种命令中的任何一个,例如git cherry-pick — 并且存在冲突时,Git 会在 Git 调用的索引暂存区中留下额外的信息。 These are two names for the same thing, and there's a third name, now largely obsolete, but it still shows up in flags: the cache , as in git rm --cached or git diff --cached .这是同一事物的两个名称,还有第三个名称,现在基本上已过时,但它仍然出现在标志中:缓存,如git rm --cachedgit diff --cached (Some commands, including git diff , accept --staged as a synonym, but git rm still doesn't even as of Git 2.39.) (一些命令,包括git diff ,接受--staged作为同义词,但git rm仍然没有 Git 2.39。)

Normally—as in, when not in the middle of a merge conflict—Git's index aka staging area contains a full copy of every file in the form it will have if you run `git commit right now .通常——当不在合并冲突的中间时——Git 的索引又名暂存区包含每个文件的完整副本,如果你现在运行 `git commit ,它将具有的形式。 1 But merging involves reading three versions of each file: 1但合并涉及读取每个文件的三个版本

  • the merge base version is the one each "side" started with;合并基础版本是每个“方面”开始的版本;
  • the --ours version is the one "we" had in "our" commit when we started our git merge ; --ours版本是我们开始git merge时“我们”提交的版本; and
  • the --theirs version is the one "they" had in "their" commit when we started our git merge . --theirs版本是我们开始git merge时“他们”在“他们的”提交中的版本。 2 2个

Git starts by looking at all three of these versions. Git 首先查看所有这三个版本。 If all three match , there's no problem, and the merge result is any of the three versions.如果三个都匹配,没有问题,合并结果是三个版本中的任何一个。 If two match and one is different, there's again no problem.如果两个匹配而一个不同,则再次没有问题。 The usual problem comes in when all three are different .三个都不同时,通常会出现问题。 (There can also be some other problems, eg, when both "sides" created a new but different file with the same name, but we'll ignore these cases for simplicity.) (也可能存在一些其他问题,例如,当“双方”都创建了一个新的但不同的同名文件时,但为了简单起见,我们将忽略这些情况。)

When all three files are different, Git really does have to merge some work.当三个文件都不同时,Git 确实需要合并一些工作。 For plain-text files, Git does a line-by-line diff of the base version against each of the branch-tip versions.对于纯文本文件,Git 对基本版本与每个分支提示版本进行逐行比较。 This results in two separate diffs .这导致两个不同的差异 Git then tries to combine the two diffs, applying both sets of changes to the base version. Git 然后尝试组合两个差异,将两组更改应用到基本版本。

If all goes well—if Git believes it successfully combined "our changes" with "their changes", in other words—Git then treats the resulting merged file as the correct merged result, and writes that file to your working tree, so that you can see it, and to its own index, ready for committing.如果一切顺利——如果 Git 认为它成功地将“我们的更改”与“他们的更改”组合在一起,换句话说——Git 会将生成的合并文件视为正确的合并结果,并将该文件写入你的工作树,以便你可以看到它,以及它自己的索引,准备提交。 It's when things go less-well that you get a merge conflict.当事情 go 不太好时,您会遇到合并冲突。

Again, remember that Git's index / staging-area normally holds a copy of every file .同样,请记住 Git 的索引/暂存区通常包含每个文件的副本 So if Git is able to merge two sets of changes to, say, README.txt , Git will put the merged version of README.txt into both its index (ready to be committed) and your working tree (so that you can see what Git did).因此,如果 Git 能够将两组更改合并README.txt ,Git 会将合并后的README.txt版本放入其索引(准备提交)和您的工作树(以便您可以看到Git 做了)。 The index copy is at what Git calls "stage zero".索引副本处于 Git 所说的“零阶段”。 But if the merge goes wrong, here's what Git does:但如果合并出错,Git 会执行以下操作:

  • Git puts the base version into the index in "staging slot 1" for this file name; Git 将基本版本放入此文件名的“暂存槽 1”中的索引中;
  • Git puts the --ours version into the index in "slot 2" for this file name; Git 将--ours版本放入此文件名的“插槽 2”中的索引中; and
  • Git puts the --theirs version into the index in "slot 3" for this file name. Git 将--theirs版本放入此文件名的“插槽 3”中的索引中。

The result is that the index now has three files named, eg, README.txt or file.py or image.jpg .结果是索引现在有三个文件,分别命名为README.txtfile.pyimage.jpg

If the file is text and Git attempted a merge, Git puts its best attempt at merging, plus conflict markers, into your working tree, under the file's name ( README.txt or whatever, again).如果文件是文本文件并且 Git尝试合并,则 Git 会在文件名( README.txt或其他名称)下将其最佳合并尝试和冲突标记放入您的工作树中。 If the file is not text, such as image.jpg , Git leaves some version in your working tree.如果文件不是文本,例如image.jpg , Git 会在您的工作树中留下一些版本。 Git doesn't put conflict markers in because binary files don't have "lines" in the first place. Git 没有放入冲突标记,因为二进制文件首先没有“行”。

In all of these cases it is your job to resolve the conflicts .在所有这些情况下,您的工作就是解决冲突 You do this by picking out the right merged result and stuffing that into Git's index at "slot zero", erasing the three conflicting versions.为此,您可以挑选出正确的合并结果并将其填充到 Git 索引的“槽零”处,同时擦除三个冲突的版本。 For a file like file.py , you might open the working tree copy in your editor, for instance, and edit it and hand-resolve the conflict.例如,对于file.py类的文件,您可以在编辑器中打开工作树副本,然后对其进行编辑并手动解决冲突。 You can then write out the updated file.py and run:然后你可以写出更新后的file.py并运行:

git add file.py

This tells Git: Erase the three nonzero-slot entries, and copy the working-tree version of file.py into the index at slot zero.这告诉 Git:擦除三个非零槽条目,并将file.py的工作树版本复制到槽零处的索引中。 The file is now "resolved".该文件现在已“解决”。

Git allows the merge to complete—that is, you'll run git merge --continue or perhaps simply git commit —once all files in Git's index are back at "slot zero". Git 允许合并完成——也就是说,你将运行git merge --continue或者简单地运行git commit commit——一旦 Git 索引中的所有文件都回到“槽零”。 Until then, any file at any nonzero slot number is "unresolved".在那之前,任何非零槽号的文件都是“未解析的”。 In most cases, if there's a file in "slot 1", there's a file of the same name in "slot 2" and "slot 3" as well.在大多数情况下,如果“slot 1”中有文件,则“slot 2”和“slot 3”中也会有同名文件。 (The cases where this isn't true are ones we are not covering here.) (这不是真的情况是我们在这里没有涉及的情况。)

It's the git add command or—if you want to remove the file—a git rm command that resolves the file, by erasing the nonzero slots and writing the correct to-be-committed file to Git's index at slot zero.它是git add命令,或者——如果你想删除文件git rm命令解析文件,通过擦除非零槽并将正确的待提交文件写入 Git 索引的槽零。 Until you git commit , you can then overwrite the slot-zero file with another (also slot-zero) file, so you can do a first pass at resolving and git add and then test, if you like: there's no need to keep files unresolved.在你git commit之前,你可以用另一个(也是 slot-zero)文件覆盖slot-zero 文件,所以你可以在解析时做第一遍,然后git add然后测试,如果你愿意:没有必要保留文件未解决。 But some people do like to keep them unresolved until the very end, so if you are one of those people, just remember how this works: whatever is in slot zero is what gets committed, and git commit won't let you commit until everything that's in Git's index is in there at slot zero.但是有些人确实喜欢让它们直到最后才解决,所以如果你是这些人中的一员,请记住它是如何工作的:插槽 0 中的任何内容都是提交的,而git commit不会让你提交直到一切Git 索引中的那个在槽零处。

The point of the staging area is partly to allow these nonzero slots, and partly to allow you to change your mind about what files are in slot zero.暂存区的意义部分是为了允许这些非零槽,部分是为了让您改变对槽零中的文件的看法。 You use git add to copy things into Git's index (at slot zero, always) and they're now ready-to-be-committed, but they just sit there, being ready, until you actually run git commit .您使用git add将内容复制到 Git 的索引中(始终位于零槽),它们现在已准备好提交,但它们只是坐在那里,准备就绪,直到您实际运行git commit If you git add again, you replace some file(s) with newer versions;如果你git add ,你一些文件替换为较新的版本; if you do this before you git commit , the versions you replaced never get committed.如果您在git commit之前执行此操作,则您替换的版本永远不会提交。

With that in mind, we can now look at git mergetool , and also some special weirdness with git checkout and git restore .考虑到这一点,我们现在可以查看git mergetool ,以及git checkoutgit restore的一些特殊怪异之处。


1 Technically, what's in the index is actually a blob hash ID: the index holds pre-compressed, pre-de-duplicated "copies" of files, which take no space if they're literally the same as any existing committed file. 1从技术上讲,索引中的内容实际上是一个 blob hash ID:索引包含预压缩、预去重的文件“副本”,如果它们与任何现有提交的文件完全相同,则不占用空间。 But you can just think of them as actual copies of files: except for using no disk space, they behave like copies of files.但是您可以将它们视为文件的实际副本:除了不使用磁盘空间外,它们的行为类似于文件副本。

2 Note that for git cherry-pick , the ours and theirs names still make sense. 2请注意,对于git cherry-pick ,我们和他们的名称仍然有意义。 The merge base version is the parent of the "theirs" commit.合并基础版本是“他们的”提交的父级。 Since git rebase works by repeated cherry-picking, this also makes sense, except that git rebase starts by checking out "their" branch-tip commit.由于git rebase通过反复挑选工作,这也是有道理的,除了git rebase检查“他们的”分支提示提交开始。 It then uses git cherry-pick on each of "our" commits.然后它在每个“我们的”提交上使用git cherry-pick This causes the ours and theirs relationships to change.这导致我们和他们的关系发生变化。 That's a matter for a separate discussion, though.不过,这是一个单独讨论的问题。


Mergetool works by reading the index Mergetool 通过读取索引来工作

You run git mergetool after you run git merge and get merge conflicts.在运行git merge运行git mergetool并得到合并冲突。 What git mergetool does is look for those nonzero-staging-slot entries in Git's index. git mergetool所做的是Git 的索引中查找那些非零暂存槽条目。 Files with a nonzero staging number are unresolved .暂存号非零的文件是未解析的。

If you run git mergetool and resolve some files, and/or use git add to resolve some files, and then interrupt the git mergetool run and start another git mergetool run, Git starts over, listing the unmerged files.如果您运行git mergetool并解析一些文件,和/或使用git add解析一些文件,然后中断git mergetool运行并启动另一个git mergetool运行,Git 重新开始,列出未合并的文件。 If that list of files is now smaller , those are the only ones still unmerged.如果该文件列表现在变小了,那么只有这些文件还没有合并。

Hence, if you have some *.jpg files that you can resolve, you can do that first:因此,如果您有一些可以解析的*.jpg文件,您可以先这样做:

git checkout --ours foo.jpg
git checkout --theirs bar.jpg
git add foo.jpg bar.jpg         # these two files are now resolved

Running git mergetool at this point will not attempt to merge bar.jpg and foo.jpg because they're not unresolved any more.此时运行git mergetool将不会尝试合并bar.jpgfoo.jpg ,因为它们不再是未解析的。

When git mergetool brings up your actual merge tool (whatever that may be), you're supposed to resolve the conflicts in that merge tool, and then exit the merge tool to tell git mergetool that it is done.git mergetool启动您的实际合并工具(无论可能是什么)时,您应该解决该合并工具的冲突,然后退出合并工具以告诉git mergetool它已完成。 The git mergetool command will then run git add for you on that file.然后git mergetool命令将在该文件上为您运行git add 3 That's how git mergetool can later pick up where you left off, after you interrupt it. 3这就是git mergetool稍后可以在您中断后从您离开的地方继续执行的方式。

This brings us to some oddities with git checkout , some of which are cleaned up in git restore .这给我们带来了一些奇怪的git checkout ,其中一些在git restore中被清理。 The checkout and restore commands have flags: checkout 和 restore 命令有标志:

  • --ours means get the file from slot 2 . --ours表示从插槽 2 获取文件
  • --theirs means get the file from slot 3 . --theirs表示从插槽 3 获取文件
  • There's one missing here: there should probably be a --base to mean slot 1 , but it's just not there.这里缺少一个:可能应该有一个--base来表示slot 1 ,但它不在那里。

These options tell git checkout and git restore to read the index copy from the given slot, and write that out to the working tree copy of that file.这些选项告诉git checkoutgit restore从给定的槽中读取索引副本,并将其写入该文件的工作树副本。 These options don't do anything to the index itself, so the file remains unresolved.这些选项索引本身没有任何作用,因此文件仍未解析。

You can, however, also run git checkout commit -- path .但是,您也可以运行git checkout commit -- path This option tells git checkout to reach into the specified commit and fine the committed copy of the specified path , and git checkout does this by writing the file to slot zero of the index first.选项告诉git checkout到达指定的commit并修改指定path的已提交副本,而git checkout通过首先将文件写入索引的零槽来执行此操作。 This action erases slots 1, 2, and 3. So this kind of git checkout marks the file resolved!此操作会删除插槽 1、2 和 3。因此,这种git checkout出标记文件已解决!

With git restore , you can do the same thing, but the places that git restore writes to are specified by the --worktree (or -W ) and --staged (or -S ) flags.使用git restore ,您可以做同样的事情,但git restore写入的位置由--worktree (或-W )和--staged (或-S )标志指定。 So:所以:

git restore -SW -s HEAD -- path/to/file

tells git restore to extract our ( HEAD ) version of the file and write that to both the index ( -S ) and your working tree ( -W ).告诉git restore提取文件的 ( HEAD ) 版本并将写入索引 ( -S ) 和您的工作树 ( -W )。 So this too resolves the file, like git checkout HEAD -- path/to/file would.所以这也解析了文件,比如git checkout HEAD -- path/to/file会。 Leaving out the -S means that git restore won't mark the file resolved.省略-S意味着git restore不会将文件标记为已解决。

You might wonder why this is all so complicated.您可能想知道为什么这一切都如此复杂。 The answer is in part "because Git just grew over time", ie, this wasn't actually planned , it just happened by mistake.部分答案是“因为 Git 只是随着时间的推移而增长”,也就是说,这实际上并不是计划好的,只是错误地发生了。 It's also in part "because Git commands try to be flexible tools": git restore in particular is more flexible than git checkout as it can write, separately, to Git's index or your working tree or both .它也部分是“因为 Git 命令试图成为灵活的工具”:特别是git restoregit checkout更灵活,因为它可以单独写入 Git 的索引您的工作树或两者 The old, more-confusing git checkout command writes to Git's index if extracting from a commit, and always writes to your working tree.如果从提交中提取,旧的、更令人困惑的git checkout命令写入 Git 的索引,并且总是写入你的工作树。

Last, if you want to "un-mark" a file as resolved—ie, to return it to its unresolved state, both git checkout and git restore have an option -m for doing this.最后,如果您想将文件“取消标记”为已解决——即,将其返回到其未解决的git checkoutgit restore都有一个选项-m来执行此操作。 Note that it destroys any working tree work you've done, and -m means something different to git checkout if you're not in "merge mode" (which to me is yet another reason to avoid the old git checkout command, using git switch and git restore instead).请注意,如果您处于“合并模式”,它会破坏您所做的所有工作树工作,并且-m意味着与git checkout不同的东西(对我来说,这是避免使用旧的git checkout命令的另一个原因,使用git switchgit restore )。 Again, I won't cover any of the details here, as this is already long enough.同样,我不会在这里介绍任何细节,因为这已经够长了。


3 Precisely when and whether git mergetool runs this git add is a little tricky because Git doesn't really know whether your merge tool has done the merge. 3 git mergetool究竟何时以及是否运行此git add有点棘手,因为 Git 并不真正知道您的合并工具是否已完成合并。 There are several knobs you can configure to tell git mergetool how to interpret results from the merge tool.您可以配置几个旋钮来告诉git mergetool如何解释合并工具的结果。 But if you're using a known merge tool, that's already configured correctly, this is all invisible.但是,如果您使用的是已知的合并工具,该工具已经正确配置,那么这一切都是不可见的。 It's when you want to use your own merge tool that you have to know how to configure this.当你想使用自己的合并工具时,你必须知道如何配置它。 We won't cover these details here.我们不会在这里介绍这些细节。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM