我们在svn下有我们的项目,一切都很顺利。最近,一位主要客户要求我们为他们做一些非常具体的定制,需要编码和填充(无法使用配置或部署完成)。我们决定保持两条不同的发展路线:
trunk
分支是我们部署的标准版本
普通客户arsh
分支适用于此客户,正在进行中,与trunk
现在,事件arsh
应该从trunk
开始计算,有时在arsh
中实施的功能在trunk
中很有用。这种关系是有点双向的,但是一个方向非常普遍(从trunk
到arsh
),但另一个方向是偶然的。
最好的方法是什么?工作流程 ?最佳做法?见解?
编辑:我们使用PHP 5.3,MySQL,Apache和Linux。答案 0 :(得分:3)
最佳做法? #ifdef
(或由运行时配置或任何其他编译时或运行时条件有条件地包含或有条件依赖注入的相同接口的单独实现)!
将并行版本维护为分支在任何版本控制系统中都很痛苦。使用适当的条件编译或运行时配置技术可以最好地维护并行版本。
请记住,如果将分支A合并到分支B而将分支B合并回分支A,则两个分支将完全相同。这是3向合并的固有属性。它正是您想要的功能分支,但它完全不适合为不同的客户维护并行版本。
为了保留不同客户的版本而不是使用条件编译。
这种方法允许每个人通过运行所有变体的测试或者通过持续集成服务器构建和测试所有变体,始终立即检查它们是否没有破坏任何变体的任何功能。
你提到PHP。那里没有编译步骤,因此配置将只是运行时。我可能会创建一个具有客户特定覆盖的目录,该目录将有条件地包含在适当的模板中。
注意:我目前正在开发一个C ++项目,该项目以这种方式为20多个客户定制,并且可以很好地扩展。我们没有每个客户的确切代码,而是我们有一组可选的功能,并且不同的子集被发送给不同的客户。这使得测试所有功能变得容易一些,因为我们可以构建一个最大变量并对其进行测试。这有助于当您发展到大量功能时,特别是如果您的项目需要很长时间才能构建(我们的持续集成构建运行大约一个小时,每晚构建8小时并构建所有客户变体需要超过一整天)。
答案 1 :(得分: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();
}
}
}
此方法的好处是可以使您的自定义代码远离普通代码,同时仍允许您在该基类中尽可能多地共享。