Mercurial如何与多个开发人员一起工作?

14
我查看了一些已知产品的Mercurial存储库,比如TortoiseHgPython,即使我可以看到多个人提交更改,时间轴看起来总是非常干净,只有一个分支在前进。
然而,假设您有14个人在同一个产品上工作,这样会不会很快陷入14个并行分支的噩梦中?
例如,只有两个人,并且产品处于变更集X状态,现在两个开发人员在星期一早上开始独立的功能开发,因此两者都从相同的父变更集开始。
当他们提交时,我们现在有两个分支,然后使用14个人,我们将很快拥有需要合并回默认值的10+(可能不是14...)个分支。
或者...我看到了什么?也许这真的不是问题?

编辑:我看到有些人对我的问题存在一些困惑,所以让我澄清一下。

我非常清楚Mercurial很容易处理多个分支和合并,正如其中一个答案所述,即使人们在同一文件上工作,他们也不经常在同一行上工作,即使这样,冲突也很容易处理。我也知道,如果两个人因为在同一文件中更改了大量相同的代码而创建了合并地狱,那么这里存在一些整体规划失败,因为我们已经将两个功能放置在完全相同的位置上,给了两个开发者,而不是尝试让他们一起工作,或者一开始就把两个功能都交给一个开发者。

所以问题不在于此。

我想知道的是这些开源项目如何管理如此干净的历史记录。对我来说(正如一条评论所想),历史记录是否干净并不重要,我的意思是,我们确实在并行工作,仓库能够反映出这一点,那就更好了(在我看来),然而我查看的这些仓库却没有这种情况。它们似乎沿用Subversion模型,在这种模型下,您不能在更新和合并之前提交,这种情况下历史记录只是一条直线。

那么他们是如何做到的呢?

他们是否正在“变基”更改,使它们看起来像是遵循了分支最新提示,即使它们最初是在分支历史中提交的?移植changesets使它们看起来像是最初就在主分支中提交的?

或者我看过的项目要么太慢(目前,我没有深入了解历史),只有一个人在工作,实际上他们只添加了一些新功能?

或者他们将更改推送给一个中央维护者进行审查和集成?看起来并不是这样,因为我看过的许多项目在更改集上有不同的名称。


我注意到Python似乎在使用Subversion作为Mercurial的前端,因此这可能不是我的问题的代表性存储库。 - Lasse V. Karlsen
我在我的回答中加入了一个具体场景的例子。 - Mizipzor
6个回答

6
或者说...我在这里没有看到什么问题吗?也许这并不是真正的问题?
这不是一个真正的问题。在大型项目中,即使人们在同一功能上工作,他们通常不会在同一文件上工作。当他们在同一文件上工作时,他们通常不会修改相同的行。当他们修改相同的行时,需要手动执行合并(对于受影响的行)。
这意味着在实践中,80%以上的合并可以由Mercurial本身自动完成。
让我们举个例子:
你有:
[branch 1]  [branch2]
        \    /
         \  /
        [base]

编辑: 为了更清晰,这里所说的分支是指未命名的分支。

如果你在分支1中修改了一个文件,但是在分支2中相同的文件与基础版本相同,则选择分支1中的版本。如果该文件在分支1分支2中都被修改,则使用相同的算法逐行合并文件:如果分支1中的文件1中的第1行与基础版本中的文件1中的第1行不同,但是分支2基础版本中的第1行相等,则选择分支1中的第1行(以此类推)。

对于在两个分支中都被修改的行,Mercurial会中断自动合并过程,并提示用户选择使用哪些行,或手动编辑这些行。

由于决定使用哪些行最好由修改这些行的人来做,因此一个好的实践是让实现功能的人执行合并。这意味着如果我和你在同一项目上工作,我实现我的功能,然后从中央/公共存储库中拉取(获取每个人都使用的最新版本),然后将我的新版本与拉取的更改合并,然后将其发布到公共存储库中(此时,公共存储库具有一个主分支,其中包含我的合并更改)。然后,你从服务器上拉取并对你的更改执行相同的操作。

这意味着每个人都能够在他们的本地存储库中做任何他们想做的事情,而公共/官方存储库只有一个分支。这也意味着你需要决定一个时间框架,让人们合并他们的更改。

我曾经在我的机器上编译了三到四个不同产品版本的已编译存储库(存储库的不同分支)以及我的主要存储库中的几个不同分支(用于重构、开发等)。每当我将一个分支带到稳定状态(例如-完成重构),我就会从服务器上拉取,将该分支合并到拉取的更改中,然后将其推回服务器,并让任何人知道,如果他们对受影响的文件进行了任何更改,他们应该先从服务器上拉取。

我们曾经每周一早晨同步实现的功能,这花费了我们大约一个小时来合并所有内容,然后在服务器上进行每周构建,以提供给QA使用(在糟糕的日子里,团队中的两个成员需要花费大约两个小时左右,然后每个人都会从服务器上拉取本周的更改,并将其用作本周的新基础)。这是一个由八名开发人员组成的团队。


你似乎是指“分支”指的是未命名的分支,这是在有多个更改集与单个父更改集时发生的情况。当这些分支合并(并追加合并提交更改集)时,将它们推送到中央服务器时仍然存在两个分支。尽管您只有一个头,但历史记录中仍然存在分歧。对于OP来说,干净的历史记录似乎很重要。或者我错过了暗示的变基吗? - Mizipzor
对我来说,一个干净的历史记录并不重要,我只是想知道我查看的这些项目如何管理多个开发人员的直线历史记录。我的意思是,我自己通过拥有几台计算机并在所有计算机上工作来获得几个并行分支(未命名)。我合并更改以进行同步,但我仍然在历史记录图中获得并行分支。然而,那些项目似乎甚至没有这样的情况。难道我只是缺乏纪律性吗? - Lasse V. Karlsen
@mizipzor:我添加了一条编辑,指明我的意思是未命名分支。谢谢。 - utnapistim
1
@Lasse V. Karlsen - 我不知道他们如何管理直线历史记录(我的单一开发人员项目也有多个分支)。 - utnapistim

5
在您更新的问题中,似乎您更感兴趣的是整理历史记录的方法。当您有一个历史记录并想将其变成单一、整洁、直线状时,您可以使用rebasetransplant和/或mercurial queues。查看这三个文档,您应该会意识到如何完成工作流程。 编辑:由于我正在等待编译,这里提供一个具体示例来说明我的意思:
> hg init
> echo test > a.txt
> hg addremove && hg commit -m "added a.txt"
> echo test > b.txt
> hg addremove && hg commit -m "added b.txt"
> hg update 0 # go back to initial revision
> echo test > c.txt
> hg addremove && hg commit -m "added c.txt"

现在运行hg glog将显示这个(分叉的)历史记录,有两个分支:

@  changeset:   2:c79893255a0f
|  tag:         tip
|  parent:      0:7e1679006144
|  user:        mizipzor
|  date:        Mon Jul 05 12:20:37 2010 +0200
|  summary:     added c.txt
|
| o  changeset:   1:74f6483b38f4
|/   user:        mizipzor
|    date:        Mon Jul 05 12:20:07 2010 +0200
|    summary:     added b.txt
|
o  changeset:   0:7e1679006144
   user:        mizipzor
   date:        Mon Jul 05 12:19:41 2010 +0200
   summary:     added a.txt

执行一次变基操作,将变更集1作为子节点合并到2而不是0:

> hg rebase -s 1 -d 2

现在让我们再次检查历史记录:
@  changeset:   2:ea0c9a705a70
|  tag:         tip
|  user:        mizipzor
|  date:        Mon Jul 05 12:20:07 2010 +0200
|  summary:     added b.txt
|
o  changeset:   1:c79893255a0f
|  user:        mizipzor
|  date:        Mon Jul 05 12:20:37 2010 +0200
|  summary:     added c.txt
|
o  changeset:   0:7e1679006144
   user:        mizipzor
   date:        Mon Jul 05 12:19:41 2010 +0200
   summary:     added a.txt

快速完成!一行代码就搞定了。:)

还要注意我没有执行合并操作。当你像这样变基时,你将不得不处理合并冲突,就像你执行合并操作一样。因为在底层,这基本上就是发生的事情。在一个小的测试仓库中尝试此操作。例如,尝试更改添加在修订版0中的文件,而不仅仅是添加更多文件。


这似乎是获得像我所看到的干净历史记录最可行的方法,是的。虽然这里有几个不错的答案,但我会接受这个答案。 - Lasse V. Karlsen

5
我是一名Mercurial开发者,让我来解释一下我们/我是如何做的。
在Mercurial项目中,我们接受以补丁形式发送到邮件列表的贡献。当我们使用hg import应用这些补丁时,会进行一个隐式的变基操作,将其变基到我们正在工作的分支的最新版本。这有助于保持历史记录的清晰。
至于我的自己的更改,我使用rebasemq来线性化事物,然后再推送它们,以保持历史记录的整洁。基本上就是做这个。
hg push  # abort: creates new remote head
hg pull
hg rebase
hg push

你可以结合拉取和变基(hg pull --rebase)如果你喜欢,但我总是喜欢一步一步来。
顺便说一下,关于线性化历史的做法存在一些分歧 - 有些人认为历史应该展示事情发生的真实情况,包括所有的分支、合并等等。我发现只要不干扰公共变更集,线性化历史就可以使用并且很有用。

我同意,变更集历史记录应该反映事情的真实发生情况。我只是想知道他们是如何获得如此干净的历史记录的,因为即使只有一个开发人员(比如我自己)偶尔也会忘记在所有机器上更新并设法获得平行未命名分支。 - Lasse V. Karlsen

3

Linux内核存储在成千上万的存储库和可能数百万的分支中,这似乎并不会造成问题。对于大型项目,您需要一个存储库策略(例如,独裁者-中尉策略),但拥有许多分支是现代DVCSes的主要优势,根本不是问题。


例如,tip仓库(http://git.kernel.org/?p=linux/kernel/git/tip/linux-2.6-tip.git;a=heads)有数百个分支,而Linus的仓库只有主分支。 - Philipp

3

是的,我们需要合并代码,为了避免在主仓库出现冲突,开发者应该在子仓库上进行代码合并。

因此,在将代码推送到父仓库之前,您需要先拉取最新的更改,在本地进行合并,然后再尝试推送。这样可以避免在主仓库中出现不必要的冲突。


2
我不知道TortoiseHg团队的做法,但你可以使用Mercurial的rebase extension来“分离”一个分支并将其放在最新版本之上,从而创建单一分支。
实际上,只要看不到比应该有的更多的分支,我就不会担心有多个分支。合并并不是什么大问题。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接