5

我们的项目在 svn 下,一切进展顺利。最近一个大客户要求我们为他们做一些非常具体的定制,这些定制需要编码和东​​西(不能使用配置或部署来完成)。我们决定维持两条独立的发展路线:

  • trunk分支是我们为普通客户部署的标准版本
  • arsh分支是针对该客户的,并且正在与正在发生的事情分开进行持续开发trunk

现在的事情是arsh应该从 countinually 接收更新trunk,有时实现的功能arshtrunk. 这种关系有点双向,但一个方向非常普遍(从trunkarsh),而另一个是偶尔的。

最好的方法是什么?工作流程 ?最佳实践?见解?

编辑:我们使用 PHP 5.3、MySQL、Apache 和 Linux。

4

2 回答 2

3

最佳实践?#ifdef(或由运行时配置或任何其他编译时或运行时条件有条件地包含或有条件地注入相同接口的单独实现)!

在任何版本控制系统中,将并行版本作为分支来维护是一件很痛苦的事情。最好使用适当的条件编译或运行时配置技术来维护并行版本。

请记住,如果将分支 A 合并到分支 B,然后将分支 B 合并回分支 A,则两个分支将完全相同。这是 3 路合并的内在属性。这正是你想要的功能分支,但它完全不适合为不同的客户维护并行版本。

为了为不同的客户保留版本,请改用条件编译。

  • 在面向对象的代码中,您通常可以拥有具有通用逻辑的基类和具有特定于该变体的逻辑的每个变体自定义派生类,这些逻辑要么有条件地包含在项目中,要么有条件地实例化。
  • 大多数编程语言都支持某种形式的条件编译,Java 是一个明显的例外。

这种方法允许每个人通过对所有变体运行测试或由持续集成服务器构建和测试所有变体来立即检查他们没有破坏任何一个变体的任何功能。

你提到PHP。那里没有编译步骤,因此配置将只是运行时。我可能会创建一个包含特定于客户的覆盖的目录,这些覆盖将有条件地包含在适当的模板中。

注意:我目前正在开发一个 C++ 项目,该项目以这种方式为 20 多个客户定制,并且可以很好地扩展。我们没有每个客户的确切代码,而是我们有一组可选的功能,并且不同的子集被运送给不同的客户。这使得测试所有特性变得更容易一些,因为我们可以构建一个最大的变体并对其进行测试。当您发展到大量功能时,这会有所帮助,特别是如果您的项目需要很长时间才能构建(我们的持续集成构建运行大约一个小时,每晚构建 8 小时,构建所有客户变体需要超过一整天)。

于 2013-03-11T14:28:45.567 回答
0

您没有提及您的语言,但假设您使用的是面向对象的语言,请考虑将您的自定义功能保留在相同接口和/或基类的单独实现中,并在运行时使用工厂来决定要执行哪个实现跑。例如(在 C# 中):

public interface IProcessor {
    void ProcessFile(string fileName);
}

public abstract class BaseProcessor : IProcessor {
    void IProcessor.ProcessFile(string fileName) {

        // Do shared stuff here like logging, validation, etc.

        ProcessFileForClient(fileName);
    }
    protected abstract void ProcessFileForClient(string fileName);
}

public class NormalProcessor : BaseProcessor {
    protected override void ProcessFileForClient(string fileName) {
        // Do your normal routine here
    }
}

public class AcmeProcessor : BaseProcessor {
    protected override void ProcessFileForClient(string fileName) {
        // Do your custom stuff here
    }
}

// This can be as complex as you need - you should probably use an IoC/DI framework,
// but this is a simple example.
public static class ProcessorFactory {
    public static IProcessor GetProcessor(string clientCode) {
        switch (clientCode) {
            case "Acme": return new AcmeProcessor();
            default: return new NormalProcessor();
        }
    }
}

这种方法的好处是使您的自定义代码不会妨碍您的正常代码,同时仍然允许您在该基类中尽可能多地共享。

于 2013-03-11T14:39:09.497 回答