使一个分支像另一个分支一样的 git 命令 [英] git command for making one branch like another

查看:36
本文介绍了使一个分支像另一个分支一样的 git 命令的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试采用一个带有更改的分支并将其恢复到与它所发散的上游相同.更改都是本地的并已推送到 github,因此 git resetgit rebase 都不是真正可行的,因为它们更改了历史记录,这对分支来说是一件坏事已经推送了.

我也尝试过 git merge 各种策略,但没有一个可以撤消本地更改,即如果我添加了一个文件,合并可能会使其他文件重新排列,但我仍然会有上游没有的文件.

我可以在上游创建一个新分支,但我真的很喜欢合并,在修订历史方面应用所有更改以获取我的分支并使其再次与上游相同,以便我可以安全地在不破坏历史的情况下推动这种变化.有没有这样的命令或一系列命令?

解决方案

您可以使用自定义合并驱动程序keepTheirs"将上游分支合并到 dev 分支:
参见git merge -s theirs" 需要——但我知道它不存在".
在您的情况下,只需要一个 .gitattributes 和一个 keepTheirs 脚本,例如:

mv -f $3 $2退出 0


git merge --strategy=theirs 模拟 #1

显示为合并,上游为第一个父级.

Jefromi 提到(在评论中)merge -s ours,通过合并您的在上游(或从上游开始的临时分支)上工作,然后将您的分支快速转发到该合并的结果:

git checkout -b tmp origin/upstreamgit merge -s ours下游#忽略下游的所有变化git checkout 下游git merge tmp # 快进到 tmp HEADgit branch -D tmp # 删除 tmp

<块引用>

这样做的好处是将上游祖先记录为第一个父级,这样合并意味着吸收这个过时的主题分支";而不是销毁这个主题分支并用上游替换它".

(2011 年编辑):

此工作流程已在此博客文章中报告操作员:

<块引用>

为什么我又想要这个?

只要我的 repo 与公共版本无关,这一切都很好,但既然现在我希望能够与其他团队成员和外部贡献者在 WIP 上进行协作,我想确保我的公共分支对于其他人来说是可靠的分支和拉取,即不再基于我推送到远程备份的内容进行重新设置和重置,因为它现在在 GitHub 和公共上.

所以这让我知道我应该如何继续.
我的副本 99% 的时间都会进入上游 master,所以我想工作我的 master 并在大部分时间推入上游.
但每隔一段时间,我在 wip 中的内容会因进入上游的内容而失效,我将放弃我的 wip 的某些部分.
那时我想让我的 master 与上游同步,但不破坏我公开推送的 master 上的任何提交点.IE.我想要与上游合并,最终得到使我的副本与上游相同的变更集.
这就是 git merge --strategy=theirs 应该做的.


git merge --strategy=theirs 模拟#2

显示为合并,我们的作为第一个父级.

(由 jcwenger 提出)

git checkout -b tmp 上游git merge -s ours thebranch # 忽略下游的所有变化git checkout 下游git merge --squash tmp # 应用来自 tmp 的更改但不是合并.git rev-parse 上游 >.git/MERGE_HEAD #记录上游第二个合并头git commit -m 从上游重新定义分支";# 进行提交.git branch -D tmp # 删除 tmp


git merge --strategy=theirs 模拟 #3

这篇博文提到::>

git merge -s ours ref-to-be-mergedgit diff --binary ref-to-be-merged |git apply -R --indexgit commit -F .git/COMMIT_EDITMSG --amend

<块引用>

有时您确实想这样做,而不是因为您有废话"在您的历史记录中,但可能是因为您想更改公共存储库中的开发基线,应避免重新定位.


git merge --strategy=theirs 模拟 #4

(同一篇博文)

<块引用>

或者,如果您想保持本地上游分支可快速转发,一个潜在的折衷方案是在理解 sid/unstable 时,上游分支可以不时重置/重新定位(基于事件在上游项目方面,您最终无法控制).
这没什么大不了的,在这个假设下工作意味着很容易将本地上游分支保持在只需要快进更新的状态.

git branch -m upstream-unstable upstream-unstable-savegit 分支上游不稳定上游远程/主git merge -s 我们的上游不稳定git diff --binary ref-to-be-merged |git apply -R --index --exclude="debian/*";git commit -F .git/COMMIT_EDITMSG --amend


git merge --strategy=theirs 模拟 #5

(由 Barak A. Pearlmutter 提出):

git checkout MINEgit merge --no-commit -s 我们的 HERSgit rm -rf .git checkout HERS -- .git checkout MINE -- debian # 或其他,视情况而定git gui # 编辑提交信息 &单击提交按钮


git merge --strategy=theirs 模拟 #6

(由相同的 Michael Gebetsroither 提出):

<块引用>

Michael Gebetsroither 插话说我在作弊";;) 并给出了另一个具有较低级别管道命令的解决方案:

(如果仅使用 git 命令无法实现,那么它就不是 git,git 中带有 diff/patch/apply 的所有内容都不是真正的解决方案;).

#获取另一个分支的内容git read-tree -u --reset # 选择性合并子目录# 例如用另一个分支的上游源取代上游源git merge -s ours --no-commit other_upstreamgit read-tree --reset -u other_upstream # 或使用 --prefix=foo/git checkout HEAD -- debian/git checkout HEAD -- .gitignoregit commit -m 'superseed 上游源' -a


git merge --strategy=theirs 模拟 #7

<块引用>

必要的步骤可以描述为:

  1. 用上游替换你的工作树
  2. 将更改应用到索引
  3. 将上游添加为第二个父级
  4. 提交

命令git read-tree用不同的树覆盖索引,完成第二步,并有更新工作树的标志,完成第一步.提交时,git 使用 .git/MERGE_HEAD 中的 SHA1 作为第二个父级,因此我们可以填充它以创建合并提交.因此,这可以通过以下方式实现:

git read-tree -u --reset upstream # 更新文件和阶段更改git rev-parse 上游 >.git/MERGE_HEAD # 设置合并提交git commit -m 将分支‘上游’合并到我的";# 犯罪

I'm trying to take a branch with changes and bring it back to be identical to the upstream it diverged from. The changes are both local and have been pushed to github, so neither git reset or git rebase are really viable, since they change history, which is a bad thing with a branch that's already been pushed.

I've also tried git merge with various strategies but none of them undo the local changes, i.e. if I'd added a file, a merge might bring other files back in line, but I'll still have that file that the upstream doesn't have.

I could just create a new branch off the upstream, but i'd really like a merge that in terms of revision history applies all the changes to take my branch and make it identical to the upstream again, so that I can safely push that change without clobbering history. Is there such a command or series of commands?

解决方案

You could merge your upstream branch to your dev branch, with a custom merge driver "keepTheirs":
See ""git merge -s theirs" needed — but I know it doesn't exist".
In your case, only one .gitattributes would be required, and a keepTheirs script like:

mv -f $3 $2
exit 0


git merge --strategy=theirs Simulation #1

Shows as a merge, with upstream as the first parent.

Jefromi mentions (in the comments) the merge -s ours, by merging your work on the upstream (or on a temp branch starting from upstream), and then fast-forwarding your branch to the result of that merge:

git checkout -b tmp origin/upstream
git merge -s ours downstream         # ignoring all changes from downstream
git checkout downstream
git merge tmp                        # fast-forward to tmp HEAD
git branch -D tmp                    # deleting tmp

This has the benefit of recording the upstream ancestor as the first parent, so that the merge means "absorb this out-of-date topic branch" rather than "destroy this topic branch and replace it with upstream".

(Edit 2011):

This workflow has been reported in this blog post by the OP:

Why do I want this again?

As long as my repo had nothing to do with the public version, this was all fine, but since now I'd want the ability to collorate on WIP with other team members and outside contributors, I want to make sure that my public branches are reliable for others to branch off and pull from, i.e. no more rebase and reset on things I've pushed to the remote backup, since it's now on GitHub and public.

So that leaves me with how I should proceed.
99% of the time my copy will go into the upstream master, so I want to work my master and push into upstream most of the time.
But every once in a while, what I have in wip will get invalidated by what goes into upstream and I will abandon some part of my wip.
At that point I want to bring my master back in sync with upstream, but not destroy any commit points on my publicly pushed master. I.e. i want a merge with upstream that ends up with the changeset that make my copy identical to upstream.
And that's what git merge --strategy=theirs should do.


git merge --strategy=theirs Simulation #2

Shows as a merge, with ours as the first parent.

(proposed by jcwenger)

git checkout -b tmp upstream
git merge -s ours thebranch         # ignoring all changes from downstream
git checkout downstream
git merge --squash tmp               # apply changes from tmp but not as merge.
git rev-parse upstream > .git/MERGE_HEAD #record upstream 2nd merge head
git commit -m "rebaselined thebranch from upstream" # make the commit.
git branch -D tmp                    # deleting tmp


git merge --strategy=theirs Simulation #3

This blog post mentions:

git merge -s ours ref-to-be-merged
git diff --binary ref-to-be-merged | git apply -R --index
git commit -F .git/COMMIT_EDITMSG --amend

sometimes you do want to do this, and not because you have "crap" in your history, but perhaps because you want to change the baseline for development in a public repository where rebasing should be avoided.


git merge --strategy=theirs Simulation #4

(same blog post)

Alternatively, if you want to keep the local upstream branches fast-forwardable, a potential compromise is to work with the understanding that for sid/unstable, the upstream branch can from time to time be reset/rebased (based on events that are ultimately out of your control on the upstream project's side).
This isn't a big deal and working with that assumption means that it's easy to keep the local upstream branch in a state where it only takes fast-forward updates.

git branch -m upstream-unstable upstream-unstable-save
git branch upstream-unstable upstream-remote/master
git merge -s ours upstream-unstable
git diff --binary ref-to-be-merged | git apply -R --index --exclude="debian/*"
git commit -F .git/COMMIT_EDITMSG --amend


git merge --strategy=theirs Simulation #5

(proposed by Barak A. Pearlmutter):

git checkout MINE
git merge --no-commit -s ours HERS
git rm -rf .
git checkout HERS -- .
git checkout MINE -- debian # or whatever, as appropriate
git gui # edit commit message & click commit button


git merge --strategy=theirs Simulation #6

(proposed by the same Michael Gebetsroither):

Michael Gebetsroither chimed in, claiming I was "cheating" ;) and gave another solution with lower-level plumbing commands:

(it wouldn't be git if it wouldn't be possible with git only commands, everything in git with diff/patch/apply isn't a real solution ;).

# get the contents of another branch
git read-tree -u --reset <ID>
# selectivly merge subdirectories
# e.g superseed upstream source with that from another branch
git merge -s ours --no-commit other_upstream
git read-tree --reset -u other_upstream     # or use --prefix=foo/
git checkout HEAD -- debian/
git checkout HEAD -- .gitignore
git commit -m 'superseed upstream source' -a


git merge --strategy=theirs Simulation #7

The necessary steps can be described as:

  1. Replace your worktree with upstream
  2. Apply the changes to the index
  3. Add upstream as the second parent
  4. Commit

The command git read-tree overwrites the index with a different tree, accomplishing the second step, and has flags to update the work tree, accomplishing the first step. When committing, git uses the SHA1 in .git/MERGE_HEAD as the second parent, so we can populate this to create a merge commit. Therefore, this can be accomplished with:

git read-tree -u --reset upstream                 # update files and stage changes
git rev-parse upstream > .git/MERGE_HEAD          # setup merge commit
git commit -m "Merge branch 'upstream' into mine" # commit

这篇关于使一个分支像另一个分支一样的 git 命令的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆