我如何覆盖',而不是'合并',在Git的另一个分支上的一个分支?

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

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

314446 次浏览

你可以使用“我们的”合并策略:

$ 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演示库的分支

中找到生成的历史

-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合并)何时使用'策略,& # 39;我们的# 39;选项和'their '选择吗?的问题和答案

首先,设置一个带有2个分支和3个提交的存储库(1个基本提交,每个分支1个提交)。你可以找到示例库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

我们最终得到两个分支内容的合并 (branch "strategy-option"在示例回购中)。将其与使用合并策略进行比较(在执行下一步之前重新初始化存储库或重置分支):

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

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

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

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

OP确实要求“;I no longer need the old changes in[…]branch”;和“所以我只想把[A]的所有内容都放到[B]”,这是策略选项无法做到的。使用“ours”合并策略是众多可能性中的一种,但可能是最简单的(其他可能性包括使用Git的低级命令,如write-treecommit-tree)。

如果你只想让“email”和“staging”这两个分支保持一致,你可以标记“email”分支,然后将“email”分支重置为“staging”分支:

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

你也可以将“staging”分支重新建立在“email”分支上。但是结果将包含两个分支的修改。

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

以下是对我有效的方法:

$ 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

如果没有与我们的战略合并的第四步,推送被认为是非快进更新,将被拒绝(GitHub)。

如果你像我一样不想处理归并,你可以执行上面的步骤,除了使用强制而不是归并,因为它会创建一个分散注意力的日志记录:

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

注意:除非你真的不想再在邮件里看到这些东西。

我已经看到了几个答案,这是唯一的程序,让我解决没有任何冲突。

如果你想要在branch_old中对branch_new的所有更改,那么:

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

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

如何:

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

其他答案看起来不完整 我已经尝试了下面的全部,它工作得很好 < p > 注意: < br > 1. 为了安全起见,在尝试下面的操作之前,请先复制您的存储库。< / p > < p >细节:< br > 1. 所有的开发都发生在dev分支
2. Qa分支只是dev
的相同副本 3.时不时地,开发代码需要移动/覆盖到qa分支

所以我们需要覆盖qa分支,从开发分支

< p > 第1部分: < br > 使用以下命令,旧的qa已更新为新的dev
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中新的本地提交,不必要的 所以,我们需要抛弃这些内容,保持开发者的原封不动。< / p >
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
< p > 第3部分: < br > 验证一切如预期:

git status


// Output:
//  *On branch dev
//  Your branch is up-to-date with 'origin/dev'.
//  nothing to commit, working tree clean*
< p >。< br > 1. 旧qa现在被新的dev分支代码
覆盖 2. 本地是干净的(远端源/dev是不受影响的)

git checkout email
git merge -m "Making email same as staging disregarding any conflicts from email in the process" -s recursive -X theirs staging

我想合并两个分支,以便old_branch中的所有内容都用new_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

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

最简单的方法是:

//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

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

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

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)

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

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

它的作用是:

  • email分支文件现在将是完全,与staging分支相同
  • email分支的历史将被保留
  • staging分支的历史将被添加到email历史中

作为附加价值,如果你不想要staging分支的所有历史记录,你可以使用squash将其总结为一条提交消息。

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

总之,第二个版本所做的是:

  • email分支文件现在将是完全,与staging分支相同
  • email分支的历史将被保留
  • 一个单独的提交将被添加到email分支的历史记录之上。这个提交将表示在staging分支中发生的所有更改

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

分支-a:保留分支

分支-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

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

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