将中间分支中的更改与 DVCS 合并
我使用 Plastic SCM 已经有一段时间了,但直到最近才发现我关于分支机构变更集工作方式的思维模型存在缺陷。
考虑这个简单的场景:
- 假设我有一个带有文件
foo.txt
的分支 A, - 我将 A 的最新更改集分支到子分支 B。
- 我将工作区切换到 B 并签出
foo.txt
并进行一些更改,我将其签入。 - 我将 B 的最后一个更改集分支到 C。
- 我尝试将 C 合并到 A
简而言之:A 分支到 B,更改 foo.txt
在 B 中,分支到 C,将 C 合并到 A。
令我惊讶的是B 中间分支中对 foo.txt
所做的更改被忽略,因为我没有对 C 中的 foo.txt
进行更改。所以从 C 合并到 A 后,A 包含分支出 B 之前的原始 foo.txt。
我本希望在执行从 C 到 A 的完全合并时合并 B 中的中间更改,但显然我对分支中更改集的理解是错误的。当更改神秘地丢失时,这会不时地导致相当多的清理工作。
Git 和 Mercurial 或其他 DVCS 的行为是否相似?
编辑:
塑料版本 <= 3 仅合并源分支中的更改,而不合并中间分支。
Plastic >= 4 合并整个分支路径。
I have been working with Plastic SCM for a while, but only recently discovered a flaw in my mental model of how change sets in branches work.
Consider this simple scenario:
- Assume I have a branch A with a file
foo.txt
- I branch latest change set of A to a child branch B.
- I switch workspace to B and checkout
foo.txt
and make some changes, which I check in. - I branch the last change set of B to C.
- I attempt to merge C to A
In short: A branch to B, change foo.txt
in B, branch to C, merge C to A.
To my surprise the changes made to foo.txt
in the B intermediate branch was ignored, because I didn't make changes to foo.txt
in C. So after the merge from C to A, A contains the original foo.txt before branching out B.
I would have expected my intermediate changes in B to be merged when performing a full merge from C to A, but obviously my understanding of change sets in branches have been wrong. This has caused quite some clean up from time to time when changes mysteriously were lost.
Does Git and Mercurial or other DVCS behave similarly?
Edit:
Plastic version <= 3 merges only changes in the source branch, not intermediate branches.
Plastic >= 4 merges the whole branch path.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
Git 不是这样工作的。在 Git 中,分支实际上只是指向一系列中最后一次提交的指针。当您在该分支上进行新提交时,指针会向前移动到新提交。
因此,当您从现有分支创建新分支时,您现在只有两个指向同一提交的指针。两个分支都有相同的历史。
您所讨论的场景在 Git 中看起来像这样:
在 Git 中,您只有一系列提交,但其中一些碰巧有指向它们的分支标签。 A 指向 a3(它知道 a2 是它的父级); B 指向 a4,它以 a3 作为其父级,依此类推。因此,默认情况下,如果您将 C 合并到 A 中,Git 会执行“快进”合并(这仅意味着 A 没有更改),您会得到以下结果:
现在您可以摆脱如果你愿意的话,B 可以改变。一种方法是使用交互式变基:
这会向您显示提交列表,您只需从该列表中删除 B 提交即可。 Git 会在没有该提交的情况下重放您的提交。另一种方法是从 A 开始,然后将
a5 更改作为 a3 之后的下一个更改集。但这两件事都是你必须做的特殊事情。默认情况下,B 将包含在合并中。
Git doesn't work this way. In Git, a branch is really just a pointer pointing to the last commit in a series. When you're on that branch and you make a new commit, the pointer moves ahead to the new commit.
Because of this, when you create a new branch from an existing branch, you now simply have two pointers to the same commit. Both branches have the same history.
The scenario you're talking about would look like this in Git:
In Git you just have a series of commits, but some of them happen to have branch labels pointing to them. A points to a3 (which knows a2 is its parent); B points to a4, which has a3 as its parent, and so forth. So by default if you merged C into A, Git would do a "fast-forward" merge (which just means A has no changes) and you'd get this:
Now you could get rid of the B changes if you wanted to. One way is with an interactive rebase:
This shows you a list of commits, and you just delete the B commit from that list. Git replays your commits without that one. Another way is to start at A, then use
to put the a5 changes as the next change set after a3. But both of these are special things you have to do. By default B would be included in the merge.
Mercurial 对分支的工作方式与 git 不同。在 Mercurial 中,变更集的分支名称实际上是变更集的一部分。书签的行为有点类似于 git 的分支。仍然存在差异,这篇文章很好地涵盖了 hg 书签和 git 分支。
无论是使用 Mercurial 书签还是命名分支(甚至只是匿名分支),您仍然会得到:
合并后,A 包含:在第二个变更集中完成附加文本的 a.txt(以及添加的 c.txt)在第三个)。
Mercurial works differently with branches than git does. In Mercurial, the branch name of a changeset is actually part of the changeset. Bookmarks behave somewhat similarly to git's branches. There are still differences, and this article does a nice job covering hg bookmarks and git branches.
Whether using Mercurial bookmarks or named branches (or even just anonymous branches), you still get this:
And after the merge, A contains: the a.txt that has the appended text done in the second changeset (as well as c.txt added in the third).