这听起来可能是一个太基本的问题,但我一直在寻找答案,现在我比以前更困惑了。
当我的分支合并到我的另一个分支时,“我们的”和“他们的”在git中是什么意思?
两个分支都是“我们的”。
在合并冲突中,“我们的”总是显示两个版本的上部吗?
“我们的”是否总是指合并开始时HEAD所指向的分支?如果是这样,那么为什么不使用一个明确的所有格,如“当前分支的”,而不是使用一个所有格代词,如“我们的”,这是指模棱两可的(因为两个分支在技术上都是我们的)?
或者只是使用分支名称(而不是说“我们的”,而是说“本地主人的”或类似的)?
最让我困惑的部分是,如果我在一个特定分支的.gitattributes文件中指定。让我们说在测试分支中,我有以下.gitattributes文件:
config.xml merge=ours
现在我签出并点HEAD到master,然后在测试中合并。因为master是我们的,test的.gitattributes没有签出,它会有影响吗?如果真的有效果,既然师父现在是“我们的”了,那又会怎样呢?
我怀疑你在这里感到困惑,因为它从根本上令人困惑。更糟糕的是,当你在做rebase时,我们/他们的所有东西都转换了角色(变成了倒退)。
最终,在git合并过程中,“ours”分支指的是你要合并到的分支:
git checkout merge-into-ours
“their”分支指的是你要合并的(单个)分支:
git merge from-theirs
在这里,“ours”和“their”是有意义的,因为即使“their”可能是你的,“their”也不是你在运行git merge时使用的那个。
虽然使用实际的分支名称可能很酷,但在更复杂的情况下就不行了。例如,你可以这样做:
git checkout ours
git merge 1234567
通过原始commit-ID进行合并。更糟糕的是,你甚至可以这样做:
git checkout 7777777 # detach HEAD
git merge 1234567 # do a test merge
在这种情况下,不涉及分支名称!
我认为这在这里没有什么帮助,但实际上,在gitrevisions语法中,您可以在冲突合并期间通过数字引用索引中的单个路径
git show :1:README
git show :2:README
git show :3:README
阶段#1是文件的共同祖先,阶段#2是目标分支版本,阶段#3是您要合并的版本。
“我们的”和“他们的”概念在改基过程中交换的原因是改基工作是通过做一系列的筛选,进入一个匿名分支(分离的HEAD模式)。目标分支是匿名分支,而merge-from分支是你的原始(预重基)分支:所以“——our”意味着匿名的一个重基正在构建,而“——their”意味着“我们的分支正在重基”。
至于gitattributes条目:它可能会产生影响:“ours”实际上在内部意味着“使用阶段#2”。但正如你所指出的,当时它还没有到位,所以它应该不会在这里产生影响……除非在开始之前将它复制到工作树中。
另外,顺便说一下,这适用于我们和他们的所有使用,但有些是在整个文件级别(-s我们的合并策略;git签出——在合并冲突时是我们的),有些是逐块的(在-s递归合并时是-X我们的或-X他们的)。这可能无助于解决任何困惑。
不过,我从来没有想过一个更好的名字。并且:参见VonC对另一个问题的回答,其中git mergetool为这些引入了更多的名称,称它们为“本地”和“远程”!
Git中的“our”指的是原始的工作分支,它在Git历史中具有权威/规范的部分。
“their”指的是保存工作以便重基于的版本(要在当前分支上重放的更改)。
这可能会被那些没有意识到做重基(例如git重基)实际上是在暂停你的工作(这是他们的),以便重放到我们的规范/主历史上,因为我们将我们的更改作为第三方工作重基。
Git -checkout的文档在Git >=2.5.1中根据f303016提交得到了进一步的澄清:
--ours
--theirs
When checking out paths from the index, check out stage #2
('ours') or #3 ('theirs') for unmerged paths.
Note that during git rebase and git pull --rebase, 'ours' and 'theirs' may appear swapped; --ours gives the version from the branch the changes are rebased onto, while --theirs gives the version from the branch that holds your work that is being rebased.
This is because rebase is used in a workflow that treats the history at the remote as the shared canonical one, and treats the work done on the branch you are rebasing as the third-party work to be integrated, and you are temporarily assuming the role of the keeper of the canonical history during the rebase. As the keeper of the canonical history, you need to view the history from the remote as ours (i.e. "our shared canonical history"), while what you did on your side branch as theirs (i.e. "one contributor's work on top of it").
对于git-merge,它的解释如下:
ours
This option forces conflicting hunks to be auto-resolved cleanly by favoring our version. Changes from the other tree that do not conflict with our side are reflected to the merge result. For a binary file, the entire contents are taken from our side.
This should not be confused with the ours merge strategy, which does not even look at what the other tree contains at all. It discards everything the other tree did, declaring our history contains all that happened in it.
theirs
This is the opposite of ours.
此外,这里解释了如何使用它们:
合并机制(git merge和git pull命令)允许使用-s选项选择后端合并策略。一些策略也可以有自己的选项,可以通过给git merge和/或git pull提供-X<option>参数来传递。
所以有时它会让人困惑,例如:
git将-Xours是我们的本地分支,- xtheir是他们的(远程)分支
git拉origin master -r where -Xours是他们的(远程),- xtheir是我们的
第二个例子与第一个相反,因为我们在远程分支的基础上重新建立了分支,所以我们的起点是远程分支,我们的更改被视为外部的。
git合并策略也类似(-X我们的和-X他们的)。
我们的:这是您目前所在的分支。
他们的:这是在你的行动中使用的另一个分支。
因此,如果你在分支版本/2.5上,你将分支feature/new-buttons合并到其中,那么在/2.5中找到的内容就是我们的内容,在feature/new-buttons上找到的内容就是他们的内容。在合并操作中,这是非常直接的。
大多数人陷入的唯一问题是rebase情况。如果您执行的是re-base而不是普通的合并,则角色将交换。这是怎么回事?嗯,这完全是由重基工作的方式引起的。想想rebase是这样工作的:
你上一次拉之后所做的所有提交都被移动到它们自己的分支中,我们将其命名为BranchX。
签出当前分支的头,丢弃任何本地分支
您所拥有的更改,但是通过这种方式检索其他人为该分支推送的所有更改。
现在BranchX上的每个提交都是按旧到新的顺序挑选的。
BranchX再次被删除,因此不会出现在任何历史记录中。
当然,事实并非如此,但这对我来说是一个很好的心智模型。如果你看第二和第三,你就会明白为什么角色互换了。在第2点,你的当前分支现在是来自服务器的分支,没有任何你的改变,所以这是我们的(你所在的分支)。您所做的更改现在在一个不同的分支上,而不是您当前的分支(BranchX),因此这些更改(尽管是您所做的更改)是他们的(在您的操作中使用的另一个分支)。
这意味着如果你合并,你想让你的改变总是赢,你会告诉git总是选择“我们的”,但如果你重新基数,你想让你的所有改变总是赢,你告诉git总是选择“他们的”。
我知道这个问题已经有答案了,但这个问题让我困惑了很多次,所以我建立了一个小的参考网站来帮助我记住:
https://nitaym.github.io/ourstheirs/
以下是基本原则:
合并:
$ git checkout master
$ git merge feature
如果你想在master中选择版本:
$ git checkout --ours codefile.js
如果您想在功能中选择版本:
$ git checkout --theirs codefile.js
重置:
$ git checkout feature
$ git rebase master
如果你想在master中选择版本:
$ git checkout --ours codefile.js
如果您想在功能中选择版本:
$ git checkout --theirs codefile.js
(当然,这是针对完整的文件)
从git checkout的用法来看:
-2, --ours checkout our version for unmerged files
-3, --theirs checkout their version for unmerged files
-m, --merge perform a 3-way merge with the new branch
在解决合并冲突时,你可以执行git checkout—他们的some_file和git checkout—我们的some_file来分别将文件重置为当前版本和传入版本。
如果你已经完成了git checkout——我们的some_file或git checkout——他们的some_file,并想要将文件重置为文件的3向合并版本,你可以执行git checkout——merge some_file。
我将把我的备忘录贴在这里,因为我必须一次又一次地回到这里。
场景1。普通开发人员:你是不能合并到精通的开发人员,只能使用功能分支。
案例1:主人是国王。你想要刷新你的特性分支(= rebase to master),因为master包含了新的依赖项更新,而你想要覆盖你的小改动。
git checkout master
git pull
git checkout feature
git rebase -X ours master
情况2:你是国王。你想要重新建立你的特性分支来控制变化。但是您比您的同事做得更多,并且希望优先使用您自己的更改。
git checkout master
git pull
git checkout feature
git rebase -X theirs master
重要提示:正如你所看到的,普通的开发人员应该更喜欢重新创建数据库,并像每天早上锻炼/喝咖啡一样重复它。
场景2。合并老师:你是一个团队领导,想合并其他分支,并将合并的结果直接推给一个主。Master是一个分支,你会改变。
案例1:master is a king您想要合并第三方分支,但master是优先级。Feature是你前辈做的一个分支。
git checkout feature
git pull
git checkout master
git merge -X ours feature
当你的高级开发人员发布了一个很酷的功能,你想要覆盖主分支中的旧的东西。
git checkout feature
git pull
git checkout master
git merge -X theirs feature
记住:在午夜记得该选择哪一个:主人永远是我们的。这是他们的特色。