TFS 2010: Как создать список изменений (то есть список рабочих элементов) между двумя версиями приложения с использованием меток?
Я ищу способ автоматического создания журнала изменений (фактически списка рабочих элементов) между двумя версиями моего приложения. У меня две версии моего приложения: v1 и v2, каждый из которых идентифицируется меткой в TFS 2010 (LABEL1 и LABEL2), которую я создал вручную, прежде чем создавать настройки моего приложения.
У меня есть ветвящаяся система, что означает, что у меня есть багажник, большинство исправлений исправлено, и ветвь, где исправления применяются в основном с использованием слияний с магистралью (но также есть некоторые исправления только для ветки, которые не относятся к магистрали), Две версии моего приложения (v1 и v2) - это версии из ветки.
Я бы хотел, чтобы TFS 2010 смог вернуть список ошибок, которые были исправлены (т.е. список рабочих элементов с типом = ошибка, которые закрыты и проверены) между этими двумя метками.
Я пытался добиться этого с помощью веб-интерфейса TFS 2010 или с помощью Visual Studio, но я не нашел никакого способа.
Затем я попытался спросить tf.exe для истории, используя следующую командную строку:
tf history /server:http://server_url/collection_name "$/project_path" /version:LLABEL1~LLABEL2 /recursive /noprompt /format:brief
где LABEL1 - это метка, связанная с исходным кодом v1 приложения, и LABEL2 - метка, которая была связана с исходным кодом v2 приложения.
Фактически это происходит не двумя способами:
- командная строка возвращает список списков изменений, а не список связанных закрытых рабочих элементов
- список наборов изменений содержит только набор изменений, которые я применил к самой ветке, а не к наборам изменений, которые я также применял, и к соединительной линии, а затем присоединился к ветке. Установка или отсутствие параметра "/slotmode" ничего не меняет.
Там я попытался написать кусок кода С#, чтобы получить список рабочих элементов (а не список наборов изменений):
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://server_url/collection_name"));
VersionControlServer controlServer = tfs.GetService<VersionControlServer>();
VersionControlServer vcs = tfs.GetService<VersionControlServer>();
VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null);
var changesets = vcs.QueryHistory(
"$/project_path",
sTo,
0,
RecursionType.Full,
null,
sFrom,
sTo,
int.MaxValue,
true,
false); // Slotmode to false
Dictionary<int, WorkItem> dico = new Dictionary<int, WorkItem>();
foreach (Changeset set in changesets)
{
foreach (WorkItem zz in set.WorkItems)
{
if (!dico.ContainsKey(zz.Id))
{
dico.Add(zz.Id, zz);
}
}
}
foreach (KeyValuePair<int, WorkItem> pair in dico.OrderBy(z => z.Key))
{
Console.WriteLine(string.Format("ID: {0}, Title: {1}", pair.Key, pair.Value.Title));
}
Это действительно работает, я получаю список рабочих элементов между двумя моими ярлыками, которые на самом деле я хотел. Но учитываются только рабочие элементы, связанные с наборами изменений, которые были зафиксированы на самой ветке: рабочие элементы типа "ошибка", которые были решены на соединительной линии, затем объединены с ветвью, не отображаются. Slotmode ничего не меняет.
Затем я, наконец, попытался заменить VersionSpecs, которые были определены меткой с VersionSpecs, которые определены наборами изменений:
VersionSpec sFrom = VersionSpec.ParseSingleSpec("C5083", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("C5276", null);
И мой код, наконец, работает.
Итак, мой вопрос: как я мог получить тот же результат с ярлыками, которые являются объектами TFS, которые я использую для идентификации версии? Если это невозможно, как определить версию в TFS 2010?
спасибо.
Btw Я нашел несколько вопросов о stackoverflow, но ни один из них не дал мне ответов с метками. Например:
Пример вопроса
Ответы
Ответ 1
Думаю, http://tfschangelog.codeplex.com/ может помочь вам здесь.
TFS ChangeLog applatoin позволяет пользователям автоматически создавать заметки о выпуске из TFS. Пользователи должны будут предоставить информацию о своем диапазоне проектов, веток и наборов изменений, а затем приложение TFS ChangeLog будет извлекать информацию из каждого набора изменений в заданном диапазоне и всех связанных с ним рабочих элементов для таких наборов изменений. то есть он будет перемещаться с начала набора изменений до окончания набора изменений и будет извлекать данные о каждом наборе изменений вместе с соответствующими рабочими элементами в файле XML.
Пользователи могут затем использовать свою собственную логику преобразования, включая фильтр, сортировку, стилирование, форматирование вывода и т.д. для создания отчета о заметках выпуска.
Еще одна вещь, которую я хотел бы добавить здесь, будет связана с ярлыками в TFS. Ярлыки в основном назначаются/связаны с наборами изменений. В настоящее время приложение TFS ChangeLog не поддерживает ярлыки для определения начальной и конечной точки, но поддерживает набор изменений, который может использоваться как решение для решения проблемы.
Надеюсь, это полезно.
Ответ 2
В общем, абсолютный метод определения точек во времени в любом SCM явно является идентификатором checkin.
Использование меток для абстрагирования этого в TFS не является оптимальным, как обсуждалось здесь и здесь. Лучшим подходом является использование сборок вместо этого, особенно в современной среде CI.
Чтобы получить максимальный набор изменений, содержащийся в данной сборке, вам нужно сделать что-то вроде этого:
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.Build.Client;
using Microsoft.TeamFoundation.Client;
namespace GetChangesetsFromBuild
{
class Program
{
static void Main()
{
TfsTeamProjectCollection tpc = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri("http://TFSServer:8080/Name"));
IBuildServer bs = (IBuildServer)tpc.GetService(typeof(IBuildServer));
IBuildDetail build = bs.GetAllBuildDetails(new Uri("vstfs:///..."));
List<IChangesetSummary> associatedChangesets = InformationNodeConverters.GetAssociatedChangesets(build);
int idMax = associatedChangesets[0].ChangesetId;
}
}
}
Трудность с вышесказанным заключается в том, чтобы извлечь BuildUri из сборных, которые вас интересуют. Чтобы получить эту информацию, вы можете сделать что-то вроде этого:
IBuildDetail[] builds = bs.QueryBuilds("TeamPorjectName", "yourBuildDefinitionName")
а затем извлеките Ури, которые важны для вас.
Это также хороший автомобиль, если вы в конечном итоге настаиваете на использовании меток: кроме Uri
, каждый build[]
имеет также LabelName
.
Ответ 3
Я был в той же ситуации, что и вы. Я также хочу, чтобы в Рабочем элементе включались объединенные дополнения. Я включаю только рабочие элементы, которые выполняются. Также, если один и тот же рабочий элемент связан с несколькими наборами изменений, сообщается только последний набор изменений. Я использую это в настройке CI; и создать журнал изменений для каждой сборки. Затем List<ChangeInfo>
можно экспортировать в файл XML/HTML/TXT. Вот мое решение:
namespace TFSChangelog
{
public class TFSChangelogGenerator
{
private const string workItemDoneText = "Done";
/// <summary>
/// This class describes a change by:
/// Changeset details
/// and
/// WorkItem details
/// </summary>
public class ChangeInfo
{
#region Changeset details
public DateTime ChangesetCreationDate { get; set; }
public int ChangesetId { get; set; }
#endregion
#region WorkItem details
public string WorkItemTitle { get; set; }
public int WorkItemId { get; set; }
#endregion
}
public static List<ChangeInfo> GetChangeinfo(string tfsServer, string serverPath, string from, string to)
{
// Connect to server
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(tfsServer));
tfs.Connect(ConnectOptions.None);
var vcs = tfs.GetService<VersionControlServer>();
// Create versionspec's
VersionSpec versionFrom = null;
if (!string.IsNullOrEmpty(from))
versionFrom = VersionSpec.ParseSingleSpec(from, null);
VersionSpec versionTo = VersionSpec.Latest;
if (!string.IsNullOrEmpty(to))
versionTo = VersionSpec.ParseSingleSpec(to, null);
// Internally used dictionary
var changes = new Dictionary<int, ChangeInfo>();
// Find Changesets that are checked into the branch
var directChangesets = vcs.QueryHistory(
serverPath,
VersionSpec.Latest,
0,
RecursionType.Full,
null,
versionFrom,
versionTo,
Int32.MaxValue,
true,
false
).Cast<Changeset>();
foreach (var changeset in directChangesets)
{
foreach (var workItem in changeset.WorkItems.Where(workItem => workItem.State == workItemDoneText))
{
if (changes.ContainsKey(workItem.Id))
{
if (changeset.ChangesetId < changes[workItem.Id].ChangesetId) continue;
}
changes[workItem.Id] = new ChangeInfo { ChangesetId = changeset.ChangesetId, ChangesetCreationDate = changeset.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title };
}
}
// Find Changesets that are merged into the branch
var items = vcs.GetItems(serverPath, RecursionType.Full);
foreach (var item in items.Items)
{
var changesetMergeDetails = vcs.QueryMergesWithDetails(
null,
null,
0,
item.ServerItem,
VersionSpec.Latest,
0,
versionFrom,
versionTo,
RecursionType.Full
);
foreach (var merge in changesetMergeDetails.Changesets)
{
foreach (var workItem in merge.WorkItems.Where(workItem => workItem.State == workItemDoneText))
{
if (changes.ContainsKey(workItem.Id))
{
if (merge.ChangesetId < changes[workItem.Id].ChangesetId) continue;
}
changes[workItem.Id] = new ChangeInfo { ChangesetId = merge.ChangesetId, ChangesetCreationDate = merge.CreationDate, WorkItemId = workItem.Id, WorkItemTitle = workItem.Title };
}
}
}
// Return a list sorted by ChangesetId
return (from entry in changes orderby entry.Value.ChangesetId descending select entry.Value).ToList();
}
}
}
Ответ 4
Этот вопрос приблизил меня к решению аналогичной проблемы, с которой я столкнулся.
Используйте тип LabelVersionSpec
вместо VersionSpec
для версий этикеток.
Заменить:
VersionSpec sFrom = VersionSpec.ParseSingleSpec("LLABEL1", null);
VersionSpec sTo = VersionSpec.ParseSingleSpec("LLABEL2", null);
с:
LabelVersionSpec sFrom = new LabelVersionSpec("LLABEL1");
LabelVersionSpec sTo = new LabelVersionSpec("LLABEL2");