Subscribe to XML Feed

27 Jul 2009

用模板(template)格式化mercurial输出

做为一款可能被众多第三方工具集成的工具软件,Mercurial内置了模板(template)功能,允许用户自定义Mercurial命令的输出格式。这不,今天我和同事 许伟 就需要把hg log的命令输出格式化成xml。

根据 这里 的介绍,hg log支持—template参数,允许用户以文本形式结合Mercurial内置的大量关键字来定义想要的格式模板。这样,一个以xml为目标结果的hg log模板就大概是这个样子:

<changeset>
  <rev>{node}</rev>
  <author>{author|escape}</author>
  <desc>{desc|escape}</desc>
  <files>
    <added>{file_adds|stringify|escape}</added>
    <modified>{file_mods|stringify|escape}</modified>
    <added>{file_adds|stringify|escape}</added>
    <deleted>{file_dels|stringify|escape}</deleted>
  </files>
</changeset>

请注意其中对stringify过滤器的使用。这个在文件处理过程中是必需的,因为file_adds等对象在Mercurial内部被称为generator,它们不是字符串,所以没有替换方法,不能直接用escape过滤器去做替换。这么用看上去像是个workaround,而事实上它也的确是。大家如果感兴趣的话可以参考Mercurial的 这个bug.

然而,这个模板存在一个问题:默认文件名是以空格来彼此区分的,所以如果有的文件名中包含空格,那么出来的结果是无法正确区分此文件和彼文件的。为了解决这个问题,我们就得用Mercurial的一个更高级的方法──样式(style)。

和模板类似,样式也是以文本加关键字的组合来定义一个模板,但是有两点不同:1)样式对格式的要求更严格,2)样式必须保存在文件中。下面就是按照样式的要求重新定义的文件:


changeset = '<changeset>\n<rev>{node}</rev>\n<author>{author}</author>\n<date>{date|date}</date>\n<files>\n<modified>\n{file_mods}</modified>\n<added>\n{file_adds}</added>\n</files>\n</changeset>'
file_mod = '<file>{file_mod}</file>\n'
file_add = '<file>{file_add}</file>\n'

其中值得一提的是file_mods等关键字的使用,当Mercurial发现一个复数的关键字引用的时候,就会在定义文件中找它的单数定义。比如说我们在changeset中定义了{file_mods},Mercurial就会在文件中寻找file_mod的单数定义。file_adds、file_dels等也都是一个道理。这样我们就可以很细粒度地去控制单个文件的输出格式了。

让人略感遗憾的是,Mercurial对样式如何使用介绍的非常有限。大家如果感兴趣,可以去研究Mercurial自带的几个样式文件,它们位于Mercurial安装目录的templates下面。

View Comments
20 Jun 2009

用hg qimport和qfresh来合并未提交代码

刚才发现自己的某一个hg工作目录里还有少量的修改没有提交,通过hg diff发现自己只是改了一个很显的spring使用错误,我不想针对这次修改再做一次单独的提交,而是想把它合并到此前的那次提交里,我该怎么办呢?

如果我在一开始用的是hg qnew的方法来提交代码的话,那么问题就变的很简单了,我只需运行hg qrefresh即可。但是我当初用的是hg ci,也就是说,本地并没有一个被queue管理的patch可以刷新。那么,是不是还可以通过qrefresh的方法来解决呢?

答案是肯定的,只不过要多走一步:

  • 首先,运行下面的命令,把tip导入hg queue:
    hg qimport -r tip
  • 然后就可以做qrefresh了:
    hg qrefresh

值得一提的是,hg qimport除了可以把一个指定的已提交版本(比如在上面例子中的tip)导入queue中之外,还可以从一个指定的文件里导入。在实际项目开发中,我和同事们常用这种方法在不同机器之间共享patch,非常方便。

View Comments
18 Mar 2009

用hg qfold来实现文件的增量更新

在日常开发中,我常常使用mercurial提供的queue功能来对本地代码进行版本管理。然而,hg qrefresh不支持增量式更新的设计却让我非常头疼。

举个例子:假设我本地queue里面已经有一个名为script的patch了,我在它的基础上又改了三个文件,现在我想把其中的两个文件更新到script patch里,用hg qrefresh的话就很别扭──直接运行hg qrefresh而不加任何参数会把所有三个文件更新上去,这是我不想看到的;而运行hg qrefresh -I file1 file2的话又会以这两个文件替换script patch里原有的所有内容,这更不是我想看到的!

这个问题可以通过使用hg qfold命令来解决。做法是:

  • 运行hg qnew命令来把想要的两个文件加到一个新的patch里:
    hg qnew -m “fold me into script” -I file1 file2 -f foldme.patch
  • 把刚创建的qfoldme弹出,使得script patch位于queue的最顶端:
    hg qpop -f
  • 把foldme合并到script patch中去:
    hg qfold foldme.patch

这样一来,就可以把两个想要的文件更新到想要的patch中去了。

大家还有什么其它的办法吗?

View Comments
16 Mar 2009

用虚拟机+mercurial来合理分配运行测试时间

在开发过程中,我们常常需要在提交代码之前先在本地运行测试,看一看当前修改的质量如何。可是问题是,一旦测试耗时过长(比如说超过15分钟),那么必然会对开发进程造成影响,因为通常在本地运行测试的时候我们都会暂时停止开发;而且过长的测试时间也势必会影响持续集成的频率,毕竟我们不想花太多的时间在等待上。

在Cruise上,我们运行本地测试的时间大约在15分钟左右,这在很大程度上阻碍了我们的持续集成步伐。解决这个问题,一方面是治本,那就是找出并解决影响测试速度的瓶颈来,我在github上有一个 项目, 专门用来做这个分析,最近一段时间会发布第一个版本;另一方面则是治标,也就是找到一个办法来让测试运行不影响本地的继续开发。

想达到后一个目的其实并不难。最简单的办法就是让测试运行在一台虚拟机上,这样测试和开发就互相不影响了。有了这个想法,我和同事Pavan就在我们的Ubuntu开发机器上装了一个VirtualBox虚拟机环境(另一个备选的方案是KVM,但是考虑到VirtualBox的安装和配置更简易省时,我们还是选定了后者),在其上创建了一个Ubuntu 8.10服务器版的虚拟机,分配了1G内存(我们的开发用机有4G内存!),然后在其上安装好运行测试所需的环境(mercurial, git, ant, nant, ruby, rake…)。

接下来要做的就是把源代码从开发机器上转移到虚拟机了。由于我们用的工具mercurial对点对点协作模式有着天然的支持,因此做到这一点非常容易:我们首先在开发机器上运行hg serve,使得它可以对外提供源代码访问服务;然后再在虚拟机里做hg clone,把开发机器上的修改同步到虚拟机上。整个过程结束后,我们就可以在虚拟机上直接运行ant命令来运行测试了。

如果工作机器上后续又出现了新的代码修改,我们的操作流程稍微有一点变化。由于我们在开发过程中都是使用 hg queue 来管理本地修改的,每次更新queue都会导致开发机器代码仓库中被修改的那条记录的版本发生变化,因此直接用hg pull -u更新到虚拟机的话就会产生版本冲突现象。解决的办法也很简单,就是在虚拟机里运行hg out,把相对于开发机器多出来的版本(其实也就是在开发机器上被更新过的版本)用hg strip命令消掉。然后再运行hg pull -u就不会出现冲突了。

通过把测试工作转移到单独的虚拟机来进行,我们节省了大量的原本用于等待的时间,从而很大地提升了团队的工作效率。

View Comments

Older Posts

在Mercurial中查看被重命名文件的历史 26 Feb 2009 Comments
有选择地创建Mercurial patch 05 Oct 2008 Comments