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

发布于 2024-10-10 19:22:43 字数 210 浏览 7 评论 0原文

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

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

I have two branches, email and staging. staging is the latest one and I no longer need the old changes in email branch, yet I don't want to delete them.

So I just want to dump all the contents of staging into email so that they both point to the same commit. Is that possible?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

愿得七秒忆 2024-10-17 19:22:43

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

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

该问题需要保留 email 分支的提交。它们不能被设置为不可访问,这排除了任何基于重置的方法或删除并重新创建分支。

我不再需要电子邮件分支中的旧更改,但我不想删除它们。

为了保持两个分支的提交均可访问,必须存在一个或多个指向这些提交的引用。您始终可以保留两个引用(stagingemail),但问题要求删除其中一个引用。因此,需要以某种方式合并分支。


编辑2020-07-30:

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

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

这基本上就像(不存在的)< code>merge -s 他们的 策略。
您可以在演示存储库的 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 合并)何时使用“我们的”策略、“我们的”选项和“他们的”选项?

首先,设置一个具有 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

我们最终会合并两个分支的内容(示例存储库中的分支“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 )。

You can use the '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

The question requires the commits of the email branch to be kept. They must not be made unreachable, which rules out any reset-based approaches or deleting and recreating the branch.

I no longer need the old changes in email branch, yet I don't want to delete them.

To keep commits of both branches reachable, one or more references must exist that point to those commits. You can always keep two references (staging and email), but the question asks to get rid of one of the refs. Therefore, the branches need to be merged somehow.


EDIT 2020-07-30:

I thought a bit more about this question and possible solutions. If you absolutely require the merge parents in the correct order, need to perform this action with a single command line invocation, and don't mind running plumbing commands, you can do the following:

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

This basically acts like the (non-existent) merge -s theirs strategy.
You can find the resulting history in the plumbing branch of the demo repository

Not very readable and not as easy to remember compared to the -s ours switch, but it does the job. The resulting tree is again the same as branch B:

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

EDIT 2020-07-29:

There seems to be a lot of confusion as to what the difference between -s ours and -X ours (the latter being equivalent to -s recursive --strategy-option ours) is. Here's a small example to show the two results from using these two methods. I also recommend reading the question and answers of (Git Merging) When to use 'ours' strategy, 'ours' option and 'theirs' option?

First, setup a repository with 2 branches and 3 commits (1 base commit, and 1 commit per branch). You can find the sample repository 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

Now, let's try the strategy option (doesn't really matter if we use theirs or ours for this explanation):

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

We end up with a merge of both branches' contents (branch "strategy-option" in the sample repo). Compare that to using the merge strategy (re-init your repository or reset branch, before executing the next steps):

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

The result is quite different (branch "merge-strategy" in the sample repo). With the strategy option, we get a merge result of both branches, with the strategy we throw away any changes which happened in the other branch.

You will also notice that the commit created by the merge-strategy in fact points to the exact same tree as the latest commit of "our" branch, while the strategy-option created a new, previously-unseen tree:

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

OP indeed asked for "I no longer need the old changes in […] branch" and "So I just want to dump all the contents of [A] into [B]", which is not possible to do with a strategy option. Using the 'ours' merge strategy is one possibility of many, but likely the easiest (other possibilities include using low level commands of Git such as write-tree and commit-tree).

卖梦商人 2024-10-17 19:22:43

如果您只是希望“email”和“staging”这两个分支相同,则可以标记“email”分支,然后将“email”分支重置为“staging”分支:

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

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

If you just want the two branches 'email' and 'staging' to be the same, you can tag the 'email' branch, then reset the 'email' branch to the 'staging' one:

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

You can also rebase the 'staging' branch on the 'email' branch. But the result will contains the modification of the two branches.

ま昔日黯然 2024-10-17 19:22:43

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

如果您想要从branch_new到branch_old的所有更改,那么:

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

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

I've seen several answers and that's the only procedure that let me fix that without any conflicts.

If you want all changes from branch_new in branch_old, then:

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

once applied those four commands you can push the branch_old without any problem

漫雪独思 2024-10-17 19:22:43

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

这对我有用:

$ 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 拒绝)。

The other answers gave me the right clues, but they didn't completely help.

Here's what worked for me:

$ 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

Without the fourth step of merging with the ours strategy, the push is considered a non-fast-forward update and will be rejected (by GitHub).

锦欢 2024-10-17 19:22:43

如果您像我一样并且不想处理合并,则可以执行上述步骤,但使用强制而不是合并,因为它会创建分散注意力的日志文件记录:

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

注意:只有当您真的从不这样做时,才可以这样做想再次查看电子邮件中的内容。

If you're like me and you don't want to deal with merging, you can do the above steps, except use force instead of merge, because it will create a distracting log paper trail:

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

Note: This is only if you REALLY never want to see the stuff in email again.

魄砕の薆 2024-10-17 19:22:43

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

最简单的方法:

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

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

WARNING: This deletes all commits on the email branch. It's like deleting the email branch and creating it anew at the head of the staging branch.

The easiest way to do it:

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

Now the email branch and the staging are the same.

情丝乱 2024-10-17 19:22:43

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

I wanted to merge two branches so that all the contents in old_branch to be updated with the contents from new_branch

For me this worked like a charm:

$ 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
心凉怎暖 2024-10-17 19:22:43

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

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

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

因此我们需要从 dev 分支覆盖 qa 分支

第 1 部分:
使用以下命令,旧的质量保证已更新为较新的开发人员:

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

最后推送的自动注释如下:

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

此注释看起来相反,因为上面的序列也看起来相反

第 2 部分:

下面是意外的新本地提交开发,不必要的
所以,我们需要扔掉,让开发保持原样。

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.本地是干净的(远程origin/dev未受影响)

Other answers looked incomplete.
I have tried below in full, and it worked fine.

NOTE:
1. Make a copy of your repository before you try below, to be on safe side.

Details:
1. All development happens in dev branch
2. qa branch is just the same copy of dev
3. Time to time, dev code needs to be moved/overwrite to qa branch

so we need to overwrite qa branch, from dev branch

Part 1:
With below commands, old qa has been updated to newer dev:

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

Automatic comment for last push gives below:

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

This comment looks reverse, because above sequence also looks reverse

Part 2:

Below are unexpected, new local commits in dev, the unnecessary ones
so, we need to throw away, and make dev untouched.

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

Part 3:
Verify everything is as expected:

git status  

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

That's all.
1. old qa is now overwritten by new dev branch code
2. local is clean (remote origin/dev is untouched)

浮世清欢 2024-10-17 19:22:43

怎么样:

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

How about:

git branch -D email
git checkout staging
git checkout -b email
git push origin email --force-with-lease
好菇凉咱不稀罕他 2024-10-17 19:22:43

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

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

What you want is this (actually the exact inverse of the currently accepted answer):

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

What this does is:

  • email branch files will now be exactly the same as staging branch
  • email branch's history will be maintained
  • staging branch's history will be added to email history

As added value, if you don't want all of staging branch's history, you can use squash to summarize it into a single commit message.

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

So in summary, what this second version does is:

  • email branch files will now be exactly the same as staging branch
  • email branch's history will be maintained
  • A single commit will be added on top of email branch's history. This commit will represent ALL the changes that took place in the staging branch
以可爱出名 2024-10-17 19:22:43
git checkout email
git merge -m "Making email same as staging disregarding any conflicts from email in the process" -s recursive -X theirs staging
git checkout email
git merge -m "Making email same as staging disregarding any conflicts from email in the process" -s recursive -X theirs staging
月朦胧 2024-10-17 19:22:43

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

I tried @knittl's write-tree/commit-tree approach.

branch-a: the kept branch

branch-b: the abandoned branch

// 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
猥琐帝 2024-10-17 19:22:43

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

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

This will merge two branches old and new and choose the new version on every merge conflict:

git checkout old
git merge new
git checkout --theirs .
git add .
git commit
从此见与不见 2024-10-17 19:22:43

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

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)

This one doesn't alter the original newer branch, and gives you the opportunity to make further modifications before final commit.

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)
噩梦成真你也成魔 2024-10-17 19:22:43

我有一个类似的问题,但所有其他答案都使用合并,我想使用变基而不是合并。

当前状态

  A1---A2 master
 /
C0---B1---B2 other

所需状态

C0---B1---B2---A1---A2 master

因此,要获得此状态,请从交互式变基开始,

git checkout master
git rebase --interactive other

将第一项从“选择”更改为“编辑”。其余的保留为“选择”。第一次 rebase 后 git 停止后,我们将把第一次 rebase 变成覆盖,

git checkout A1 :/  # get all changes from A1

这应该将 A1 复制到索引。仔细检查一切是否正确,然后继续,git 将处理剩下的事情

git rebase --continue

I had a similar problem, but all the other answers use merge, and I wanted to use rebase rather than merge.

current state

  A1---A2 master
 /
C0---B1---B2 other

desired state

C0---B1---B2---A1---A2 master

So to get this, start with an interactive rebase

git checkout master
git rebase --interactive other

change first item from 'pick' to 'edit'. Keep the rest as 'pick'. After git stops after the first rebase, we will turn the first rebase into an overwrite

git checkout A1 :/  # get all changes from A1

This should copy A1 to index. Double check everything is correct and then continue and git will take care of the rest

git rebase --continue
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文