TFS не обязательно создает кандидатов на слияние на основе объектов ветвления для обратной совместимости (объекты ветвей были новыми в TFS 2010) и для поддержки необоснованных слияний (после выполнения безосновательного слияния между двумя путями они будут сохранены с слиянием отношения для будущих слияний.)
Я бы предложил использовать здесь собственную политику регистрации, которая заставляет слияния идти из Dev -> Test или Test -> Dev и не пропускать промежуточный уровень. Это также позволяет вам сохранять Dev как объект ветвления, так что вы все равно получите все приятные функции, такие как визуализация веток.
Я могу привести очень грубый, очень псевдокодийный пример того, что я имел в виду. (Это грубо и потому, что я ленивый, и потому, что я провожу свое время в Java SDK, а не .NET SDK, и я понимаю, что большинству людей нужна политика регистрации .NET. Но в основном потому, что я ленив.)
/*
* Create a dictionary of merge targets to allowable merge sources.
* For an item in this list, the allowable merge sources are a whitelist.
*/
private readonly Dictionary<String, List<String>> restrictedMergeTargets =
new Dictionary<String, List<String>>();
public static MergeWhitelistPolicy
{
/* Only allowed merges to $/Main from $/Test. */
List<String> mainWhitelist = new List<String>();
mainWhitelist.add("$/Test");
allowedMerges.put("$/Main", mainWhitelist);
/* Only allow merges to $/Test from $/Dev. */
List<String> testWhitelist = new List<String>();
testWhitelist.add("$/Dev");
allowedMerges.put("$/Test", testWhitelist);
}
public PolicyFailure[] evaluate(PolicyContext context)
{
PendingChange[] pendingChanges = GetPendingCheckin().GetCheckedPendingChanges();
foreach(PendingChange change : pendingChanges)
{
if(! change.IsMerge())
{
continue;
}
foreach(KeyValuePair<String, List<String>> restrictedTarget : restrictedMergeTargets)
{
if(VersionControlPath.IsChild(restrictedTarget.GetKey(), change.GetServerItem())
{
/* Whitelisted merge path - investigate. */
foreach(String allowedSource : restrictedTarget.GetValue())
{
foreach(MergeSource mergeSource : change.GetMergeSources())
{
if(! VersionControlPath.IsChild(allowedSource, mergeSource.GetServerItem()))
{
return new PolicyFailure("Merge from " +
mergeSource.GetServerItem() + " to " +
change.GetServerItem() + " is disallowed.");
}
}
}
}
}
}
return null;
}
Есть несколько проблем с этим, конечно. Вы, конечно, не захотите жестко закодировать список допустимых отношений слияния в политику - вы можете перенести его в файл конфигурации или запросить отношения слияния на сервере и кэшировать их, если у вас есть определенные правила, как могут делать только прямые потомки. слияния. (Это важно для кэширования, поскольку оценка политики регистрации запускается часто и, как ожидается, будет быстрой. Иногда она может даже выполняться в потоке пользовательского интерфейса (хотя я сомневаюсь в этом), поэтому ваш пробег может отличаться.)
Кроме того, мой код тестирования пути довольно неаккуратный, в основном просто для экономии места в моем комментарии. (А также вышеупомянутая лень с моей стороны.)
Надеюсь, это хорошее начало.