Git中"ours"和"theirs"的确切含义是什么?

529

这可能听起来是一个太基础的问题,但我已经搜索了答案,现在比以前更加困惑。

在将我的分支合并到我的另一个分支时,"ours"和"theirs"在git中是什么意思?两个分支都是"ours"。

在合并冲突中,"ours"是否总是显示的两个版本中较上面的那个?

"ours"是否总是指向合并开始时HEAD所指向的分支?如果是这样,为什么不使用明确的所有格形式引用,例如"current branch's",而是使用指示代词"ours",其具有参照模糊性(因为两个分支在技术上都是我们的)?

或者只需使用分支名称(而不是说"ours",只需说"local master's"或类似的内容)?

对我最困惑的部分是,如果我在特定分支的.gitattributes文件中指定。假设在测试分支中,我有以下.gitattributes文件:

config.xml merge=ours

现在我要进行结账,然后将 HEAD 指向master,再合并test。由于master是我们的,而test的 .gitattributes 文件没有被检出,它会产生影响吗?如果它确实产生影响,那么既然master现在是“我们的”,那么会发生什么?


5
参见:根据 Git,"us" 和 "them" 分别指什么? 我在那里有一个答案,详细解释了 us / oursthem / theirs 的含义以及它们在 git mergegit cherry-pickgit rebasegit revert 中如何变化。 - Gabriel Staples
3
该死,这个命名的人应该坐在调皮的凳子上;它太模糊了。 - Steve Horvath
哇,谢谢你发布这个。无论是谁想出那个命名方式的,显然都很困惑。 - BV45
这可能听起来像一个太基础的问题,但不要感到内疚。Git并不简单。我从未见过比Git更晦涩、复杂、不友好、非图形化和令人困惑的程序。看起来Git的作者选择了一个完美的名字。是预感还是他知道自己在做什么?对于这个“太基础”的问题的投票数量就说明了一切。 - undefined
10个回答

540

我猜你可能会感到困惑,因为这个问题本来就很令人困惑。更糟糕的是,在进行rebase操作时,整个"ours/theirs"概念会反转(变得相反)。

最终,在git merge期间,“ours”分支是指你要合并到的分支:

git checkout merge-into-ours

而"theirs"分支是指您正在合并的(单个)分支:

git merge from-theirs

这里的“ours”和“theirs”有一定的意义,因为即使“theirs”可能已经是你的了,“theirs”也不是你在运行git merge时所在的分支。

尽管使用实际的分支名称可能很酷,但在更复杂的情况下会出现问题。例如,你可能会这样做:

git checkout ours
git merge 1234567

你可以通过原始提交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是您正在合并的版本。


“我们的”和“他们的”在rebase期间交换的原因是,rebase通过进行一系列cherry-pick操作来工作(进入匿名分支(分离的HEAD模式))。目标分支是匿名分支,合并来源分支是您原始的(pre-rebase)分支:所以,“--ours”意味着rebase正在构建的匿名分支,“--theirs”意味着“正在被rebase的我们的分支”。


至于gitattributes条目:它内部实际上是“使用第二个阶段”,因此它可能会产生影响。但是正如您所注意到的,在此之前实际上还没有完成这一点,因此除非您在开始之前将其复制到工作树中,否则它不应该在这里产生影响。

另外,顺便说一下,这适用于所有“我们的”和“他们的”的用法,但有些是基于整个文件的(merge策略的-s ours;在合并冲突期间的git checkout --ours),有些是基于逐个片段的(在 -s recursive 合并期间的-X ours-X theirs)。这可能对任何混淆都没有帮助。

我从未想出更好的名称。另外:请参见VonC的答案以获取有关这些的其他名称,其中git mergetool引入了更多名称,称其为“本地”和“远程”!


哦我的天。谢谢你,Torek。我正在做变基的过程中,但是我不明白为什么我的分支上的更改不在<<<<<<< HEAD和=======之间。非常感谢。 - Reza
2
@torek,这可能是我遇到的最好的解释之一。感谢您在回复中投入了如此多的思考和努力,这非常有帮助。 - Jav
1
@Joseph:不,提交ID可以正常工作。但这里的问题不是原始提交哈希,而是“ours”和“theirs”的含义,这相当于在某个提交中选择文件(blob)哈希值。如果您使用提交哈希ID,则还必须添加文件路径,例如a123456:path/to/file.ext - torek
只是随口想想...似乎“源分支”和“目标分支”会是更清晰的术语,供Git社区采用。这个答案使用“目标分支”来帮助澄清“我们”的含义。只是一个想法。可能与数据迁移的语言平行。VSCode使用“当前更改”和“传入更改”,虽然合理准确,但并不比“我们”的含义和“他们”的含义更直观。 - Kay V
2
@JānisElmeris:是的,git stash apply 字面上运行内部的 git merge 代码(根据 Git 版本可能是 git merge-recursivegit merge-ort)。合并基础是原始提交,HEAD 是当前提交,“theirs”版本是存储的 w 提交。当使用 --indexgit stash pop 一起使用时,i 提交的更改将使用 git apply 而不是 git merge 应用。 - torek
显示剩余3条评论

160

我知道这个问题已经有答案了,但这个问题曾经让我感到困惑很多次,所以我建立了一个小的参考网站来帮助我记忆:

https://nitaym.github.io/ourstheirs/

以下是基础知识:

合并:

$ git checkout master 
$ git merge feature

如果您想选择 master 版本:

$ git checkout --ours codefile.js

如果您想选择feature版本:

$ git checkout --theirs codefile.js

变基 (Rebase):

$ git checkout feature 
$ git rebase master 

如果您想选择master版本:

$ git checkout --ours codefile.js

如果您想要选择feature版本:

$ git checkout --theirs codefile.js

(当然,这是针对完整的文件)


6
那个网站超级有帮助。流程图样式和颜色编码的文字使得这个网站比Stack Overflow的回答更容易理解。谢谢。 - Ron
2
谢谢,这就是我想要的简单示例解释。其他答案过多地讨论了为什么你可能会感到困惑,术语应该意味着什么等等,以至于答案本身变得混乱。 - Dario Seidl
这个答案非常棒。每当我在进行合并冲突时需要处理我们/他们的问题,我总是会来查看这个答案。 - Praveen Singh
补充一下其他人所说的。谢谢。这正是我在寻找的答案。请永远不要删除它。 - Dark Star1

80

在Git中,“ours”是指原始工作分支,拥有Git历史的权威/规范部分。

theirs”指的是保存工作以进行变基的版本(将更改重新应用到当前分支上)。

对于不知道进行变基(例如git rebase)实际上是将您的暂停工作(即theirs)重新应用到主要历史记录的人来说,这可能会出现交换的情况,因为我们正在将自己的更改作为第三方工作进行变基。

在Git > =2.5.1中,git-checkout的文档已经有进一步澄清,根据f303016提交

--ours --theirs

在从索引中检出路径时,检出第2阶段('ours')或第3阶段('theirs')以获取未合并的路径。

请注意,在git rebasegit pull --rebase期间,“ours”和“theirs”可能会交换位置;--ours提供基于变更的分支版本,而--theirs提供持有正在被重新定位的工作的分支版本。

这是因为rebase用于将远程历史视为共享的规范历史,并将在您正在重新定位的分支上完成的工作视为要集成的第三方工作。在重新定位期间,您暂时扮演规范历史的保管者角色。作为规范历史的保管人,您需要将远程历史视为“ours”(即“我们共享的规范历史”),而将您在分支上所做的内容视为“theirs”(即“一个贡献者在其之上完成的工作”)。

对于 git-merge,其解释如下:

ours

此选项通过偏爱我们的版本来强制清洁地自动解决冲突块。不与我们方面冲突的其他树更改将反映到合并结果中。对于二进制文件,整个内容都取自我们的方面。

这不应与我们的合并策略混淆,后者根本不看其他树包含什么。它放弃了其他树所做的一切,声明我们的历史包含其中发生的所有事情。

theirs

这是 ours 的相反操作。

此外,以下是如何使用它们的说明:

合并机制 (git mergegit pull 命令) 允许选择后端合并策略,使用 -s 选项。有些策略也可以采用自己的选项,这些选项可以通过给 git merge 和/或 git pull 提供 -X<option> 参数来传递。


有时候会有些混淆,例如:
  • git pull origin master 中的 -Xours 是本地的,-Xtheirs 是他们(远程)的分支
  • git pull origin master -r 中的 -Xours 是他们(远程)的,-Xtheirs 是我们的
所以第二个例子与第一个相反,因为我们正在将我们的分支重新定位到远程分支之上,所以我们的起点是远程分支,我们的更改被视为外部更改。
对于git merge策略(-X ours-X theirs)也是类似的。

2
这个答案似乎已经过时了 => "git merge --ours" 不是一个有效的选项。 - Alexander Mills
@AlexanderMills 这个答案没有提到 git merge,而是以 git pullgit checkout 作为例子。如果你想在 git merge 中使用这个参数,你应该使用 -X ours。你仍然可以在 git checkout 中使用 --ours 语法。我已经进一步澄清了答案。 - kenorb
2
你对rebase的反向操作的解释非常清晰易懂,我可能可以不用Google就记住它。谢谢! - Doug

33

我知道这并不能解释其意义,但我做了一个小图像作为参考,以便提醒使用哪个:

enter image description here

希望能对您有所帮助!

PS - 也请检查Nitay's answer中的链接。


2
我花了太长时间阅读一些内容繁重的书籍来寻找答案,但你提供的图片虽然并不完美,却以更为简洁的方式完美地解释了问题。第二版需要在视觉上对HEAD这个术语进行解释。 - David van Dugteren
谢谢夸奖!我很高兴它能帮助到别人,因为它在视觉上也帮助了我!我省略了 HEAD,因为它是一个特定时刻指针的时间相关信息。通过这张图片,我想要呈现一个“永恒”的视角;虽然我想象对于一个 git 新手来说可能不太容易理解。如果要解释/展示 HEAD,我需要为每个 git .. 命令(在 checkoutmergerebase 之后)都展示一个;在我看来,这可能会导致更混乱的图像;所以我认为已经掌握了 historyHEADcheckoutmergerebase 的知识/概念。 - Kamafeather

25
  • 我们的:当前所在的分支。
  • 他们的:在您的操作中使用的另一个分支。

因此,如果您在分支release/2.5上,并将分支feature/new-buttons合并到它中,则我们的指的是release/2.5中找到的内容,而他们的指的是在feature/new-buttons中找到的内容。在合并操作期间,这很简单明了。

大多数人犯的唯一问题是rebase情况。 如果您执行re-base而不是普通合并,则角色将被交换。怎么样? 好吧,这完全是由于re-basing的工作方式引起的。 把 rebase 想象成这样:

  1. 自上次拉取以来您做出的所有提交都移动到它们自己的分支上,请称其为BranchX.
  2. 您检出当前分支的头指针,丢弃您本地所做的任何更改,但从服务器重新获取其他人推送到该分支的所有更改。
  3. 现在,每个提交在BranchX中按顺序从旧到新地挑选到当前分支。
  4. BranchX被再次删除,因此永远不会出现在任何历史记录中。

当然,这并不是正在发生的事情,但对我来说这是一个很好的心智模型。如果您看看第2和第3项,您将了解为什么现在角色已经交换了。根据第2点,当前分支现在是来自服务器的分支,没有您的更改,因此这是我们的(您所在的分支)。您所做的更改现在位于不是当前分支的其他分支上(BranchX),因此这些更改(尽管是您进行的更改)是他们的(在您的操作中使用的另一个分支)。

这意味着如果你合并代码时,想让你的更改始终占优势,你需要告诉git始终选择"ours";但是如果你变基代码时,想让你的所有更改始终占优势,你需要告诉git始终选择"theirs"。


15

我会在这里发布我的备忘录,因为我需要一遍又一遍地回到这里。

场景1:普通开发者:你是一个无法合并到master分支的开发者,只能使用feature分支。

案例1:master是王者。你想刷新你的feature分支(即将其变基到master),因为master包含依赖项的新更新,你想覆盖你的谦虚更改。

git checkout master
git pull

git checkout feature
git rebase -X ours master

情况二:你是国王。 你想将你的feature分支基于master分支的更改进行变基。但你所做的事情比同事多,希望优先使用你自己的更改。

git checkout master
git pull

git checkout feature
git rebase -X theirs master

重要提示: 如您所见,普通开发者应该优先使用 rebase 命令,并将其像做运动或喝咖啡一样每天反复练习。

情景2. 合并大师: 您是团队负责人,想要合并其他分支并将合并结果直接推送到主分支 mastermaster 是您将要修改的分支。

情况1: master 为王 您想要合并第三方分支,但 master 的优先级更高。 feature 分支是您的上级创建的。

git checkout feature
git pull

git checkout master
git merge -X ours feature

情况2:新变化是王者 当你的高级开发人员发布了一个很酷的特性,而你想要覆盖主分支中旧有的代码。

git checkout feature
git pull

git checkout master
git merge -X theirs feature

记住:在午夜想起要选择哪一个时,master 始终是 ours。而 theirs 是他们做过的一个 feature


3
注意:-X 表示 --strategy-option,可以优先选择 theirsours 中的代码。 - Kamafeather

7

仅为澄清--如上所述,在变基时方向被颠倒,因此如果您看到

<<<<<<< HEAD
        foo = 12;
=======
        foo = 22;
>>>>>>> [your commit message]

使用“我的”解决 -> foo = 12

使用“他们的”解决 -> foo = 22


4

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 --theirs some_filegit checkout --ours some_file命令分别将文件重置为当前版本和传入版本。

如果您已经使用了git checkout --ours some_filegit checkout --theirs some_file命令,并且想要将文件重置为三路合并后的版本,则可以使用git checkout --merge some_file命令。


4

这些术语并没有确切的意义,因为它们的意思取决于您是在合并还是变基。对于两个分支的自然思考方式是“我的工作”和“别人的工作”。选择掩盖这种分类的术语绝对是Git中最糟糕的设计选择。它一定是受到了会计学中的“借方”和“贷方”或“资产”和“负债”的启发 - 同样令人头疼。


3
我不太赞同,这里确实有一个精确的含义:在合并或变基中,“ours”表示“HEAD所在的位置”:https://dev59.com/pHA85IYBdhLWcg3wD_O8#2960751 - VonC
4
“Where HEAD is”是一个以工具为中心而不是以用户为中心的概念。这正是问题所在。 - Szczepan Hołyszewski
我现在理解了你的观点。你是正确的,但是从那个角度来看(工具 vs. 用户),确实存在很多值得质疑的设计决策。其中一些正在改进: git checkout vs. git switch/git restore 是一个例子。 - VonC

4

--oursHEAD,而 --theirs 则是 REBASE_HEADMERGE_HEADCHERRY_PICK_HEAD。我发现直接使用提交引用要简单得多。

当进行变基时:

git restore HEAD thing.js         # use what the current branch has
git restore REBASE_HEAD thing.js  # use what the other branch has

合并时:

git restore HEAD thing.js        # use what the current branch has
git restore MERGE_HEAD thing.js  # use what the other branch has

当进行挑选时:

git restore HEAD thing.js              # use what the current branch has
git restore CHERRY_PICK_HEAD thing.js  # use what the other branch has

简单而一致。我非常喜欢这个! - nmbell
简单而一致。我非常喜欢这个! - undefined

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接