在svn下保持两条非常接近但独立的开发线?
我们有我们的svn项目,一切都进展顺利。最近,一位主要客户要求我们为他们进行一些非常具体的定制,这些定制需要编码和其他东西(无法使用配置或部署)。我们决定维持两条独立的发展路线:在svn下保持两条非常接近但独立的开发线?,svn,version-control,release-management,Svn,Version Control,Release Management,我们有我们的svn项目,一切都进展顺利。最近,一位主要客户要求我们为他们进行一些非常具体的定制,这些定制需要编码和其他东西(无法使用配置或部署)。我们决定维持两条独立的发展路线: trunk分支是我们的标准版本,用于 普通顾客 arsh分支机构专为该客户而设,与trunk 现在的问题是,arsh应该定期从trunk接收更新,有时在arsh中实现的功能在trunk中很有用。这种关系是双向的,但一个方向相当普遍(从trunk到arsh),但另一个方向是偶然的 最好的方法是什么?工作流程?最佳实践
分支是我们的标准版本,用于 普通顾客trunk
分支机构专为该客户而设,与arsh
trunk
arsh
应该定期从trunk
接收更新,有时在arsh
中实现的功能在trunk
中很有用。这种关系是双向的,但一个方向相当普遍(从trunk
到arsh
),但另一个方向是偶然的
最好的方法是什么?工作流程?最佳实践?洞察力
编辑:我们使用PHP5.3、MySQL、Apache和Linux。最佳实践#ifdef
(或运行时配置或任何其他编译时或运行时条件下有条件地包含相同接口的单独实现,或有条件地注入依赖项)
在任何版本控制系统中,将并行版本作为分支进行维护都是一件痛苦的事情。最好使用适当的条件编译或运行时配置技术来维护并行版本
请记住,如果将分支a合并到分支B,然后再将分支B合并回分支a,则两个分支将完全相同。这是3路合并的固有属性。这正是您想要的特性分支,但它完全不适合为不同的客户维护并行版本
要为不同的客户保留版本,请使用条件编译
- 在面向对象的代码中,通常可以使用带有公共逻辑的基类和带有特定于该变量的逻辑的每变量自定义派生类,该变量可以有条件地包含在项目中,也可以有条件地实例化
- 大多数编程语言支持某种形式的条件编译,Java是一个显著的例外
注:我目前正在开发一个C++项目,它以这种方式为超过20个客户定制,并且规模很好。我们没有为每个客户提供精确的代码,相反,我们有一组可选的功能,不同的子集将被提供给不同的客户。这使得测试所有特性变得更加容易,因为我们可以构建一个最大的变体并测试它。当您扩展到大量功能时,这会有所帮助,特别是当您的项目需要很长时间来构建时(我们的持续集成构建运行约一小时,每夜构建8小时,构建所有客户变体需要一整天以上)。您没有提到您的语言,但假设您使用的是面向对象的语言,考虑将自定义功能保留在同一接口和/或基类的不同实现中,并在运行时使用工厂来决定运行哪个实现。例如(在C中):
这种方法的好处是使您的自定义代码不受普通代码的影响,同时仍然允许您在该基类中共享您想要的内容。只要您可以向不同的客户提供不同的版本,我宁愿只在代码中的相应实现中编译。即,同一名称下的多个实施,有条件地包括在项目中。C#还有条件编译,如果差异很小,这是最合适的。@JanHudec非常正确-我想这取决于环境。如果您正在处理一个基于web或服务的应用程序,这其实并不重要。但是作为一个打包产品,您的条件编译可能是更好的选择。
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();
}
}
}