内容


为 Eclipse Galileo 打补丁

学会应用和共享更改

Comments

本文讨论在 Eclipse 中应用补丁,包括在 Eclipse Galileo 中引入的一个功能。要利用本篇文章中的示例,您需要安装 Eclipse 并且有一个可用的源代码库,如 Subversion(SVN)或 Concurrent Versions System(CVS)。

问题

Eclipse 集成开发环境(IDE)通过提供一些功能来促进团队环境中的工作。这些功能使您可以直接在 IDE 中与源代码控制管理系统进行集成。它们不仅能够使您获得源代码、查看并提交更改,还提供了通过把补丁应用到代码来处理更改的能力。

补丁可以以文件的形式交换,其中含有使用标准 diff 格式的代码版本之间的更改。正确创建后,补丁文件只包含修改后的文件与您的工作空间中的文件之间存在差异的地方。这不仅可以使补丁文件变得更小,而且能够更容易、更有选择地运用补丁。

在团队开发环境中,有时需要直接在开发人员之间共享对代码库的更改。使用补丁文件的不同场景包括:

  • 来自团队外部的更改 — 例如,在开源代码中,更改可能来自社区的某个人。
  • 出于某些原因,更改无法提交到当前源代码树中,比如将会影响编译的重大更改。
  • 更改很复杂,而且在提交到源代码管理系统中之前需要与其他的更改进行整合。

补丁文件的一个优势在于它能够作为电子邮件消息的附件或者 bug 报告进行提交。然后,可以把补丁文件应用到源代码中以整合修改后的代码。

补丁格式概览

在 Eclipse 中创建补丁时,补丁是以统一的 diff 格式进行编写的。这意味着您可以从 CVS 或 SVN 中创建 diff 并把它们应用到 Eclipse 项目中。它还意味着对于补丁文件,您可以依靠一个标准格式,因此,它们很容易就可以进行共享。diff 文件有几种格式(要了解更多情况,请参见 参考资料)。

了解补丁文件的格式对于理解如何在 Eclipse 中应用它们并不是很重要,但对 Eclipse 使用的 diff 文件格式有一定的了解能够帮助您解决问题并帮助您了解应用补丁时将会发生什么。

例如,请参见清单 1 中的简单 Motorcycle 类。在本文中,它是一个基准示例。您将对其进行修改,而且通过这个示例,您将了解补丁文件是如何出现在后面的示例中的。

清单 1. 示例 Motorcycle
package com.nathangood.examples;

public class Motorcycle {

    private int cc;
    private String model;
    private String make;
    private String year;

    public String getModel() {
        return model;
    }

    public int getCc() {
        return cc;
    }

    public void setCc(int cc) {
        this.cc = cc;
    }

    public void setModel(String model) {
        this.model = model;
    }

    public String getMake() {
        return make;
    }

    public void setMake(String make) {
        this.make = make;
    }

    public String getYear() {
        return year;
    }

    public void setYear(String year) {
        this.year = year;
    }
}

在类中添加一个简单的构造函数,如 清单 2 中的构造函数。

清单 2. 一个使用 make 的简单的构造函数
    public Motorcycle(String make) {
        super();
        this.make = make;
    }

现在,当您在修改后的类和类的原始版本之间生成一个统一的 diff 时,您的 diff 将如清单 3 中所示。

清单 3. 用来添加构造函数的补丁
Index: src/com/nathangood/examples/Motorcycle.java
===================================================================
--- src/com/nathangood/examples/Motorcycle.java    (revision 3)
+++ src/com/nathangood/examples/Motorcycle.java    (working copy)
@@ -7,6 +7,11 @@
     private String make;
     private String year;
 
+    public Motorcycle(String make) {
+        super();
+        this.make = make;
+    }
+
     public String getModel() {
         return model;
     }

补丁文件只包含已添加的新行数,以及前后若干行,用来提供更改的上下文。

注意补丁文件中的如下行:@@ -7,6 +7,11 @@。该行提供了行号,紧接着是它的更改范围。每个更改前的减号(-)或加号(+)与行上方的文件名一致。在这个示例中,- 指的是修订 3,而 + 指的是工作副本。

工作副本(文件的新版本)中的更改从第 7 行开始,共有 11 行,其中包括显示上下文的行。文件的原始版本(在这个示例中叫作修订 3)只包含了 6 行。这两个版本的区别在于前面标有 + 的 5 个新行。

看看另外一个示例,删除 make 变量、getMake()setMake() 的访问器(accessor)— 暂时不考虑构建函数的更改。显示这些区别的补丁如清单 4 所示。

清单 4. 使用补丁删除元素的示例
Index: src/com/nathangood/examples/Motorcycle.java
===================================================================
--- src/com/nathangood/examples/Motorcycle.java    (revision 3)
+++ src/com/nathangood/examples/Motorcycle.java    (working copy)
@@ -23,14 +23,6 @@
         this.model = model;
     }
 
-    public String getMake() {
-        return make;
-    }
-
-    public void setMake(String make) {
-        this.make = make;
-    }
-
     public String getYear() {
         return year;
     }

从补丁中可以看到,更改的上下文从存储库(修订 3)中的文件版本的第 23 行开始,共有 14 行。由于已经删除了文件中的行,所以新的更改的上下文也从第 23 行开始,仅仅只有 6 行。要删除的行以 - 开头。

最后看看原始文件的一个修改(同样不考虑最近的一次更改)。更改在清单 5 中以粗体显示,演示了从 cccubicCentimeters 的重命名。

清单 5. 使用补丁修改元素的示例
Index: src/com/nathangood/examples/Motorcycle.java
===================================================================
--- src/com/nathangood/examples/Motorcycle.java    (revision 3)
+++ src/com/nathangood/examples/Motorcycle.java    (working copy)
@@ -2,7 +2,7 @@
 
 public class Motorcycle {
 
-    private int cc;
+    private int cubicCentimeters;
     private String model;
     private String make;
     private String year;
@@ -11,12 +11,12 @@
         return model;
     }
 
-    public int getCc() {
-        return cc;
+    public int getCubicCentimeters() {
+        return cubicCentimeters;
     }
 
-    public void setCc(int cc) {
-        this.cc = cc;
+    public void setCubicCentimeters(int cubicCentimeters) {
+        this.cubicCentimeters = cubicCentimeters;
     }
 
     public void setModel(String model) {

显示更改的补丁要比简单的添加或删除更为复杂一些,这是因为它要结合这两者来显示更改。

第一处更改的上下文从第 2 行开始,共有 7 行。虽然删除了一行,但又添加了一行,所以修改后的文件上下文的范围不变。含有 cc 的原始行被含有 cubicCentimeters 的新行替换了。文件中的第二处修改采用同样的方式,上下文从第 11 行开始,一共 12 行。

创建一个简单的补丁

现在了解了补丁文件的基本结构,您可以在 Eclipse 中创建一个补丁,并把它应用到另外一个工作空间的相同项目中。如果您想在同一个工作空间中浏览这些示例,只需在 Eclipse 的源代码控制中修改某个文件即可。创建一个补丁并还原到文件的原始版本。然后重新应用补丁,您会发现所做的修改又回来了。

Motorcycle 类的原始版本中,使用 Source 菜单生成几个构造函数,如清单 6 中所示。

清单 6. 新构造函数的示例
    public Motorcycle() {
        super();
    }
    
    public Motorcycle(int cc, String model, String make, String year) {
        super();
        this.cc = cc;
        this.model = model;
        this.make = make;
        this.year = year;
    }

在 Eclipse 中,打开项目的上下文菜单(通常按下鼠标的右键),然后选择 Team > Create Patch

在 Create Patch 窗口,选择 Save in File System(请参见图 1),然后单击 Browse 为新补丁文件找到一个合适位置。结束后单击 Next

图 1. 创建补丁
显示 Create Patch 的图片
显示 Create Patch 的图片

下一步,选择 Project(请参见图 2),然后单击 Finish。您可以为工作空间中的多个项目创建一个补丁。选择 Project 时,Eclipse 会创建补丁,把当前项目作为补丁的根目录并忽略其他项目中的更改。把补丁范围限定在您的项目中能使事情变得简单些,因为这样可以减少您要检查的更改的数量。

图 2. 选择补丁根目录
显示选择补丁根目录的图片
显示选择补丁根目录的图片

查看补丁

当 Eclipse 创建好补丁文件后,您可以通过单击 File > Open File 打开文件,在 Eclipse 中查看补丁。补丁将如清单 7 所示。

清单 7. 添加新构造函数的补丁
Index: src/com/nathangood/examples/Motorcycle.java
===================================================================
--- src/com/nathangood/examples/Motorcycle.java    (revision 3)
+++ src/com/nathangood/examples/Motorcycle.java    (working copy)
@@ -6,7 +6,19 @@
     private String model;
     private String make;
     private String year;
-
+    
+    public Motorcycle() {
+        super();
+    }
+    
+    public Motorcycle(int cc, String model, String make, String year) {
+        super();
+        this.cc = cc;
+        this.model = model;
+        this.make = make;
+        this.year = year;
+    }
+    
     public String getModel() {
         return model;
     }

在 Package Explorer 中应用补丁

如果您只有一个可用的单一工作空间,那么还原您对 Motorcycle.java 所做的更改。另外一个办法就是使用一个存放有项目文件原始版本的工作空间。

在项目的原始版本中,使用 Team > Apply Patch 上下文菜单项打开图 3 中显示的 Apply Patch 向导。选择 File,然后单击 Browse,查找您在 “创建一个简单的补丁” 中创建的补丁文件的位置。

图 3. 选择补丁的输入
描述如何选择补丁输入的图片
描述如何选择补丁输入的图片

由于您是在项目而非工作空间根目录的基础上创建了补丁,选择 Apply the patch to the selected file,然后选择列表(请参见图 4)中的项目文件夹。单击 Next

图 4. 选择补丁的目标
显示如何选择补丁目标的图片
显示如何选择补丁目标的图片

下一个屏幕(如图 5 所示)显示了原版(Local Copy)和修订版(After Patch)的对比。由于这是个 Java™ 文件,Java Structure Compare 部分以概要的形式显示了文件中的区别,这在一定程度上有助于理解。

图 5. 应用更改之前进行检查
描述应用更改之前进行检查的图片
描述应用更改之前进行检查的图片

如果您对显示的更改进行了确认,单击 Finish

现在当你打开文件时,它包含了您前面所做的更改。

使用 Eclipse Galileo 应用补丁的一个替代办法就是把内容粘贴到 Package Explorer 视图中。如果您已经安装了 Eclipse Galileo,您可以根据下面的步骤应用您创建的补丁:

  1. 在文本编辑器中打开您创建的补丁文件。
  2. 把文件内容复制到剪切板。
  3. 在 Package Explorer 中,右键单击项目,然后在上下文菜单中选择 Paste
  4. 应用来自一个文件的补丁时,按照向导所示进行操作。

应用补丁的选项

图 5 显示的 Review Patch 这一步中,有几个有关应用补丁的选项。前一个示例中的简单补丁不需要在 Review Patch 步骤中对默认选项作任何更改。但是,当您在检查补丁以获得您想要的结果时,有些情况下可能需要修改设置。

应用补丁时,有时在 Review Patch(请参见图 6)步骤期间在 Patch Contents 列表中会出现错误。这表示您需要修改一些补丁选项。

图 6. 应用补丁时处理错误
应用补丁时处理错误
应用补丁时处理错误

如果补丁中的原始目录结构与您应用补丁的版本不一样,Ignore leading path name segments 选项特别有用。您可以通过创建项目补丁和在 Apply Patch 向导的 Target Resource 步骤(图 4)中选择 src 文件夹来模拟这个行为。这将会报错(如图 6),但如果您把 Ignore leading path name segments 的值改为 1,Eclipse 就能够正确地应用补丁。

Show matched hunks 选项在 Patch Contents 列表中显示行号和上下文。

Fuzz factor 选项使您能够告诉 Eclipse 在应用补丁时忽略一些上下文行。在本文的示例中,可以通过使用 Source > Sort Members 菜单选项在尝试应用补丁之前归类原始文件,从而模拟对 Fuzz factor 的需求。由于一些上下文行现在会出现问题,您需要用 Fuzz factor 来正确地应用补丁。单击 Guess,Eclipse 会尽最大努力为您查找 Fuzz factor,或者您可以不断修改设置直到您对更改满意为止。

Show Excluded 选项显示排除的补丁操作(如果有的话)。

Generate a .rej file for unmerged hunks 存放了操作过程中没有被应用的补丁文件的条目。要查看这个行为的实际效果,可以使用含有新构造函数的补丁,但是在应用之前,输入您自己的、稍微有差异的构造函数 — 如清单 8 中所示。

清单 8. 添加一个本地构造函数,形成一个冲突
    public Motorcycle() {
        // TODO:  Create some conflict with this constructor
    }

当你尝试应用补丁时,补丁文件中的代码和 Motorcycle 类中的代码将会发生一个冲突。这个冲突不会被应用到已修改的文件中。相反,它将会被写到一个叫 Motorcycle.java.rej 的文件中,如清单 9 所示。

清单 9. 被拒绝的补丁代码
@@ -5,6 +5,10 @@
     private String model;
     private String make;
     private String year;
+    
+    public Motorcycle() {
+        super();
+    }
 
     public String getModel() {
         return model;

清单 9 中显示的文件并不是一个完整有效的补丁文件。但如果先解决了冲突的话,您就能够使用它来为这个类创建一个补丁。

在 Synchronize View 中创建一个补丁

使用代码库同步更改后,另一个办法是在 Synchronize View 中创建补丁。要在 Synchronize View 中创建补丁,打开 Team Synchronizing 透视图,然后单击同步按钮,如图 7 所示。

图 7. 同步按钮
描述同步按钮的图片

在 Synchronize View 中打开项目的上下文菜单,然后单击 Create Patch。从这开始,按照 “创建一个简单的补丁” 中所描述的补丁创建流程进行操作。

逆向补丁

逆向补丁指反向执行应用补丁的动作。添加行变成了删除行。补丁中将被删除的行将被添加。您可以使用逆向补丁恢复文件的前一个版本的某些更改或者从造成问题的补丁中撤销更改。

结束语

Eclipse 团队工具使您能够使用标准统一的 diff 格式的补丁轻松共享代码更改。现在团队成员能够应用以文件形式作为附件进行提交的补丁,这可通过使用一个新方法实现:在 Eclipse Galileo 的 Package Explorer 中粘帖补丁内容。


相关主题


评论

添加或订阅评论,请先登录注册

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=10
Zone=Open source, Java technology
ArticleID=452413
ArticleTitle=为 Eclipse Galileo 打补丁
publish-date=12032009