如何和/或为什么在 Git 中合并比在 SVN 中更好?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/2471606/
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
How and/or why is merging in Git better than in SVN?
提问by Mr. Boy
I've heard in a few places that one of the main reasons why distributed version control systems shine, is much better merging than in traditional tools like SVN. Is this actually due to inherent differences in how the two systems work, or do specificDVCS implementations like Git/Mercurial just have cleverer merging algorithms than SVN?
我在一些地方听说过,分布式版本控制系统大放异彩的主要原因之一是合并比在 SVN 等传统工具中要好得多。这实际上是由于两个系统工作方式的固有差异,还是像 Git/Mercurial这样的特定DVCS 实现只是具有比 SVN 更聪明的合并算法?
回答by Spoike
The claim of why merging is better in a DVCS than in Subversion was largely based on how branching and merge worked in Subversion a while ago. Subversion prior to 1.5.0didn't store any information about when branches were merged, thus when you wanted to merge you had to specify which range of revisions that had to be merged.
为什么在 DVCS 中合并比在 Subversion 中更好的说法主要基于不久前在 Subversion 中分支和合并的工作方式。1.5.0之前的 Subversion不存储有关何时合并分支的任何信息,因此当您想要合并时,您必须指定必须合并的修订范围。
So why did Subversion merges suck?
那么为什么 Subversion 合并很糟糕呢?
Ponder this example:
思考这个例子:
1 2 4 6 8
trunk o-->o-->o---->o---->o
\
\ 3 5 7
b1 +->o---->o---->o
When we want to mergeb1's changes into the trunk we'd issue the following command, while standing on a folder that has trunk checked out:
当我们想要将b1 的更改合并到主干中时,我们将发出以下命令,同时站在已检出主干的文件夹上:
svn merge -r 2:7 {link to branch b1}
… which will attempt to merge the changes from b1
into your local working directory. And then you commit the changes after you resolve any conflicts and tested the result. When you commit the revision tree would look like this:
...这将尝试将更改合并b1
到您的本地工作目录中。然后在解决任何冲突并测试结果后提交更改。当您提交修订树时,它看起来像这样:
1 2 4 6 8 9
trunk o-->o-->o---->o---->o-->o "the merge commit is at r9"
\
\ 3 5 7
b1 +->o---->o---->o
However this way of specifying ranges of revisions gets quickly out of hand when the version tree grows as subversion didn't have any meta data on when and what revisions got merged together. Ponder on what happens later:
然而,当版本树增长时,这种指定修订范围的方式很快就会失控,因为 subversion 没有任何关于何时以及哪些修订合并在一起的元数据。想想以后会发生什么:
12 14
trunk …-->o-------->o
"Okay, so when did we merge last time?"
13 15
b1 …----->o-------->o
This is largely an issue by the repository design that Subversion has, in order to create a branch you need to create a new virtual directoryin the repository which will house a copy of the trunk but it doesn't store any information regarding when and what things got merged back in. That will lead to nasty merge conflicts at times. What was even worse is that Subversion used two-way merging by default, which has some crippling limitations in automatic merging when two branch heads are not compared with their common ancestor.
这主要是 Subversion 的存储库设计的一个问题,为了创建一个分支,您需要在存储库中创建一个新的虚拟目录,该目录将容纳主干的副本,但它不存储有关何时何地的任何信息事情又被合并了。这有时会导致令人讨厌的合并冲突。更糟糕的是,Subversion 默认使用双向合并,当两个分支头不与它们的共同祖先进行比较时,它在自动合并方面有一些严重的限制。
To mitigate this Subversion now stores meta data for branch and merge. That would solve all problems right?
为了缓解这种情况,Subversion 现在存储用于分支和合并的元数据。这样就能解决所有问题了吧?
And oh, by the way, Subversion still sucks…
哦,顺便说一句,Subversion 仍然很烂……
On a centralized system, like subversion, virtual directoriessuck. Why? Because everyone has access to view them… even the garbage experimental ones. Branching is good if you want to experiment but you don't want to see everyones' and their aunts experimentation. This is serious cognitive noise. The more branches you add, the more crap you'll get to see.
在像 subversion 这样的集中式系统上,虚拟目录很糟糕。为什么?因为每个人都可以查看它们……甚至是垃圾实验性的。如果您想进行实验,但又不想看到每个人及其阿姨的实验,分支是很好的。这是严重的认知噪音。你添加的分支越多,你就会看到越多的废话。
The more public branches you have in a repository the harder it will be to keep track of all the different branches. So the question you'll have is if the branch is still in development or if it is really dead which is hard to tell in any centralized version control system.
存储库中的公共分支越多,跟踪所有不同分支的难度就越大。因此,您将面临的问题是分支是否仍在开发中,或者它是否真的已经死了,这在任何集中式版本控制系统中都很难判断。
Most of the time, from what I've seen, an organization will default to use one big branch anyway. Which is a shame because that in turn will be difficult to keep track of testing and release versions, and whatever else good comes from branching.
大多数情况下,根据我所见,组织无论如何都会默认使用一个大分支。这是一种耻辱,因为这反过来将很难跟踪测试和发布版本,而其他任何好处都来自分支。
So why are DVCS, such as Git, Mercurial and Bazaar, better than Subversion at branching and merging?
那么为什么 DVCS(例如 Git、Mercurial 和 Bazaar)在分支和合并方面优于 Subversion?
There is a very simple reason why: branching is a first-class concept. There are no virtual directoriesby design and branches are hard objects in DVCS which it needs to be such in order to work simply with synchronization of repositories (i.e. pushand pull).
原因很简单:分支是一流的概念。设计上没有虚拟目录,分支是 DVCS 中的硬对象,它需要这样才能简单地与存储库同步(即push和pull)一起工作。
The first thing you do when you work with a DVCS is to clone repositories (git's clone
, hg's clone
and bzr's branch
). Cloning is conceptually the same thing as creating a branch in version control. Some call this forkingor branching(although the latter is often also used to refer to co-located branches), but it's just the same thing. Every user runs their own repository which means you have a per-user branchinggoing on.
使用 DVCS 时要做的第一件事是克隆存储库(git 的clone
、hg 的clone
和 bzr 的branch
)。克隆在概念上与在版本控制中创建分支相同。有些人称之为分叉或分支(尽管后者通常也用于指代位于同一位置的分支),但这是同一回事。每个用户都运行自己的存储库,这意味着您有一个针对每个用户的分支。
The version structure is not a tree, but rather a graphinstead. More specifically a directed acyclic graph(DAG, meaning a graph that doesn't have any cycles). You really don't need to dwell into the specifics of a DAG other than each commit has one or more parent references (which what the commit was based on). So the following graphs will show the arrows between revisions in reverse because of this.
版本结构不是树,而是图。更具体地说,有向无环图(DAG,意思是没有任何循环的图)。除了每个提交都有一个或多个父引用(提交所基于的引用)之外,您真的不需要深入研究 DAG 的细节。因此,下图将因此反向显示修订之间的箭头。
A very simple example of merging would be this; imagine a central repository called origin
and a user, Alice, cloning the repository to her machine.
一个非常简单的合并示例是这样的;想象一个名为的中央存储库origin
和一个用户 Alice,将存储库克隆到她的机器上。
a… b… c…
origin o<---o<---o
^master
|
| clone
v
a… b… c…
alice o<---o<---o
^master
^origin/master
What happens during a clone is that every revision is copied to Alice exactly as they were (which is validated by the uniquely identifiable hash-id's), and marks where the origin's branches are at.
在克隆过程中发生的事情是,每个修订版都完全按原样复制到 Alice(由唯一可识别的哈希 ID 验证),并标记原始分支所在的位置。
Alice then works on her repo, committing in her own repository and decides to push her changes:
然后 Alice 处理她的 repo,在她自己的存储库中提交并决定推送她的更改:
a… b… c…
origin o<---o<---o
^ master
"what'll happen after a push?"
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
The solution is rather simple, the only thing that the origin
repository needs to do is to take in all the new revisions and move it's branch to the newest revision (which git calls "fast-forward"):
解决方案相当简单,origin
存储库唯一需要做的就是接收所有新修订并将其分支移动到最新修订(git 称之为“快进”):
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
a… b… c… d… e…
alice o<---o<---o<---o<---o
^master
^origin/master
The use case, which I illustrated above, doesn't even need to merge anything. So the issue really isn't with merging algorithms since three-way merge algorithm is pretty much the same between all version control systems. The issue is more about structure than anything.
我在上面说明的用例甚至不需要合并任何东西。所以问题真的不在于合并算法,因为三路合并算法在所有版本控制系统之间几乎相同。问题更多地与结构有关。
So how about you show me an example that has a realmerge?
那你给我看一个真正合并的例子怎么样?
Admittedly the above example is a very simple use case, so lets do a much more twisted one albeit a more common one. Remember that origin
started out with three revisions? Well, the guy who did them, lets call him Bob, has been working on his own and made a commit on his own repository:
诚然,上面的例子是一个非常简单的用例,所以让我们做一个更扭曲的例子,尽管它更常见。还记得那origin
是从三个修订版开始的吗?好吧,做这些的人,让我们称他为Bob,一直在自己工作并在他自己的存储库上进行了提交:
a… b… c… f…
bob o<---o<---o<---o
^ master
^ origin/master
"can Bob push his changes?"
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
Now Bob can't push his changes directly to the origin
repository. How the system detects this is by checking if Bob's revisions directly descents from origin
's, which in this case doesn't. Any attempt to push will result into the system saying something akin to "Uh... I'm afraid can't let you do that Bob."
现在,Bob 无法将他的更改直接推送到origin
存储库。系统如何检测到这一点是通过检查 Bob 的修订是否直接从origin
's下降,在这种情况下不是。任何推动的尝试都会导致系统说类似于“呃......我恐怕不能让你这样做鲍勃”。
So Bob has to pull-in and then merge the changes (with git's pull
; or hg's pull
and merge
; or bzr's merge
). This is a two-step process. First Bob has to fetch the new revisions, which will copy them as they are from the origin
repository. We can now see that the graph diverges:
因此,Bob 必须引入并合并更改(使用 git 的pull
; 或 hgpull
和merge
; 或 bzr merge
)。这是一个两步过程。首先,Bob 必须获取新的修订,这将从origin
存储库中按原样复制它们。我们现在可以看到图形发散:
v master
a… b… c… f…
bob o<---o<---o<---o
^
| d… e…
+----o<---o
^ origin/master
a… b… c… d… e…
origin o<---o<---o<---o<---o
^ master
The second step of the pull process is to merge the diverging tips and make a commit of the result:
拉取过程的第二步是合并不同的提示并提交结果:
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
^ origin/master
Hopefully the merge won't run into conflicts (if you anticipate them you can do the two steps manually in git with fetch
and merge
). What later needs to be done is to push in those changes again to origin
, which will result into a fast-forward merge since the merge commit is a direct descendant of the latest in the origin
repository:
希望合并不会遇到冲突(如果你预料到它们,你可以在 git 中手动完成这两个步骤fetch
和merge
)。稍后需要做的是将这些更改再次推送到origin
,这将导致快进合并,因为合并提交是origin
存储库中最新提交的直接后代:
v origin/master
v master
a… b… c… f… 1…
bob o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
v master
a… b… c… f… 1…
origin o<---o<---o<---o<-------o
^ |
| d… e… |
+----o<---o<--+
There is another option to merge in git and hg, called rebase, which'll move Bob's changes to after the newest changes. Since I don't want this answer to be any more verbose I'll let you read the git, mercurialor bazaardocs about that instead.
还有另一种合并 git 和 hg 的选项,称为rebase,它将把 Bob 的更改移到最新的更改之后。因为我不想让这个答案变得更冗长,所以我会让你阅读关于它的git、mercurial或bazaar文档。
As an exercise for the reader, try drawing out how it'll work out with another user involved. It is similarly done as the example above with Bob. Merging between repositories is easier than what you'd think because all the revisions/commits are uniquely identifiable.
作为读者的练习,请尝试绘制出与其他用户一起使用的方法。与上面使用 Bob 的示例类似。存储库之间的合并比您想象的要容易,因为所有修订/提交都是唯一可识别的。
There is also the issue of sending patches between each developer, that was a huge problem in Subversion which is mitigated in git, hg and bzr by uniquely identifiable revisions. Once someone has merged his changes (i.e. made a merge commit) and sends it for everyone else in the team to consume by either pushing to a central repository or sending patches then they don't have to worry about the merge, because it already happened. Martin Fowler calls this way of working promiscuous integration.
还有在每个开发人员之间发送补丁的问题,这在 Subversion 中是一个巨大的问题,在 git、hg 和 bzr 中通过唯一可识别的修订来缓解。一旦有人合并了他的更改(即进行了合并提交)并将其发送给团队中的其他人以通过推送到中央存储库或发送补丁来使用,那么他们就不必担心合并,因为它已经发生了. Martin Fowler 将这种工作方式称为混杂集成。
Because the structure is different from Subversion, by instead employing a DAG, it enables branching and merging to be done in an easier manner not only for the system but for the user as well.
由于其结构与 Subversion 不同,因此通过使用 DAG,它不仅对系统而且对用户而言都能够以更简单的方式完成分支和合并。
回答by Andrew Aylett
Historically, Subversion has only been able to perform a straight two-way merge because it's didn't store any merge information. This involves taking a set of changes and applying them to a tree. Even with merge information, this is still the most commonly-used merge strategy.
从历史上看,Subversion 只能执行直接的双向合并,因为它不存储任何合并信息。这涉及采取一组更改并将它们应用于树。即使有合并信息,这仍然是最常用的合并策略。
Git uses a 3-way merge algorithm by default, which involves finding a common ancestor to the heads being merged and making use of the knowledge that exists on both sides of the merge. This allows Git to be more intelligent in avoiding conflicts.
Git 默认使用 3 路合并算法,这涉及找到被合并的头的共同祖先,并利用合并双方存在的知识。这使得 Git 在避免冲突方面更加智能。
Git also has some sophisticated rename finding code, which also helps. It doesn'tstore changesets or store any tracking information -- it just stores the state of the files at each commit and uses heuristics to locate renames and code movements as required (the on-disk storage is more complicated than this, but the interface it presents to the logic layer exposes no tracking).
Git 还有一些复杂的重命名查找代码,这也有帮助。它不存储变更集或存储任何跟踪信息——它只存储每次提交时文件的状态,并根据需要使用启发式方法来定位重命名和代码移动(磁盘存储比这更复杂,但界面它呈现给逻辑层不暴露任何跟踪)。
回答by Andreas Krey
Put simply, the merge implementation is done better in Gitthan in SVN. Before 1.5 SVN did not record a merge action, so it was incapable to do future merges without help by the user which needed to provide information that SVN did not record. With 1.5 it got better, and indeed the SVN storage model is slightly more capable that Git's DAG. But SVN stored the merge information in a rather convoluted form that lets merges take massively more time than in Git - I've observed factors of 300 in execution time.
简而言之,合并实现在Git 中比在SVN 中做得更好。在 1.5 之前的 SVN 没有记录合并操作,因此在没有用户帮助的情况下无法进行未来的合并,用户需要提供 SVN 没有记录的信息。在 1.5 中它变得更好了,实际上 SVN 存储模型比 Git 的 DAG 更强大。但是 SVN 以一种相当复杂的形式存储合并信息,这使得合并花费的时间比在 Git 中要多得多——我观察到执行时间有 300 倍。
Also, SVN claims to track renames to aid merges of moved files. But actually it still stores them as a copy and a separate delete action, and the merge algorithm still stumbles over them in modify/rename situations, that is, where a file is modified on one branch and rename on the other, and those branches are to be merged. Such situations will still produce spurious merge conflicts, and in the case of directory renames it even leads to silent loss of modifications. (The SVN people then tend to point out that the modifications are still in the history, but that doesn't help much when they aren't in a merge result where they should appear.
此外,SVN 声称跟踪重命名以帮助合并移动的文件。但实际上它仍然将它们存储为一个副本和一个单独的删除操作,并且合并算法仍然在修改/重命名情况下偶然发现它们,即在一个分支上修改文件并在另一个分支上重命名,而这些分支是要合并。这种情况仍然会产生虚假的合并冲突,在目录重命名的情况下,它甚至会导致修改的无声丢失。(然后 SVN 人员倾向于指出修改仍然在历史中,但是当它们不在它们应该出现的合并结果中时,这没有多大帮助。
Git, on the other hand, does not even track renames but figures them out after the fact (at merge time), and does so pretty magically.
另一方面,Git 甚至不跟踪重命名,而是事后(在合并时)计算出它们,并且这样做非常神奇。
The SVN merge representation also has issues; in 1.5/1.6 you could merge from trunk to branch as often as just liked, automatically, but a merge in the other direction needed to be announced (--reintegrate
), and left the branch in an unusable state. Much later they found out that this actually isn't the case, and that a) the --reintegrate
canbe figured out automatically, and b) repeated merges in both directions are possible.
SVN 合并表示也有问题;在 1.5/1.6 中,您可以随意自动地从主干合并到分支,但是需要宣布另一个方向的合并 ( --reintegrate
),并使分支处于无法使用的状态。很久以后,他们发现实际情况并非如此,并且 a)--reintegrate
可以自动计算出来,并且 b) 可以在两个方向上重复合并。
But after all this (which IMHO shows a lack of understanding of what they are doing), I'd be (OK, I am) very cautions to use SVN in any nontrivial branching scenario, and would ideally try to see what Git thinks of the merge result.
但毕竟这一切(恕我直言,这表明他们对他们在做什么缺乏了解),我会(好吧,我)非常谨慎地在任何重要的分支场景中使用 SVN,并且理想情况下会尝试了解 Git 的想法合并结果。
Other points made in the answers, as the forced global visibility of branches in SVN, aren't relevant to merge capabilities (but for usability). Also, the 'Git stores changes while SVN stores (something different)' are mostly off the point. Git conceptually stores each commit as a separate tree (like a tarfile), and then uses quite some heuristics to store that efficiently. Computing the changes between two commits is separate from the storage implementation. What is true is that Git stores the history DAG in a much more straightforward form that SVN does its mergeinfo. Anyone trying to understand the latter will know what I mean.
答案中提出的其他观点,如 SVN 中分支的强制全局可见性,与合并功能无关(但与可用性无关)。此外,“Git 存储变化而 SVN 存储(不同的东西)”大多是离题的。Git 在概念上将每个提交存储为一个单独的树(如tar文件),然后使用相当多的启发式方法来有效地存储它。计算两次提交之间的更改与存储实现是分开的。确实,Git 以一种更直接的形式存储历史 DAG,而 SVN 会执行其合并信息。任何试图理解后者的人都会明白我的意思。
In a nutshell: Git uses a much simpler data model to store revisions than SVN, and thus it could put a lot of energy into the actual merge algorithms rather than trying to cope with the representation => practically better merging.
简而言之:Git 使用比 SVN 更简单的数据模型来存储修订,因此它可以将大量精力投入到实际的合并算法中,而不是试图处理表示 => 实际上更好的合并。
回答by daniel kullmann
One thing that hasn't been mentioned in the other answers, and that really is a big advantage of a DVCS, is that you can commit locally before you push your changes. In SVN, when I had some change I wanted to check in, and someone had already done a commit on the same branch in the meantime, this meant that I had to do an svn update
before I could commit. This means that my changes, and the changes from the other person are now mixed together, and there is no way to abort the merge (like with git reset
or hg update -C
), because there is no commit to go back to. If the merge is non-trivial,this means that you can't continue to work on your feature before you have cleaned up the merge result.
其他答案中没有提到的一件事,这确实是 DVCS 的一大优势,是您可以在推送更改之前在本地提交。在 SVN 中,当我有一些更改时,我想签入,同时有人已经在同一个分支上进行了提交,这意味着我必须先进行一次svn update
才能提交。这意味着我的更改和其他人的更改现在混合在一起,并且无法中止合并(如 withgit reset
或hg update -C
),因为没有返回到的提交。如果合并很重要,这意味着在清理合并结果之前您无法继续处理您的功能。
But then, maybe that is only an advantage for people who are too dumb to use separate branches (if I remember correctly, we had only one branch that was used for development back in the company where I used SVN).
但是,对于那些太笨而无法使用单独分支的人来说,这可能只是一个优势(如果我没记错的话,在我使用 SVN 的公司中,我们只有一个用于开发的分支)。
回答by Peter
EDIT: This is primarily addressing this partof the question:
Is this actually due to inherent differences in how the two systems work, or do specific DVCS implementations like Git/Mercurial just have cleverer merging algorithms than SVN?
TL;DR - Those specific tools have better algorithms. Being distributed has some workflow benefits, but is orthogonal to the merging advantages.
END EDIT
编辑:这主要是解决问题的这一部分:
这实际上是由于两个系统工作方式的固有差异,还是像 Git/Mercurial 这样的特定 DVCS 实现只是比 SVN 具有更聪明的合并算法?
TL;DR - 那些特定工具有更好的算法。分布式具有一些工作流优势,但与合并优势正交。
结束编辑
I read the accepted answer. It's just plain wrong.
我阅读了接受的答案。这完全是错误的。
SVNmerging can be a pain, and it can also be cumbersome. But, ignore how it actually works for a minute. There is no information that Gitkeeps or can derive that SVN doesn't also keep or can derive. More importantly, there is no reason why keeping separate (sometimes partial) copies of the version control system will provide you with more actual information. The two structures are completely equivalent.
SVN合并可能很痛苦,也可能很麻烦。但是,暂时忽略它的实际工作原理。没有任何信息是Git保留或可以导出的,而 SVN 也没有保留或可以导出。更重要的是,没有理由保留版本控制系统的单独(有时是部分)副本将为您提供更多实际信息。这两种结构是完全等价的。
Assume you want to do "some clever thing" Git is "better at". And you're thing is checked into SVN.
假设你想做“一些聪明的事情”,Git 是“擅长”的。你的东西被签入了SVN。
Convert your SVN into the equivalent Git form, do it in Git, and then check the result in, perhaps using multiple commits, some extra branches. If you can imagine an automated way to turn an SVN problem into a Git problem, then Git has no fundamental advantage.
将您的 SVN 转换为等效的 Git 形式,在 Git 中进行,然后检查结果,可能使用多次提交,一些额外的分支。如果你能想象一种将 SVN 问题变成 Git 问题的自动化方式,那么 Git 就没有根本的优势了。
At the end of the day, any version control system will let me
归根结底,任何版本控制系统都会让我
1. Generate a set of objects at a given branch/revision.
2. Provide the difference between a parent child branch/revisions.
Additionally, for merging it's also useful (or critical) to know
此外,对于合并,了解它也很有用(或至关重要)
3. The set of changes have been merged into a given branch/revision.
Mercurial, Git and Subversion (now natively, previously using svnmerge.py) can all provide all three pieces of information. In order to demonstrate something fundamentally better with DVC, please point out some fourth piece of information which is available in Git/Mercurial/DVC not available in SVN / centralized VC.
Mercurial、Git 和 Subversion(现在是原生的,以前使用 svnmerge.py)都可以提供所有这三个信息。为了从根本上用 DVC 展示一些更好的东西,请指出在 Git/Mercurial/DVC 中可用但在 SVN/集中式 VC 中不可用的第四条信息。
That's not to say they're not better tools!
这并不是说它们不是更好的工具!
回答by used2could
SVN tracks files while Git tracks contentchanges. It is clever enough to track a block of code that was refactored from one class/file to another. They use two complete different approaches to tracking your source.
SVN 跟踪文件,而 Git 跟踪内容更改。跟踪从一个类/文件重构到另一个类/文件的代码块是足够聪明的。他们使用两种完全不同的方法来跟踪您的来源。
I still use SVN heavily, but I am very pleased with the few times I've used Git.
我仍然大量使用 SVN,但我对使用 Git 的几次感到非常满意。
A nice read if you have the time: Why I chose Git
如果你有时间,请阅读:为什么我选择 Git
回答by rubayeet
Just read an article on Joel's blog(sadly his last one). This one is about Mercurial, but it actually talks about advantages of Distributed VC systems such as Git.
刚刚阅读了 Joel 博客上的一篇文章(遗憾的是他的最后一篇)。这个是关于 Mercurial 的,但它实际上是在谈论分布式 VC 系统(例如 Git)的优势。
With distributed version control, the distributed part is actually not the most interesting part. The interesting part is that these systems think in terms of changes, not in terms of versions.
对于分布式版本控制,分布式部分实际上并不是最有趣的部分。有趣的是,这些系统根据变化而不是版本来思考。
Read the article here.
在此处阅读文章。