【问题标题】:How do I 'overwrite', rather than 'merge', a branch on another branch in Git?我如何“覆盖”而不是“合并”Git 中另一个分支上的一个分支?
【发布时间】:2011-06-05 04:37:09
【问题描述】:

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

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

【问题讨论】:

标签: git


【解决方案1】:

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

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

【讨论】:

    【解决方案2】:

    您可以使用'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
    

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

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

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

    您还会注意到,merge-strategy 创建的提交实际上与“我们的”分支的最新提交指向完全相同的树,而 strategy-option 创建了一个新的、以前看不见的树:

    $ 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。
    • 为什么; 在每个命令的末尾?我没有;,它似乎工作正常。这个答案也不完整,第三步是检查旧分支(电子邮件),然后再次与 staging 合并。
    • git rebase -s theirs <oldbranc> <newbranch> 也可以(无论您是哪个分支)。请注意,在 rebase 中,“他们的”实际上是新分支,因为“我们的”是我们当前应用提交的头部。
    • @Rolf:但是 rebase 将摆脱合并信息并展平历史记录。不一定是你所追求的。如果您正在处理已发布的历史记录,这也不是一个好主意。
    【解决方案3】:

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

    最简单的方法:

    //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,他们现在是否会获得属于自己的分支但相同的电子邮件分支,如果没有提交,则与 staging 相同?
    • 你是救命稻草
    【解决方案4】:

    我尝试了@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
    

    【讨论】:

      【解决方案5】:

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

      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 分支中发生的所有更改

      【讨论】:

      • 阅读merge docs。看来我们不能使用--strategy-option=theirs
      • 对我不起作用。尽管使用了“他们的”,但会导致冲突。不过,使用 -s ours 进行第一次反向合并的公认答案确实有效。
      【解决方案6】:

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

      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)
      

      【讨论】:

        【解决方案7】:

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

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

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

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

        第 1 部分:
        使用以下命令,旧的 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 中意想不到的新本地提交,不必要的
        所以,我们需要扔掉,让 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 现在被新的开发分支代码覆盖
        2.本地干净(远程源/开发未触及)

        【讨论】:

        • 哈哈哈非常感谢!!我是使用 gits 存储库的新手,你救了我!!很好的解释!!!它对我有用!
        • 很高兴知道:)
        【解决方案8】:

        我想合并两个分支,以便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
        

        【讨论】:

          【解决方案9】:
          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 感谢您的评论,您是对的。我将来可能会编辑答案。
          【解决方案10】:

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

          这对我有用:

          $ 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,我很好奇。你期待的合并提交消息是什么?它是怎么搞砸的?
          • 它说“将远程跟踪分支 'origin/email' 合并到电子邮件中”。它没有提到分期。不过没什么大不了的。只需修改提交,或使用merge -m 'This is not my beautiful house.' -s ours origin/email
          • @ShyamHabarakada,我照你说的做,但我遇到了关于非快进更新的问题。因为当我做第四步时什么都没有发生。
          • 第 4 步要求您使用 origin/email,它不适用于本地电子邮件。
          【解决方案11】:

          如果你只是想让'email'和'staging'两个分支相同,你可以标记'email'分支,然后将'email'分支重置为'staging'分支:

          $ 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 的同一个头部,并且它们都将具有相同的提交、相同的历史记录。
          【解决方案12】:

          怎么样:

          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,以非破坏性的方式解决问题。
          • 如果您只想吹走一个分支并从源分支开始清理它,这是最好的方法。
          【解决方案13】:

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

          如果您想要从 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/…
          • 它说,找不到“他们的”合并策略。
          • 非常适合我,优点是不重置任何东西
          【解决方案14】:

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

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

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

          【讨论】:

          • git push origin email --force 对我不起作用,因为 Step2 左分支发散了。所以在 Step2 之后,这就是我所做的: git reset origin/email 然后 git push
          • 这正是被问到的问题 - “如何覆盖而不是合并”。应该被接受的答案。
          • 如果你还想查看邮件分支中的内容,只需在推送之前标记它,例如,git tag old-email-branch。然后,您将能够通过标记版本保留以前的内容。
          猜你喜欢
          • 2023-03-14
          • 1970-01-01
          • 2010-12-15
          • 1970-01-01
          • 2013-02-21
          • 2014-10-28
          • 2022-08-17
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多