使一个分支像另一个分支一样的 git 命令
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4911794/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
git command for making one branch like another
提问by Arne Claassen
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.
我试图采取一个有变化的分支,并将其恢复到与它分歧的上游相同。更改都是本地的并且已推送到 github,因此两者都不是git reset
或git rebase
真的可行,因为它们更改了历史记录,这对于已经推送的分支来说是一件坏事。
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.
我也尝试git merge
过各种策略,但都没有撤消本地更改,即如果我添加了一个文件,合并可能会使其他文件重新排列,但我仍然拥有上游没有的那个文件有。
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?
我可以在上游创建一个新分支,但我真的很喜欢合并,在修订历史方面应用所有更改以获取我的分支并使其再次与上游相同,以便我可以安全地推送该更改没有破坏历史。有没有这样的命令或一系列命令?
回答by VonC
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:
您可以dev
使用自定义合并驱动程序“keepTheirs”将上游分支合并到您的分支:
请参阅需要的“ “ git merge -s theirs
”——但我知道它不存在“。
在您的情况下,只.gitattributes
需要一个,并且keepTheirs
脚本如下:
mv -f
exit 0
git merge --strategy=theirs
Simulation #1
git merge --strategy=theirs
模拟 #1
Shows as a merge, with upstream as the first parent.
显示为合并,上游作为第一个父级。
Jefromimentions (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:
Jefromi提到(在评论中)merge -s ours
通过合并上游(或从上游开始的临时分支)上的工作,然后将分支快进到该合并的结果:
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):
(编辑 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 inwip
will get invalidated by what goes into upstream and I will abandon some part of mywip
.
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 whatgit merge --strategy=theirs
should do.
为什么我又想要这个?
只要我的 repo 与公共版本无关,这一切都很好,但是现在我希望能够与其他团队成员和外部贡献者在 WIP 上进行协作,我想确保我的公共分支是对于其他人来说,分支和拉出是可靠的,即不再对我推送到远程备份的内容进行重新设置和重置,因为它现在在 GitHub 上并且是公开的。
所以这让我知道我应该如何继续。
我的副本有 99% 的时间会进入上游 master,所以我想在大部分时间里工作我的 master 并推送到上游。
但是每隔一段时间,我所拥有wip
的东西就会因上游的东西而失效,我会放弃我的wip
.
在这一点上,我想让我的主人与上游同步,但不破坏我公开推送的主人上的任何提交点。即我想要与上游合并,最终得到使我的副本与上游相同的变更集。
这就是git merge --strategy=theirs
应该做的。
git merge --strategy=theirs
Simulation #2
git merge --strategy=theirs
模拟#2
Shows as a merge, with ours as the first parent.
显示为合并,我们的为第一个父级。
(proposed by jcwenger)
(由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
git merge --strategy=theirs
模拟 #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
git merge --strategy=theirs
模拟 #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.
或者,如果您想保持本地上游分支可快速转发,一个潜在的折衷方案是了解对于 sid/unstable,上游分支可以不时重置/重新定位(基于最终退出的事件)您在上游项目方面的控制权)。
这没什么大不了的,使用该假设意味着很容易将本地上游分支保持在只进行快进更新的状态。
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
git merge --strategy=theirs
模拟 #5
(proposed by Barak A. Pearlmutter):
(由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
git merge --strategy=theirs
模拟 #6
(proposed by the same Michael Gebetsroither):
(由同一个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 ;).
Michael Gebetsroither 插话说我在“作弊”;) 并给出了另一个具有较低级别管道命令的解决方案:
(如果仅使用 git 命令无法实现,那么它就不是 git,git 中带有 diff/patch/apply 的所有内容都不是真正的解决方案;)。
# 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
git merge --strategy=theirs
模拟 #7
The necessary steps can be described as:
- Replace your worktree with upstream
- Apply the changes to the index
- Add upstream as the second parent
- 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
用不同的树覆盖索引,完成第二步,并具有更新工作树的标志,完成第一步。提交时,git 使用 .git/MERGE_HEAD 中的 SHA1 作为第二个父级,因此我们可以填充它以创建合并提交。因此,这可以通过以下方式完成:
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
回答by wuputah
It sounds to me like you just need to do:
在我看来,您只需要执行以下操作:
$ git reset --hard origin/master
If there is no change to push upstream, and you simply want the upstream branch to be your current branch, this will do that. It is not harmful to do this locally butyou will lose any local changes** that haven't been pushed to master.
如果没有更改推送到上游,并且您只是希望上游分支成为您的当前分支,则可以这样做。在本地执行此操作无害,但您将丢失任何尚未推送到 master 的本地更改**。
** Actually the changes are still around if you have committed them locally, as the commits will still be in your git reflog
, usually for at least 30 days.
** 实际上,如果您在本地提交了更改,这些更改仍然存在,因为提交仍将在您的git reflog
.
回答by wuputah
You can do this rather easily now:
你现在可以很容易地做到这一点:
$ git fetch origin
$ git merge origin/master -s recursive -Xtheirs
This gets your local repo in-sync with the origin, and preserves the history.
这使您的本地存储库与源同步,并保留历史记录。
回答by michas
Another simulation for git merge -s theirs ref-to-be-merged
:
另一个模拟git merge -s theirs ref-to-be-merged
:
git merge --no-ff -s ours ref-to-be-merged # enforce a merge commit; content is still wrong
git reset --hard HEAD^2; git reset --soft HEAD@{1} # fix the content
git commit --amend
An alternative to the double reset would be applying the reverse patch:
双重重置的替代方法是应用反向补丁:
git diff --binary ref-to-be-merged | git apply -R --index
回答by Michal Soltys
There's also a way with little help of plumbing command - IMHO the most straightforward. Say you want to emulate "theirs" for 2 branches case:
还有一种几乎不需要管道命令帮助的方法 - 恕我直言最直接。假设您想为 2 个分支案例模拟“他们的”:
head1=$(git show --pretty=format:"%H" -s foo)
head2=$(git show --pretty=format:"%H" -s bar)
tree=$(git show --pretty=format:"%T" -s bar)
newhead=$(git commit-tree $tree -p $head1 -p $head2 <<<"merge commit message")
git reset --hard $newhead
This merges arbitrary number of heads (2 in the example above) using tree of one of them (bar in the example above, providing 'theirs' tree), disregarding any diff/file issues (commit-tree is low level command, so it doesn't care about those). Note that head can be just 1 (so equivalent of cherry-pick with "theirs").
这使用其中之一的树(上面的示例中的栏,提供“他们的”树)合并任意数量的头(在上面的示例中为 2),忽略任何差异/文件问题(commit-tree 是低级命令,因此它不在乎这些)。请注意, head 可以仅为 1(因此相当于带有“他们的”的樱桃选择)。
Note, that which parent head is specified first, can influence some stuff (see e.g. --first-parent of git-log command) - so keep that in mind.
请注意,首先指定哪个父头会影响某些内容(参见例如 git-log 命令的 --first-parent) - 所以请记住这一点。
Instead of git-show, anything else capable of outputting tree and commit hashes can be used - whatever one's is used to parsing (cat-file, rev-list, ...). You can follow everything with git commit --amend to interactively beautify commit message.
代替 git-show,可以使用任何其他能够输出树和提交哈希的东西——任何用于解析的东西(cat-file,rev-list,...)。您可以使用 git commit --amend 跟踪所有内容以交互美化提交消息。
回答by sscarduzio
Heavy handed, but hell, what can possibly go wrong?
笨手笨脚,但见鬼,可能会出什么问题?
- Check out the branch X you want to look like the Y
cp -r .git /tmp
- Check out branch Y
git checkout y
rm -rf .git && cp -r /tmp/.git
.- Commit & push any difference
- DONE.
- 查看您想要看起来像 Y 的分支 X
cp -r .git /tmp
- 检出分支 Y
git checkout y
rm -rf .git && cp -r /tmp/.git
.- 承诺并推动任何差异
- 完毕。
回答by Ivan
Use git reset BACKWARDS!
使用 git reset 向后!
You can make a branch look like any other commit with git reset
, but you have to do it in a round-about way.
您可以使用 使分支看起来像任何其他提交git reset
,但您必须以迂回的方式进行。
To make a branch on commit <old>
look like a commit <new>
, you can do
要使提交时的分支<old>
看起来像提交<new>
,您可以执行以下操作
git reset --hard <new>
in order to make <new>
the contents of the working tree.
为了制作<new>
工作树的内容。
Then do
然后做
git reset --mixed <old>
to change the branch back to the original commit but leaving working tree in the<new>
state.
将分支更改回原始提交,但将工作树保留在<new>
state 中。
Then you can add and commit the changes, in order to make your branch exactly match the contents of the <new>
commit.
然后您可以添加和提交更改,以使您的分支与<new>
提交的内容完全匹配。
It's counter-intuitive that to move from the <old>
state to the <new>
you need to do a git reset
from<new>
to<old>
. However with the option --mixed
the working tree is left at <new>
and the branch pointer set to <old>
, so that when the changes are committed the branch looks how we want.
违反直觉的是,要从<old>
state移动到 the<new>
您需要执行git reset
from <new>
to<old>
。但是,使用该选项--mixed
,工作树将保留在<new>
并且分支指针设置为<old>
,以便在提交更改时分支看起来像我们想要的那样。
Warning
警告
Don't lose track of your commits, e.g. forget what <old>
is when doing git reset --hard <new>
.
不要忘记你的提交,例如忘记<old>
在做什么git reset --hard <new>
。
回答by kelloti
change to the remote upstream branch and do a git merge
with the merge strategy set to ours
.
更改到远程上游分支git merge
,并将合并策略设置为ours
.
git checkout origin/master
git merge dev --strategy=ours
git commit ...
git push
All the history will still be present, but you'll have an extra merge commit. The important thing here is to start from the version you want to be at and merge ours
with the branch github is actually at.
所有历史记录仍将存在,但您将有一个额外的合并提交。这里重要的是从你想要的版本开始,并ours
与 github 实际所在的分支合并。
回答by esso
I followed those roles:
我遵循了这些角色:
Fetching the origin, reset hard from the branch then recursive from theirs and then forced push to branch
获取原点,从分支硬重置然后从他们的递归,然后强制推送到分支
ON YOUR OWN RISK
风险自担
git fetch origin
git reset --hard origin/<branch>
git merge origin/<branch> -s recursive -Xtheirs
git push -f <remote> <branch>