ChatGPT解决这个技术问题 Extra ChatGPT

我如何“覆盖”而不是“合并”Git 中另一个分支上的一个分支?

我有两个分支,emailstagingstaging 是最新的,我不再需要 email 分支中的旧更改,但我不想删除它们。

所以我只想将 staging 的所有内容转储到 email 中,以便它们都指向同一个提交。那可能吗?


J
John Cvelth

您可以使用 'ours' merge strategy

$ git checkout staging
$ git merge -s ours email # Merge branches, but use our (=staging) branch head
$ git checkout email
$ git merge staging

编辑 2020-07-30:

我对这个问题和可能的解决方案进行了更多思考。如果您绝对需要以正确的顺序合并父级,需要通过单个命令行调用执行此操作,并且不介意运行管道命令,您可以执行以下操作:

$ git checkout A
$ git merge --ff-only $(git commit-tree -m "Throw away branch 'A'" -p A -p B B^{tree})

这基本上类似于(不存在的)merge -s theirs 策略。您可以在 plumbing branch of the demo repository 中找到生成的历史记录

-s ours 开关相比,它的可读性和记忆力都不是很好,但它确实可以完成工作。结果树再次与分支 B 相同:

$ git rev-parse A^{tree} B^{tree} HEAD^{tree}
3859ea064e85b2291d189e798bfa1bff87f51f3e
0389f8f2a3e560b639d82597a7bc5489a4c96d44
0389f8f2a3e560b639d82597a7bc5489a4c96d44

编辑 2020-07-29:

关于 -s ours-X ours(后者等同于 -s recursive --strategy-option ours)之间的区别似乎存在很多混淆。这里有一个小例子来展示使用这两种方法的两个结果。我还建议阅读(Git Merging) When to use 'ours' strategy, 'ours' option and 'theirs' option?的问答

首先,设置一个包含 2 个分支和 3 个提交(1 个基本提交,每个分支 1 个提交)的存储库。您可以找到示例存储库 on GitHub

$ git init
$ echo 'original' | tee file1 file2 file3
$ git commit -m 'initial commit'
$ git branch A
$ git branch B
$ git checkout A
$ echo 'A' > file1
$ git commit -m 'change on branch A' file1
$ git checkout B
$ echo 'B' > file2
$ git commit -m 'change on branch B' file2

现在,让我们尝试一下策略选项(我们使用他们的还是我们的并不重要):

$ git merge -X ours A
$ cat file*
A
B
original

我们最终合并了两个分支的内容(示例 repo 中的分支“strategy-option”)。将其与使用合并策略(在执行后续步骤之前重新初始化存储库或重置分支)进行比较:

$ git merge -s ours A
$ cat file*
original
B
original

结果完全不同(示例回购中的分支“合并策略”)。使用策略选项,我们得到两个分支的合并结果,使用策略我们丢弃另一个分支中发生的任何更改。

您还会注意到,由合并策略创建的提交实际上指向与“我们的”分支的最新提交完全相同的树,而策略选项创建了一个新的、以前看不见的树:

$ git rev-parse A^{tree} B^{tree} merge-strategy^{tree} strategy-option^{tree}
3859ea064e85b2291d189e798bfa1bff87f51f3e
0389f8f2a3e560b639d82597a7bc5489a4c96d44
0389f8f2a3e560b639d82597a7bc5489a4c96d44
5b09d34a37a183723b409d25268c8cb4d073206e

OP 确实要求“我不再需要 [...] 分支中的旧更改”和“所以我只想将 [A] 的所有内容转储到 [B]”,这与策略选项无关。使用“我们的”合并策略是许多可能性中的一种,但可能是最简单的(其他可能性包括使用 Git 的低级命令,例如 write-treecommit-tree)。


我试过了,这对我来说似乎倒退了?这不是将电子邮件的内容转储到暂存中,而不是相反吗?
@Max 我也有同样的困惑,这给我带来了很多麻烦。您需要从较新的分支(暂存)执行此命令,然后对旧分支(电子邮件)执行正常的合并/PR。
为什么每个命令末尾都有 ;?我没有 ;,它似乎工作。这个答案也不完整,第三步是检查旧分支(电子邮件),然后再次与登台合并。
git rebase -s theirs <oldbranc> <newbranch> 也可以(无论您是哪个分支)。请注意,在 rebase 中,“他们的”实际上是新分支,因为“我们的”是我们当前应用提交的头部。
@Rolf:但是 rebase 将摆脱合并信息并展平历史记录。不一定是你所追求的。如果您正在处理已发布的历史记录,这也不是一个好主意。
P
Peter Mortensen

如果您只是希望“电子邮件”和“暂存”两个分支相同,则可以标记“电子邮件”分支,然后将“电子邮件”分支重置为“暂存”分支:

$ git checkout email
$ git tag old-email-branch
$ git reset --hard staging

您还可以在“电子邮件”分支上重新设置“暂存”分支。但结果将包含两个分支的修改。


你可能是说 git checkout,据我所知 git check 不存在
你是对的,我已经习惯了 git 命令的 tab 完成,所以我总是写 "git check" 来写 "git checkout" !已更正。
git reset 破坏了其他人的 repo,这些人已经克隆了你的 repo
我相信这是正确的答案。 email 分支的头应该只指向 staging 的同一个头,并且它们都将具有相同的提交,相同的历史记录。
B
Brugolo

我已经看到了几个答案,这是唯一可以让我在没有任何冲突的情况下解决这个问题的程序。

如果您想要从 branch_old 中的 branch_new 进行所有更改,则:

git checkout branch_new
git merge -s ours branch_old
git checkout branch_old
git merge branch_new

一旦应用了这四个命令,您就可以毫无问题地推送 branch_old


您的回答最适合我的问题,因为我不想重置 HEAD,只需在将内容合并到旧分支时声明对新分支中新文件的偏好,并且工作没有问题! +1
在“我们的合并”之后,我将 branch_new 推送到远程,这种方法对我有用。我有必要推吗?或者没有它会起作用吗? (不确定它是否会使用本地分支或远程)。谢谢!
为什么您不能简单地执行以下操作? git checkout branch_old git merge -s theirs branch_new 线索取自 stackoverflow.com/questions/14275856/…
它说,找不到“他们的”合并策略。
非常适合我,优点是不重置任何东西
S
Shyam Habarakada

其他答案给了我正确的线索,但它们并没有完全帮助。

这对我有用:

$ git checkout email
$ git tag old-email-branch # This is optional
$ git reset --hard staging
$
$ # Using a custom commit message for the merge below
$ git merge -m 'Merge -s our where _ours_ is the branch staging' -s ours origin/email
$ git push origin email

如果没有与 ours 策略合并的第四步,则推送被视为非快进更新,将被拒绝(被 GitHub 拒绝)。


这使得合并提交消息错误,但是是的,它工作得很好。
cdunn2001,我很好奇。你期待的合并提交消息是什么,它是如何搞砸的?
它说“将远程跟踪分支'来源/电子邮件'合并到电子邮件中”。它没有提到分期。不过没什么大不了的。只需修改提交,或使用 merge -m 'This is not my beautiful house.' -s ours origin/email
@ShyamHabarakada,我照你说的做,我遇到了关于非快进更新的问题。因为当我做第四步时什么都没有发生。
第 4 步要求您使用原始/电子邮件,它不适用于仅本地电子邮件。
j
jsarma

如果你和我一样不想处理合并,你可以执行上述步骤,除了使用强制而不是合并,因为它会创建一个分散注意力的日志文件痕迹:

git checkout email
git reset --hard staging
git push origin email --force

注意:仅当您真的不想再在电子邮件中看到这些内容时。


git push origin email --force 对我不起作用,因为 Step2 左分支发散了。所以在第 2 步之后,这就是我所做的: git reset origin/email 然后 git push
这正是所问的问题-“如何覆盖而不是合并”。应该被接受的答案。
如果您仍想查看电子邮件分支中的内容,只需在推送之前对其进行标记,例如 git tag old-email-branch。然后,您将能够通过标记版本保留以前的内容。
m
mjjf

警告:这会删除电子邮件分支上的所有提交。这就像删除电子邮件分支并在暂存分支的头部重新创建它。

最简单的方法:

//the branch you want to overwrite
git checkout email 

//reset to the new branch
git reset --hard origin/staging

// push to remote
git push -f

现在电子邮件分支和暂存是相同的。


警告:这会删除 email 分支上的所有提交。这就像删除 email 分支并在 staging 分支的头部重新创建它。
这会给与分支机构合作的多人造成问题吗?如果他们 git pull email,他们现在是否会获得作为其自己的分支但相同的电子邮件分支,如果没有提交,是否与暂存相同?
V
Vadim Kotov

我想合并两个分支,以便用 new_branch 中的内容更新 old_branch 中的所有内容

对我来说,这就像一个魅力:

$ git checkout new_branch
$ git merge -m 'merge message' -s ours origin/old_branch
$ git checkout old_branch
$ git merge new_branch
$ git push origin old_branch

M
Manohar Reddy Poreddy

其他答案看起来不完整。我已经在下面进行了完整的尝试,并且效果很好。

注意:1. 为了安全起见,在您尝试以下操作之前,请先复制您的存储库。

详细信息: 1. 所有开发都发生在 dev 分支 2. qa 分支只是 dev 的同一个副本 3. 有时,需要将 dev 代码移动/覆盖到 qa 分支

所以我们需要从 dev 分支覆盖 qa 分支

第 1 部分:使用以下命令,旧 qa 已更新为较新的开发:

git checkout dev
git merge -s ours qa
git checkout qa
git merge dev
git push

最后推送的自动评论如下:

// Output:
//  *<MYNAME> Merge branch 'qa' into dev,*  

这个评论看起来是相反的,因为上面的序列也看起来是相反的

第2部分:

下面是 dev 中意想不到的新本地提交,不必要的,所以我们需要扔掉,让 dev 保持不变。

git checkout dev

// Output:
//  Switched to branch 'dev'  
//  Your branch is ahead of 'origin/dev' by 15 commits.  
//  (use "git push" to publish your local commits)


git reset --hard origin/dev  

//  Now we threw away the unexpected commits

第 3 部分:验证一切是否符合预期:

git status  

// Output:
//  *On branch dev  
//  Your branch is up-to-date with 'origin/dev'.  
//  nothing to commit, working tree clean*  

就这样。 1. 旧的 qa 现在被新的 dev 分支代码覆盖 2. 本地是干净的(远程源/开发未触及)


哈哈哈非常感谢!!我是使用 gits 存储库的新手,你救了我!!很好的解释!!!这个对我有用!!
很高兴知道:)
很好的一步一步的答案,谢谢
f
frandroid

怎么样:

git branch -D email
git checkout staging
git checkout -b email
git push origin email --force-with-lease

@emptywalls,因为它在没有警告用户它可以被服务器拒绝的情况下强制推送,也没有解释为什么需要它。也就是说,对于一个开发人员项目来说,这是一个可行的选择
@DavidCosta 为什么这会被服务器拒绝?这是一个简单的删除电子邮件分支并将登台复制到电子邮件'...所以我只想将“登台”的所有内容转储到“电子邮件”中,以便它们都指向同一个提交”——这正是正在发生的事情这里。
@ArekS 如果分支设置为“受保护”,例如在 GitLab 中,它可以被服务器端钩子拒绝。问题是,如果其他人在此操作之前从电子邮件中派生出他的分支,然后尝试推送,则引用不再匹配(因为某些提交根本就消失了)
我将原件从--force 编辑为--force-with-lease,以非破坏性的方式实现了这一点。
如果您只想删除一个分支并从源分支开始清理它,这是最好的方法。
l
lenz

你想要的是这个(实际上与当前接受的答案完全相反):

git checkout email
git merge --strategy-option=theirs staging  

这是做什么的:

电子邮件分支文件现在将与暂存分支完全相同

电子邮件分支的历史将被维护

暂存分支的历史记录将添加到电子邮件历史记录中

作为附加值,如果您不想要 staging 分支的所有历史记录,您可以使用 squash 将其汇总为单个提交消息。

git checkout email
git merge --squash --strategy-option=theirs staging  
git commit -m "Single commit message for squash branch's history here'

总而言之,第二个版本的作用是:

电子邮件分支文件现在将与暂存分支完全相同

电子邮件分支的历史将被维护

单个提交将添加到电子邮件分支的历史记录之上。此提交将代表暂存分支中发生的所有更改


阅读 merge docs。看起来我们不能使用 --strategy-option=theirs
对我不起作用。尽管使用了“他们的”,但会导致冲突。不过,使用 -s ours 进行第一次反向合并的公认答案确实有效。
W
Willa
git checkout email
git merge -m "Making email same as staging disregarding any conflicts from email in the process" -s recursive -X theirs staging

如果注释与命令中的合并注释分开,这个答案对我来说会更清楚。简要说明 -X 的作用以及它如何影响此合并。不过谢谢。让我查了一下:)
@noelicus 感谢您的评论,您是对的。我将来可能会编辑答案。
J
Jan Kyu Peblik

这不会改变原来的较新分支,并让您有机会在最终提交之前进行进一步修改。

git checkout new -b tmp
git merge -s ours old -m 'irrelevant'
git checkout old
git merge --squash tmp
git branch -D tmp
#do any other stuff you want
git add -A; git commit -m 'foo' #commit (or however you like)

z
zjk

我尝试了@knittl 的 write-tree/commit-tree 方法。

branch-a:保留的分支

branch-b:废弃的分支

// goto branch-a branch
$ git checkout branch-a

$ git write-tree
6fa6989240d2fc6490f8215682a20c63dac5560a // echo tree id? I guess

$ git commit-tree  -p branch-a -p branch-b 6fa6989240d2fc6490f8215682a20c63dac5560a
<type some commit message end with Ctrl-d>
20bc36a2b0f2537ed11328d1aedd9c3cff2e87e9 // echo new commit id

$ git reset --hard 20bc36a2b0f2537ed11328d1aedd9c3cff2e87e9

N
Noah Nuebling

这将合并两个分支 oldnew 并在每次合并冲突时选择 new 版本:

git checkout old
git merge new
git checkout --theirs .
git add .
git commit