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
22 Apr 2009

重构技巧──inline类成员变量

这是今天在和 李教授 pair的时候学到的:

问题

有一个这样的类定义(简化版):


public class OnCurrentActivityPage {
  private String pipelineName;
  
  public OnCurrentActivityPage(ScenarioState scenarioState) {
    this.pipelineName = scenarioState.pipelineName();
  }

  public void triggerPipeline() {
    selenium.trigger(pipelineName);
  }

  public void rerunStage() {
    selenium.rerun(pipelineName, stageName);
  }

  public void pausePipeline() {
    selenium.pause(pipelineName);
  }

  // 很多其它方法也要用到pipelineName
}

我们发现在OnCurrentActivityPage构造函数里调用scenarioState.pipelineName()有bug,需要把它改成不缓存pipelineName而在每次需要它的时候直接调用scenarioState.pipelineName()。

解决方案一(人工替换)

最直接的办法是删掉pipelineName,把scenarioState保存成类成员变量,然后把所有使用pipelineName的代码替换成使用scenarioState.pipelineName()。由于这个类里面有不少方法(超过10个)需要修改,这个方案颇花时间。

解决方案二(自动重构)

Intellij支持对类成员变量做inline重构。但是前提是:

  • 成员变量必须在声明的同时做初始化
  • 成员变量必须被标记为final

这两点要求都很容易满足。为了满足第一点,我们可以把pipelineName的初始化移到声明处:


private String pipelineName = scenarioState.pipelineName();

这时候IDE会报错说scenarioState没有定义,不要理它,因为inline的时候Intellij会把该代码原样移走。

为了满足第二点,我们只需要把pipelineName标记为final:


private final String pipelineName = scenarioState.pipelineName();

然后,再在任何一个使用pipelineName的地方用Ctrl + N执行inline重构,并且指定重构范围为所有的pipelineName调用。转瞬之间,费时费力的重构被工具自动化完成了!

View Comments
20 Apr 2009

在Jetty中定位临时文件目录

今天花了不少时间改一个使用Jetty部署Web应用程序的bug,对Jetty中设置Web应用程序临时文件路径的规则有了一定了解。俗话说“好记性不如烂笔头”,现在把学到的知识整理一下,加深一下学习印象,呵呵。

Jetty在启动一个Web应用程序的时候,会按照以下顺序来决定当前应用的临时文件目录(javax.servlet.context.tempdir):

  • 通过WebAppContext.setTempDirectory设置的临时文件目录
  • 上下文中已经定义的javax.servlet.context.tempdir所对应的目录
  • ${jetty.home}/work目录
  • WEB-INF/work目录
  • ${java.io.tmpdir}目录

在通过上述方式定位到一个目录后,Jetty会在其中按照jetty_host_port_virtualHost_contextPath_hash的格式创建一个子目录,并把该子目录作为临时文件目录注册给javax.servlet.context.tempdir。

这里面需要注意的是,除了第一种(并且仅当该目录为Jetty创建的前提下)和最后一种使用场景下Jetty会在Web应用程序退出以后自动删除临时目录以外,其余场景Jetty都会保留临时文件目录。这就意味着,如果在同一目录下部署应用程序的新版本,那么很有可能会出现文件版本不一致的问题!请大家在使用的时候要格外小心。

View Comments

Older Posts

单纯的过程改进真的能提高开发团队的交付能力吗? 07 Apr 2009 Comments
用hg qfold来实现文件的增量更新 18 Mar 2009 Comments
用虚拟机+mercurial来合理分配运行测试时间 16 Mar 2009 Comments
持续集成是一种思维方式 07 Mar 2009 Comments
用visitor来封装实现细节 06 Mar 2009 Comments
在Mercurial中查看被重命名文件的历史 26 Feb 2009 Comments
用封装来解决代码重复 18 Feb 2009 Comments
向github迁移 20 Jan 2009 Comments
用脚本自动转换和发布blog 30 Dec 2008 Comments
重构blog 29 Dec 2008 Comments
一次有趣的开发经历 28 Dec 2008 Comments
Sunbird 16 Dec 2008 Comments
自动删除不需要的方法调用 22 Nov 2008 Comments
寻找问题的根源 19 Nov 2008 Comments
有选择地创建Mercurial patch 05 Oct 2008 Comments
团队设计能力的交接 18 Aug 2008 Comments
禁掉烦人的system beep 02 Aug 2008 Comments
Cruise 1.0正式发布! 28 Jul 2008 Comments
在Ubuntu上安装fcitx 27 Jul 2008 Comments
两个有趣的alias 27 Jul 2008 Comments