ChatGPT解决这个技术问题 Extra ChatGPT

是否有“git merge -s ours”的“他们的”版本?

使用 git merge 将主题分支“B”合并到“A”时,我遇到了一些冲突。我知道所有的冲突都可以使用“B”中的版本来解决。

我知道 git merge -s ours。但我想要的是 git merge -s theirs 之类的东西。

为什么不存在?在与现有 git 命令发生冲突合并后,如何获得相同的结果? (git checkout 来自 B 的每个未合并文件)

仅丢弃分支 A 中的任何内容(合并提交点到树的 B 版本)的“解决方案”不是我想要的。

请参阅 SO answer git command for making one branch like another 了解所有当前模拟git merge -s their的可能方式。
因此,您并不是真的在寻找 git merge -s theirs(可以通过 git merge -s ours 和临时分支轻松实现),因为 -s ours 完全忽略了 merge-from 分支的更改......
@Torek - Git 开发人员 真的 觉得除了 ours 之外还提供 theirs 冒犯吗???这是 Git 中高级工程和设计问题之一的症状:不一致。
@jww这里的问题不在于“git merge -s ours”具有冒犯性,而在于它违反直觉。您可以从 OP 的问题中看到,如果要添加这样的功能,当他真正想要做的是“git merge -s recursive -X theirs”时,他会错误地使用它。想要合并另一个分支覆盖与另一个分支上的版本冲突是很常见的,但是用另一个完全丢弃当前更改的分支完全覆盖当前分支确实是一个例外情况。
每个人都应该仔细检查 OP 的“更新”,因为问题与 git merge -s ours无关

u
uhbif19

一个类似的替代方法是接受 theirs--strategy-option(短格式 -X)选项。例如:

git checkout branchA
git merge -X theirs branchB

但是,这更等同于 -X ours 而不是 -s ours。主要区别在于 -X 执行常规递归合并,使用所选一侧解决任何冲突,而 -s ours 将合并更改为完全忽略另一侧。

在某些情况下,使用 -X theirs 而不是假设的 -s theirs 的主要问题是删除文件。在这种情况下,只需使用已删除文件的名称运行 git rm

git rm {DELETED-FILE-NAME}

之后,-X theirs 可能会按预期工作。

当然,使用 git rm 命令进行实际删除将首先防止冲突发生。


有关原始问题的更好解决方案(Paul Pladijs),请参见下面的其他答案。
值得注意的是,这与“合并策略theirs”不同。 -Xtheirs 是应用于递归策略的策略选项。这意味着递归策略仍然会合并任何它可以合并的东西,并且只会在发生冲突时回退到“他们的”逻辑。虽然这是在大多数情况下需要的,就像上面一样,但这与“按原样从分支 B 获取所有内容”不同。无论如何,它会进行真正的合并。
这是一个糟糕的答案,因为它看起来正确,但实际上是错误的。 “git merge -X theirs”不会做“git merge -s theirs”会做的事情。它不会用合并分支的内容替换当前分支。它更喜欢“他们的”更改,但仅在发生冲突的情况下。因此,生成的提交可能与“他们的”分支完全不同。这不是问题海报的想法。
@IvanKrivyakov 这只是一个糟糕的答案,因为这个问题太糟糕了,OP 想要 git merge -X ours 的反面而不是 git merge -s ours 的反面。这个答案应该反映 question 实际上是invalid
@user3338098:是的,你是对的。我重新阅读了这个问题,它也不是那么好。不幸的是,混淆的根本原因不是答案,甚至不是问题。 git 作者的设计选择是为合并策略和合并策略“递归”选项赋予相同的名称“我们的”。这种情况下的混乱实际上是不可避免的。
P
Paul Pladijs

将分支 B 合并到我们签出的分支 A 中的一种可能且经过测试的解决方案:

# in case branchA is not our current branch
git checkout branchA

# make merge commit but without conflicts!!
# the contents of 'ours' will be discarded later
git merge -s ours branchB    

# make temporary branch to merged commit
git branch branchTEMP         

# get contents of working tree and index to the one of branchB
git reset --hard branchB

# reset to our merged commit but 
# keep contents of working tree and index
git reset --soft branchTEMP

# change the contents of the merged commit
# with the contents of branchB
git commit --amend

# get rid off our temporary branch
git branch -D branchTEMP

# verify that the merge commit contains only contents of branchB
git diff HEAD branchB

要使其自动化,您可以使用 branchA 和 branchB 作为参数将其包装到脚本中。

此解决方案保留合并提交的第一个和第二个父项,就像您对 git merge -s theirs branchB 所期望的一样。


问:为什么需要 branchTEMP?你不能只是git reset --soft branchA吗?
@cdunn2001:不知何故,我也有同样的想法,但没有。请注意,提交 branchA 指向的 git reset --hard 更改。
很抱歉问了一个愚蠢的问题,但为什么这种方法比-xtheirs 更好?有什么优点/缺点
@chrispepper1989 -x theirs 只影响冲突,如果我们的一大块可以干净地应用,它将通过结果合并。在这种情况下,如最后一条命令所示,我们将得到一个与 branchB 完全相同的合并,无论是否存在冲突。
@UncleZeiv 谢谢你的解释,所以基本上做一个“他们的”会保持不冲突的更改和文件,导致 int 代码与提取的代码不同。
J
Jeff Puckett

旧版本的 git 允许您使用“他们的”合并策略:

git pull --strategy=theirs remote_branch

但这已被删除,正如 Junio Hamano(Git 维护者)在此消息中所解释的那样。如链接中所述,您可以这样做:

git fetch origin
git reset --hard origin

但请注意,这与实际的合并不同。您的解决方案可能是您真正需要的选项。


我真的完全不明白滨野朱尼奥的解释。 git reset --hard origin 如何解决他们的风格合并?如果我想将 BranchB 合并到 BranchA 中(就像在 Alan W. Smith 的回答中一样),我将如何使用 reset 方法来做到这一点?
@James McMahon:Junio C Hamano 的观点不是 git reset --hard 进行“他们的”式合并。当然,git reset --hard 不会创建任何合并提交或任何提交。他的观点是我们不应该使用合并来用其他东西替换 HEAD 中的任何内容。不过,我不一定同意。
很遗憾,theirs 被删除,因为理由不完整。它没有考虑到那些代码很好的情况,只是上游是在不同的哲学基础上维护的,所以从这个意义上说是“坏的”,所以一个人确实想跟上上游的最新情况,但是同时保留好的代码“修复”[git & msysgit 由于其不同的目标平台理念而有一些这种“冲突”]
它也缺少我现在坚持的用例,我正在与其他人共享一个分支,我们都碰巧同时推送了更改(在不同的文件上)。所以我想破坏我在本地拥有的所有旧版本,并改用他的新版本。他想对我更新的文件做同样的事情。没有什么可以“重置”的。当它是〜20个文件时,检查每个单独文件的解决方案是不切实际的。
我真的不懂哲学。我只使用了 theirs,因为我不小心更改了两个单独分支中的一些文件,并且想要放弃一个更改,而不必为每个文件手动执行此操作。
s
siegi

目前尚不清楚您想要的结果是什么,因此在答案和他们的评论中对“正确”的执行方式存在一些混淆。我尝试给出一个概述并查看以下三个选项:

尝试合并并使用 B 来解决冲突

不是“他们的 git merge -s ours 版本”而是“他们的 git merge -X ours 版本”(git merge -s recursive -X ours 的缩写):

git checkout branchA
# also uses -s recursive implicitly
git merge -X theirs branchB

这就是例如 Alan W. Smith's answer 所做的。

仅使用 B 中的内容

这会为两个分支创建一个合并提交,但会丢弃来自 branchA 的所有更改,只保留来自 branchB 的内容。

# Get the content you want to keep.
# If you want to keep branchB at the current commit, you can add --detached,
# else it will be advanced to the merge commit in the next step.
git checkout branchB

# Do the merge an keep current (our) content from branchB we just checked out.
git merge -s ours branchA

# Set branchA to current commit and check it out.
git checkout -B branchA

请注意,合并提交的第一个父级现在来自 branchB,只有第二个来自 branchA。这就是例如 Gandalf458's answer 所做的。

仅使用 B 中的内容并保持正确的父顺序

这是真正的“他们的 git merge -s ours 版本”。它的内容与之前的选项相同(即仅来自branchB 的内容),但父级的顺序是正确的,即第一个父级来自branchA,第二个来自branchB

git checkout branchA

# Do a merge commit. The content of this commit does not matter,
# so use a strategy that never fails.
# Note: This advances branchA.
git merge -s ours branchB

# Change working tree and index to desired content.
# --detach ensures branchB will not move when doing the reset in the next step.
git checkout --detach branchB

# Move HEAD to branchA without changing contents of working tree and index.
git reset --soft branchA

# 'attach' HEAD to branchA.
# This ensures branchA will move when doing 'commit --amend'.
git checkout branchA

# Change content of merge commit to current index (i.e. content of branchB).
git commit --amend -C HEAD

这就是 Paul Pladijs's answer 所做的(不需要临时分支)。

特别案例

如果 branchB 的提交是 branchA 的祖先,则 git merge 不起作用(它只是退出并显示“已经是最新的。”这样的消息)。

在这种情况或其他类似/高级情况下,可以使用低级命令 git commit-tree


选项 3 的更简洁版本:git checkout branchA; git merge -s ours --no-commit branchB; git read-tree -um @ branchB; git commit
我认为最后一个选项不应使用“正确的父顺序”一词,因为从逻辑上讲,该选项的父顺序不正确,即使它确实与问题中提出的顺序相匹配。这是逻辑上不正确的原因是合并通常是在某个分支中完成的,如果您合并被忽略的代码,它会在逻辑上合并到当前分支中,因此,-s ours应始终改为使用。 -s ours 旨在将某些分支保存为已弃用,并且该分支的所有“用户”应快进到另一个分支。将父母置于“正确”(原文如此)的顺序将是相反的。
此外,这应该是公认的答案,因为它正确地解释了这个看似简单的问题有多种解释。
@MikkoRantalainen,我的想法:将 branchB 合并到 branchA 应始终导致 branchA 成为第一个父项,而 branchB 成为第二个父项。选择的策略应该对此没有影响。因此,模仿(不存在的)策略 -s theirsbranchB 合并到 branchA 应该导致 branchA 也成为第一个父级。我同意它可能经常没有用(这就是它不存在的原因),但如果你真的想做 -s theirs,正确的父顺序是内容等效 -s ours 的逆定义,在我的观点。为什么你还需要它?
并且 git commit-tree 是您可以做到这一点的唯一方法,如果“他们的”是同一分支的旧版本,假设您想要一个实际的合并提交。请参阅 devblogs.microsoft.com/oldnewthing/20200928-00/?p=104302 中的第二张图
m
musicmatze

从现在开始,我就使用了 Paul Pladijs 的答案。我发现,你可以做一个“正常”的合并,发生冲突,所以你做

git checkout --theirs <file>

通过使用来自其他分支的修订来解决冲突。如果您对每个文件都执行此操作,您将获得与预期相同的行为

git merge <branch> -s theirs

无论如何,努力比合并策略要多! (这是用 git 版本 1.8.0 测试的)


如果可以检索文件列表,那就太好了。例如,git ls-files --modified | xargs git add 我想这样做是为了在两侧添加合并:/
实际上 git 用 git ls-files --modified 返回添加的双方文件,所以我想这也是一个可行的解决方案。
也感谢您添加此内容。更多时候,它不需要在逐个文件的基础上。此外, git status 在最底部显示“未合并的路径”。为了获取未解析路径的列表,我使用了这个别名:git config --global alias.unresolved '!git status --short|egrep "^([DAU])\1"'
-X 是什么意思,-X-s 有什么区别?找不到任何文件。
C
Community

使用 git merge 合并“A”中的主题分支“B”时,我遇到了一些冲突。我>知道使用“B”中的版本可以解决所有冲突。我知道 git merge -s ours 。但我想要的是像 git merge >-s 他们的东西。

我假设您从 master 创建了一个分支,现在想要合并回 master,覆盖 master 中的任何旧内容。当我看到这篇文章时,这正是我想做的。

做你想做的事,除了先将一个分支合并到另一个分支。我刚做了这个,效果很好。

git checkout Branch
git merge master -s ours

然后,checkout master 并合并你的分支(现在可以顺利进行):

git checkout master
git merge Branch

谢谢。这应该是公认的答案,是迄今为止最简单、最正确的答案。如果您的分支落后/与 master 冲突,那么在将所有内容合并回 master 之前,它的分支作业是合并并使一切正常工作。
e
elmarco

我解决了我的问题

git checkout -m old
git checkout -b new B
git merge -s ours old

“旧”分支的分支“B”
关于从 B 合并到旧分支并使用旧分支的更改解决冲突的问题,您的答案是相反的。
r
rafalmag

如果您在分支 A 上,请执行以下操作:

git merge -s recursive -X theirs B

在 git 版本 1.7.8 上测试


这是合乎逻辑的,-s 和 -X 一起
V
VonC

为什么不存在?

虽然我在“git command for making one branch like another”中提到了如何模拟 git merge -s theirs,但请注意 Git 2.15(2017 年第四季度)现在更加清晰:

用于合并的“-X

请参阅 Junio C Hamano (gitster)commit c25d98b(2017 年 9 月 25 日)。
(由 commit 4da3e23 中的 Junio C Hamano -- gitster -- 合并,2017 年 9 月 28 日)

合并策略:避免暗示“-s theirs”存在。-Xours 合并选项的描述有一个括号说明,告诉读者它与 -s ours 有很大不同,这是正确的,但是后面的 -Xtheirs 的描述它漫不经心地说“这与我们的相反”,给人一种错误的印象,即还需要警告读者,它与 -s 他们的非常不同,实际上甚至不存在。

-Xtheirs 是一个应用于递归策略的策略选项。这意味着递归策略仍然会合并任何它可以合并的东西,并且只会在发生冲突时回退到“theirs”逻辑。

关于 theirs 合并策略是否相关的辩论最近被重新提起in this Sept. 2017 thread
它承认 older (2008) threads

简而言之,前面的讨论可以总结为“我们不想要'-s theirs',因为它会鼓励错误的工作流程”。

它提到了别名:

mtheirs = !sh -c 'git merge -s ours --no-commit $1 && git read-tree -m -u $1' -

雅罗斯拉夫·哈尔琴科 (Yaroslav Halchenko) 试图再次倡导该策略,but Junio C. Hamano adds

我们的和他们的不对称的原因是你是你而不是他们——我们的历史和他们的历史的控制权和所有权是不对称的。一旦您决定他们的历史是主线,您宁愿将您的开发线视为分支并朝该方向进行合并,即结果合并的第一个父级是对他们历史的提交,第二个父母是你历史上最后一个坏人。所以你最终会使用“checkout their-history && merge -s ours your-history”来保持第一父母的合理性。到那时,使用“-s ours”不再是缺少“-s theirs”的解决方法。它是所需语义的适当部分,即从幸存的规范历史线的角度来看,您希望保留它所做的,而取消另一条历史线所做的。

正如 Mike Beaton 所评论的,Junio 补充说:

git merge -s ours 有效地表示“将其分支上的 提交标记为永久忽略的提交”;这很重要,因为如果您随后从其分支的后续状态合并,则将引入他们以后的更改,而不会引入忽略的更改。


这是一个有用的答案,但它没有提到我刚刚从 Junio C. Hamano 的回答中理解的一个关键点:git merge -s ours <their-ref> 有效地表示“标记提交到 <their-ref>在他们的分支上作为提交被永久忽略';这很重要,因为如果您随后从其分支的后续状态合并,则将引入他们以后的更改,而不会引入忽略的更改。
@MikeBeaton 谢谢。我已将您的评论包含在答案中以提高知名度。
E
Elijah Lynn

要真正正确地进行仅从您要合并的分支中获取输入的合并,您可以这样做

git merge --strategy=ours ref-to-be-merged

git diff --binary ref-to-be-merged | git apply --reverse --index

git commit --amend

在我所知道的任何情况下都不会发生冲突,您不必创建额外的分支,它就像一个正常的合并提交。

然而,这对子模块并不好。


-R 在这里做什么?
这样,您就会丢失“移动和更改”文件的历史记录。我对吗?
-R 是--reverse,我更新了答案,使其更具自我记录性。
如果您尝试合并的文件在传入分支中被删除,这似乎不起作用。
j
jthill

请参阅 Junio Hamano's widely cited answer:如果您要丢弃提交的内容,只需丢弃提交,或者无论如何将其保留在主历史记录之外。为什么将来要打扰每个人从没有提供任何东西的提交中阅读提交消息?

但有时有行政要求,或者可能是其他一些原因。对于那些您确实必须记录无贡献的提交的情况,您需要:

(编辑:哇,我以前有没有弄错这个。这个有效。)

git update-ref HEAD $(
        git commit-tree -m 'completely superseding with branchB content' \
                        -p HEAD -p branchB    branchB:
)
git reset --hard

这是 Raymond-Chen 最喜欢的获得 Paul Pladijs 结果的方法。毫不奇怪,这里很少有人得到它。
M
Michael R

这个使用 git 管道命令 read-tree,但会缩短整体工作流程。

git checkout <base-branch>

git merge --no-commit -s ours <their-branch>
git read-tree -u --reset <their-branch>
git commit

# Check your work!
git diff <their-branch>

B
Boaz Nahum

'git merge -s theirs branchB' 的等价物(保持父顺序)

https://i.stack.imgur.com/6AFQ5.png

!!!确保您处于清洁状态!

进行合并:

git commit-tree -m "take theirs" -p HEAD -p branchB 'branchB^{tree}'
git reset --hard 36daf519952 # is the output of the prev command

我们做了什么 ?我们创建了一个新的提交,其中两个父母是我们的和他们的,并且提交的内容是 branchB - 他们的

https://i.stack.imgur.com/XlMV5.png

更确切地说:

git commit-tree -m "take theirs" -p HEAD -p 'SOURCE^{commit}' 'SOURCE^{tree}'

与 jthill 的回答相同,我认为,但有更多的视觉解释。
b
briemers

我认为你真正想要的是:

git checkout -B mergeBranch branchB
git merge -s ours branchA
git checkout branchA
git merge mergeBranch
git branch -D mergeBranch

这看起来很笨拙,但它应该工作。我真正不喜欢这个解决方案的唯一想法是 git 历史会令人困惑......但至少历史将被完全保留,您不需要为已删除的文件做一些特殊的事情。


B
Brain2000

这个答案是由 Paul Pladijs 给出的。为了方便起见,我只是接受了他的命令并做了一个 git 别名。

编辑您的 .gitconfig 并添加以下内容:

[alias]
    mergetheirs = "!git merge -s ours \"$1\" && git branch temp_THEIRS && git reset --hard \"$1\" && git reset --soft temp_THEIRS && git commit --amend && git branch -D temp_THEIRS"

然后你可以通过运行“git merge -s theirs A”:

git checkout B (optional, just making sure we're on branch B)
git mergetheirs A

P
Pawan Maheshwari

这会将您的 newBranch 合并到现有的 baseBranch

git checkout <baseBranch> // this will checkout baseBranch
git merge -s ours <newBranch> // this will simple merge newBranch in baseBranch
git rm -rf . // this will remove all non references files from baseBranch (deleted in newBranch)
git checkout newBranch -- . //this will replace all conflicted files in baseBranch

我建议将 git commit --amend 添加到您的示例末尾,否则用户可能会看到带有 git log 的合并提交并假设操作已完成。
T
Trann

我最近才需要为共享共同历史的两个独立存储库执行此操作。我开始:

组织/存储库1 主

org/repository2 master

我希望将来自 repository2 master 的所有更改应用到 repository1 master,接受存储库 2 将进行的所有更改。在 git 的术语中,这个 应该 是一个名为 -s theirs 但它不存在的策略。请注意,因为 -X theirs 的命名方式就像您想要的那样,但它 相同(它甚至在手册页中也是如此)。

我解决这个问题的方法是转到 repository2 并创建一个新分支 repo1-merge。在那个分支中,我运行了 git pull git@gitlab.com:Org/repository1 -s ours,它合并得很好,没有任何问题。然后我把它推到遥控器上。

然后我回到 repository1 并创建一个新分支 repo2-merge。在该分支中,我运行 git pull git@gitlab.com:Org/repository2 repo1-merge 将完成问题。

最后,您需要在 repository1 中发出合并请求以使其成为新的主节点,或者将其保留为分支。


r
rubund

一个简单直观(在我看来)的两步方法是

git checkout branchB .
git commit -m "Picked up the content from branchB"

其次是

git merge -s ours branchB

(将两个分支标记为合并)

唯一的缺点是它不会从当前分支中删除已在 branchB 中删除的文件。之后两个分支之间的简单差异将显示是否有任何此类文件。

这种方法还可以从之后的修订日志中清楚地看出做了什么 - 以及打算做什么。


这对于原始问题可能是正确的,但是 OP 在 2008 年更新了这个问题,使得这个答案不正确。
@ user3338098 是的,很遗憾他们留下了误导性的标题,只是在问题主体的末尾拍了一个不那么明显的更新……因为这个答案确实正确回答了标题问题。
我完全同意@RomainValeri