List changed files in TFS but ordered by number of changes applied - version-control

Is there a way of listing all the files that have changed on a project, but ordered by the number of changes made to each file?
I want to do a code review but only from a selection of the most active files.

You may try to use Excel as a TFS reporting tool like in this blog post:
http://www.woodwardweb.com/vsts/getting_started.html
ps. I found that link in this question.

I searched different ways and finally I found that best way is using TFS API
here is the code :
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
namespace VControl
{
class Program
{
class SourceElement
{
public string filename;
public int numberOfModification;
}
static void Main(string[] args)
{
TfsTeamProjectCollection projectCollection = new
TfsTeamProjectCollection(new Uri("http://server:8080/tfs/ProjectCollection/"),
new System.Net.NetworkCredential("username", "password"));
projectCollection.EnsureAuthenticated();
Workspace workspace = null;
Boolean createdWorkspace = false;
String newFolder = String.Empty;
VersionControlServer versionControl = projectCollection.GetService<VersionControlServer>();
var teamProjects = new List<TeamProject>(versionControl.GetAllTeamProjects(false));
String workspaceName = String.Format("{0}-{1}", Environment.MachineName, "TestWorkspace");
try
{
workspace = versionControl.GetWorkspace(workspaceName, versionControl.AuthorizedUser);
}
catch (WorkspaceNotFoundException)
{
workspace = versionControl.CreateWorkspace(workspaceName, versionControl.AuthorizedUser);
createdWorkspace = true;
}
var serverFolder = String.Format("$/{0}", teamProjects[0].Name) + "/solutionFolder/";
var localFolder = Path.Combine(Path.GetTempPath(), "localFolder") + "/solutionFolder/";
var workingFolder = new WorkingFolder(serverFolder, localFolder);
// Create a workspace mapping.
workspace.CreateMapping(workingFolder);
if (!workspace.HasReadPermission)
{
throw new SecurityException(
String.Format("{0} does not have read permission for {1}",
versionControl.AuthorizedUser, serverFolder));
}
// Get the files from the repository.
workspace.Get();
string[] directories = Directory.GetDirectories(workspace.Folders[0].LocalItem);
FileStream outputFile = new FileStream("result.txt", FileMode.Create);
StreamWriter writer = new StreamWriter(outputFile);
List<SourceElement> fileLiset = new List<SourceElement>();
foreach (string dir in directories)
{
foreach (string file in Directory.GetFiles(dir))
{
string filenamae = System.IO.Path.GetFileName(file);
Item source = versionControl.GetItem(file);
System.Collections.IEnumerable history = versionControl.QueryHistory(file,
VersionSpec.Latest, 0, RecursionType.Full, null, null, null, 300, true, true, false, false);
int numberOfModification = 0;
foreach (var item in history)
numberOfModification++;
SourceElement fileElement = new SourceElement();
fileElement.filename = filenamae;
fileElement.numberOfModification = numberOfModification;
fileLiset.Add(fileElement);
}
}
var sortedList = fileLiset.OrderBy(x=> x.numberOfModification);
// Loop through keys.
foreach (var key in sortedList)
{
writer.WriteLine("{0}: {1}", key.filename, key.numberOfModification);
}
writer.Close();
}
}
}

Related

Open OSM pbf results in Protobuf exception

Using OSMSharp I am having trouble to open a stream for a file (which I can provide on demand)
The error occurs in PBFReader (line 104)
using (var tmp = new LimitedStream(_stream, length))
{
header = _runtimeTypeModel.Deserialize(tmp, null, _blockHeaderType) as BlobHeader;
}
and states: "ProtoBuf.ProtoException: 'Invalid field in source data: 0'" which might mean different things as I have read in this SO question.
The file opens and is visualized with QGis so is not corrupt in my opinion.
Can it be that the contracts do not match? Is OsmSharp/core updated to the latest .proto files for OSM from here (although not sure if this is the real original source for the definition files).
And what might make more sense, can it be that the file I attached is generated for v2 of OSM PBF specification?
In the code at the line of the exception I see the following comment which makes me wonder:
// TODO: remove some of the v1 specific code.
// TODO: this means also to use the built-in capped streams.
// code borrowed from: http://stackoverflow.com/questions/4663298/protobuf-net-deserialize-open-street-maps
// I'm just being lazy and re-using something "close enough" here
// note that v2 has a big-endian option, but Fixed32 assumes little-endian - we
// actually need the other way around (network byte order):
// length = IntLittleEndianToBigEndian((uint)length);
BlobHeader header;
// again, v2 has capped-streams built in, but I'm deliberately
// limiting myself to v1 features
So this makes me wonder if OSM Sharp is (still) up-to-date.
My sandbox code looks like this:
using OsmSharp.Streams;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using OsmSharp.Tags;
namespace OsmSharp
{
class Program
{
private const string Path = #"C:\Users\Bernoulli IT\Documents\Applications\Argaleo\Test\";
private const string FileNameAntarctica = "antarctica-latest.osm";
private const string FileNameOSPbf = "OSPbf";
private const Boolean useRegisterSource = false;
private static KeyValuePair<string, string> KeyValuePair = new KeyValuePair<string, string>("joep", "monita");
static void Main(string[] args)
{
Console.WriteLine("Hello World!");
//string fileName = $#"{Path}\{FileNameAntarctica}.pbf";
string fileName = $#"{Path}\{FileNameOSPbf}.pbf";
string newFileName = $"{fileName.Replace(".pbf", string.Empty)}-{Guid.NewGuid().ToString().Substring(0, 4)}.pbf";
Console.WriteLine("*** Complete");
string fileNameOutput = CompleteFlow(fileName, newFileName);
Console.WriteLine("");
Console.WriteLine("*** Display");
DisplayFlow(fileNameOutput);
Console.ReadLine();
}
private static string CompleteFlow(string fileName, string newFileName)
{
// 1. Open file and convert to bytes
byte[] fileBytes = FileToBytes(fileName);
// 2. Bytes to OSM stream source (pbf)
PBFOsmStreamSource osmStreamSource;
osmStreamSource = BytesToOsmStreamSource(fileBytes);
osmStreamSource.MoveNext();
if (osmStreamSource.Current() == null)
{
osmStreamSource = FileToOsmStreamSource(fileName);
osmStreamSource.MoveNext();
if (osmStreamSource.Current() == null)
{
throw new Exception("No current in stream.");
}
}
// 3. Add custom tag
AddTag(osmStreamSource);
// 4. OSM stream source to bytes
//byte[] osmStreamSourceBytes = OsmStreamSourceToBytes(osmStreamSource);
// 5. Bytes to file
//string fileNameOutput = BytesToFile(osmStreamSourceBytes, newFileName);
OsmStreamSourceToFile(osmStreamSource, newFileName);
Console.WriteLine(newFileName);
return newFileName;
}
private static void DisplayFlow(string fileName)
{
// 1. Open file and convert to bytes
byte[] fileBytes = FileToBytes(fileName);
// 2. Bytes to OSM stream source (pbf)
BytesToOsmStreamSource(fileBytes);
}
private static byte[] FileToBytes(string fileName)
{
Console.WriteLine(fileName);
return File.ReadAllBytes(fileName);
}
private static PBFOsmStreamSource BytesToOsmStreamSource(byte[] bytes)
{
MemoryStream memoryStream = new MemoryStream(bytes);
memoryStream.Position = 0;
PBFOsmStreamSource osmStreamSource = new PBFOsmStreamSource(memoryStream);
foreach (OsmGeo element in osmStreamSource.Where(osmGeo => osmGeo.Tags.Any(tag => tag.Key.StartsWith(KeyValuePair.Key))))
{
foreach (Tag elementTag in element.Tags.Where(tag => tag.Key.StartsWith(KeyValuePair.Key)))
{
Console.WriteLine("!!!!!!!!!!!!!! Tag found while reading !!!!!!!!!!!!!!!!!!".ToUpper());
}
}
return osmStreamSource;
}
private static PBFOsmStreamSource FileToOsmStreamSource(string fileName)
{
using (FileStream fileStream = new FileInfo(fileName).OpenRead())
{
PBFOsmStreamSource osmStreamSource = new PBFOsmStreamSource(fileStream);
return osmStreamSource;
}
}
private static void AddTag(PBFOsmStreamSource osmStreamSource)
{
osmStreamSource.Reset();
OsmGeo osmGeo = null;
while (osmGeo == null)
{
osmStreamSource.MoveNext();
osmGeo = osmStreamSource.Current();
if(osmGeo?.Tags == null)
{
osmGeo = null;
}
}
osmGeo.Tags.Add("joep", "monita");
Console.WriteLine($"{osmGeo.Tags.FirstOrDefault(tag => tag.Key.StartsWith(KeyValuePair.Key)).Key} - {osmGeo.Tags.FirstOrDefault(tag => tag.Key.StartsWith(KeyValuePair.Key)).Value}");
}
private static byte[] OsmStreamSourceToBytes(PBFOsmStreamSource osmStreamSource)
{
MemoryStream memoryStream = new MemoryStream();
PBFOsmStreamTarget target = new PBFOsmStreamTarget(memoryStream, true);
osmStreamSource.Reset();
target.Initialize();
UpdateTarget(osmStreamSource, target);
target.Flush();
target.Close();
return memoryStream.ToArray();
}
private static string BytesToFile(byte[] bytes, string fileName)
{
using (FileStream fs = new FileStream(fileName, FileMode.Create, FileAccess.Write))
{
fs.Write(bytes, 0, bytes.Length);
}
return fileName;
}
private static void OsmStreamSourceToFile(PBFOsmStreamSource osmStreamSource, string fileName)
{
using (FileStream fileStream = new FileInfo(fileName).OpenWrite())
{
PBFOsmStreamTarget target = new PBFOsmStreamTarget(fileStream, true);
osmStreamSource.Reset();
target.Initialize();
UpdateTarget(osmStreamSource, target);
target.Flush();
target.Close();
}
}
private static void UpdateTarget(OsmStreamSource osmStreamSource, OsmStreamTarget osmStreamTarget)
{
if (useRegisterSource)
{
osmStreamTarget.RegisterSource(osmStreamSource, osmGeo => true);
osmStreamTarget.Pull();
}
else
{
bool isFirst = true;
foreach (OsmGeo osmGeo in osmStreamSource)
{
Tag? tag = osmGeo.Tags?.FirstOrDefault(t => t.Key == KeyValuePair.Key);
switch (osmGeo.Type)
{
case OsmGeoType.Node:
if (isFirst)
{
for (int indexer = 0; indexer < 1; indexer++)
{
(osmGeo as Node).Tags.Add(new Tag(KeyValuePair.Key + Guid.NewGuid(), KeyValuePair.Value));
}
isFirst = false;
}
osmStreamTarget.AddNode(osmGeo as Node);
break;
case OsmGeoType.Way:
osmStreamTarget.AddWay(osmGeo as Way);
break;
case OsmGeoType.Relation:
osmStreamTarget.AddRelation(osmGeo as Relation);
break;
default:
throw new ArgumentOutOfRangeException();
}
}
}
}
}
}
Already I posted this question on the GITHube page of OSMSharp as is linked here. Any help would be very appreciated.

Is it possible to rollback a changeset using Microsoft.TeamFoundation.SourceControl.WebApi?

Following code try to rollback a changeset using Microsoft.TeamFoundation.SourceControl.WebApi. But "The specified change type Rollback is not supported." is raised when calling CreateChangesetAsync. Is this a limitation of Microsoft.TeamFoundation.SourceControl.WebApi? Or I did something wrong?
using System;
using System.Collections.Generic;
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
using Microsoft.VisualStudio.Services.WebApi;
namespace RestRollBackInTFS
{
class Program
{
static void Main(string[] args)
{
const String collectionUri = #"https://myTfs.visualstudio.com/";
VssConnection connection = new VssConnection(new Uri(collectionUri),
new VssClientCredentials()
);
TfvcHttpClient tfsClient = connection.GetClient<TfvcHttpClient>();
int changeSetIdToRollBack = 11651;
var changeSet = tfsClient.GetChangesetAsync(changeSetIdToRollBack, maxChangeCount: 10, includeDetails: true).Result;
foreach (var c in changeSet.Changes)
{
c.ChangeType = VersionControlChangeType.Rollback;
}
TfvcChangeset newC = new TfvcChangeset();
newC.Changes = new List<TfvcChange>(changeSet.Changes);
var ret = tfsClient.CreateChangesetAsync(newC).Result;
}
}
}

How to find top 10 files changed most frequently in a period from TFS 2015 Version Control?

My team uses TFS 2015 as an ALM and Version Control system,I want to analyse which files change most frequently.
I found TFS didn't have this functionality out of the box, but TFS2015 has a REST API to query Changesets for files, like this below:
http://{instance}/tfs/DefaultCollection/_apis/tfvc/changesets?searchCriteria.itemPath={filePath}&api-version=1.0
There are thousands of files in my Project Repository, querying it one by one is not a good idea, are there any better solution to this question?
I don't think there's a defacto out of the box solution for your question, I've tried two separate approaches to solve your question, I initially focused on the REST API but later switched to the SOAP API to see what features are supported in it.
In all options below the following api should suffice:
Install the client API link
#NuGet
Install-Package Microsoft.TeamFoundationServer.ExtendedClient -Version 14.89.0 or later
In all options the following extension method is required ref
public static class StringExtensions
{
public static bool ContainsAny(this string source, List<string> lookFor)
{
if (!string.IsNullOrEmpty(source) && lookFor.Count > 0)
{
return lookFor.Any(source.Contains);
}
return false;
}
}
OPTION 1: SOAP API
With the SOAP API one is not explicitly required to limit the number of query results using the maxCount parameter as described in this excerpt of QueryHistory method's IntelliSense documentation:
maxCount: This parameter allows the caller to limit the number of
results returned. QueryHistory pages results back from the server on
demand, so limiting your own consumption of the returned IEnumerable
is almost as effective (from a performance perspective) as providing a
fixed value here. The most common value to provide for this parameter
is Int32.MaxValue.
Based on the maxCount documentation I made a decision to extract statistics for each of the products in my source control system since it may be of great value to see how much code flux there is for each system in the codebase independent of each other instead of limiting to 10 files across the entire codebase which could contain hundreds of systems.
C# REST and SOAP (ExtendedClient) api reference
Install the SOAP API Client link
#NuGet
Install-Package Microsoft.TeamFoundationServer.ExtendedClient -Version 14.95.2
limiting criteria are: Only scan specific paths in source
control since some systems in source control are older and possibly only there for historic purposes.
only certain file extensions included e.g. .cs, .js
certain filenames excluded e.g. AssemblyInfo.cs.
items extracted for each path: 10
from date: 120 days ago
to date: today
exclude specific paths e.g. folders containing release branches or
archived branches
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
public void GetTopChangedFilesSoapApi()
{
var tfsUrl = "https://<SERVERNAME>/tfs/<COLLECTION>";
var domain = "<DOMAIN>";
var password = "<PASSWORD>";
var userName = "<USERNAME>";
//Only interested in specific systems so will scan only these
var directoriesToScan = new List<string> {
"$/projectdir/subdir/subdir/subdirA/systemnameA",
"$/projectdir/subdir/subdir/subdirB/systemnameB",
"$/projectdir/subdir/subdir/subdirC/systemnameC",
"$/projectdir/subdir/subdir/subdirD/systemnameD"
};
var maxResultsPerPath = 10;
var fromDate = DateTime.Now.AddDays(-120);
var toDate = DateTime.Now;
var fileExtensionToInclude = new List<string> { ".cs", ".js" };
var extensionExclusions = new List<string> { ".csproj", ".json", ".css" };
var fileExclusions = new List<string> { "AssemblyInfo.cs", "jquery-1.12.3.min.js", "config.js" };
var pathExclusions = new List<string> {
"/subdirToForceExclude1/",
"/subdirToForceExclude2/",
"/subdirToForceExclude3/",
};
using (var collection = new TfsTeamProjectCollection(new Uri(tfsUrl),
new NetworkCredential(userName: userName, password: password, domain: domain)))
{
collection.EnsureAuthenticated();
var tfvc = collection.GetService(typeof(VersionControlServer)) as VersionControlServer;
foreach (var rootDirectory in directoriesToScan)
{
//Get changesets
//Note: maxcount set to maxvalue since impact to server is minimized by linq query below
var changeSets = tfvc.QueryHistory(path: rootDirectory, version: VersionSpec.Latest,
deletionId: 0, recursion: RecursionType.Full, user: null,
versionFrom: new DateVersionSpec(fromDate), versionTo: new DateVersionSpec(toDate),
maxCount: int.MaxValue, includeChanges: true,
includeDownloadInfo: false, slotMode: true)
as IEnumerable<Changeset>;
//Filter changes contained in changesets
var changes = changeSets.SelectMany(a => a.Changes)
.Where(a => a.ChangeType != ChangeType.Lock || a.ChangeType != ChangeType.Delete || a.ChangeType != ChangeType.Property)
.Where(e => !e.Item.ServerItem.ContainsAny(pathExclusions))
.Where(e => !e.Item.ServerItem.Substring(e.Item.ServerItem.LastIndexOf('/') + 1).ContainsAny(fileExclusions))
.Where(e => !e.Item.ServerItem.Substring(e.Item.ServerItem.LastIndexOf('.')).ContainsAny(extensionExclusions))
.Where(e => e.Item.ServerItem.Substring(e.Item.ServerItem.LastIndexOf('.')).ContainsAny(fileExtensionToInclude))
.GroupBy(g => g.Item.ServerItem)
.Select(d => new { File=d.Key, Count=d.Count()})
.OrderByDescending(o => o.Count)
.Take(maxResultsPerPath);
//Write top items for each path to the console
Console.WriteLine(rootDirectory); Console.WriteLine("->");
foreach (var change in changes)
{
Console.WriteLine("ChangeCount: {0} : File: {1}", change.Count, change.File);
}
Console.WriteLine(Environment.NewLine);
}
}
}
OPTION 2A: REST API
(!! problem identified by OP led to finding a critical defect in v.xxx-14.95.4 of api) - OPTION 2B is the workaround
defect discovered in v.xxx to 14.95.4 of api: The TfvcChangesetSearchCriteria type contains an ItemPath property
which is supposed to limit the search to a specified directory. The
default value of this property is $/, unfortunately when used
GetChangesetsAsync will always use the root path of the tfvc source repository irrespective of the value set.
That said, this will still be a reasonable approach if the defect were to be fixed.
One way to limit the impact to your scm system would be to specify limiting criteria for the query/s using the TfvcChangesetSearchCriteria Type parameter of the GetChangesetsAsync member in the TfvcHttpClient type.
You do not particularly need to check each file in your scm system/project individually, checking the changesets for the specified period may be enough. Not all of the limiting values I used below are properties of the TfvcChangesetSearchCriteria type though so I've written a short example to show how I would do it i.e.
you can specify the maximum number of changesets to consider initially and the specific project you want to look at.
Note: The TheTfvcChangesetSearchCriteria type contains some additional properties that you may want to consider using.
In the example below I've used the REST API in a C# client and getting results from tfvc.
If your intention is to use a different client language and invoke the REST services directly e.g. JavaScript; the logic below should still give you some pointers.
//targeted framework for example: 4.5.2
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
public async Task GetTopChangedFilesUsingRestApi()
{
var tfsUrl = "https://<SERVERNAME>/tfs/<COLLECTION>";
var domain = "<DOMAIN>";
var password = "<PASSWORD>";
var userName = "<USERNAME>";
//Criteria used to limit results
var directoriesToScan = new List<string> {
"$/projectdir/subdir/subdir/subdirA/systemnameA",
"$/projectdir/subdir/subdir/subdirB/systemnameB",
"$/projectdir/subdir/subdir/subdirC/systemnameC",
"$/projectdir/subdir/subdir/subdirD/systemnameD"
};
var maxResultsPerPath = 10;
var fromDate = DateTime.Now.AddDays(-120);
var toDate = DateTime.Now;
var fileExtensionToInclude = new List<string> { ".cs", ".js" };
var folderPathsToInclude = new List<string> { "/subdirToForceInclude/" };
var extensionExclusions = new List<string> { ".csproj", ".json", ".css" };
var fileExclusions = new List<string> { "AssemblyInfo.cs", "jquery-1.12.3.min.js", "config.js" };
var pathExclusions = new List<string> {
"/subdirToForceExclude1/",
"/subdirToForceExclude2/",
"/subdirToForceExclude3/",
};
//Establish connection
VssConnection connection = new VssConnection(new Uri(tfsUrl),
new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential(userName, password, domain))));
//Get tfvc client
var tfvcClient = await connection.GetClientAsync<TfvcHttpClient>();
foreach (var rootDirectory in directoriesToScan)
{
//Set up date-range criteria for query
var criteria = new TfvcChangesetSearchCriteria();
criteria.FromDate = fromDate.ToShortDateString();
criteria.ToDate = toDate.ToShortDateString();
criteria.ItemPath = rootDirectory;
//get change sets
var changeSets = await tfvcClient.GetChangesetsAsync(
maxChangeCount: int.MaxValue,
includeDetails: false,
includeWorkItems: false,
searchCriteria: criteria);
if (changeSets.Any())
{
var sample = new List<TfvcChange>();
Parallel.ForEach(changeSets, changeSet =>
{
sample.AddRange(tfvcClient.GetChangesetChangesAsync(changeSet.ChangesetId).Result);
});
//Filter changes contained in changesets
var changes = sample.Where(a => a.ChangeType != VersionControlChangeType.Lock || a.ChangeType != VersionControlChangeType.Delete || a.ChangeType != VersionControlChangeType.Property)
.Where(e => e.Item.Path.ContainsAny(folderPathsToInclude))
.Where(e => !e.Item.Path.ContainsAny(pathExclusions))
.Where(e => !e.Item.Path.Substring(e.Item.Path.LastIndexOf('/') + 1).ContainsAny(fileExclusions))
.Where(e => !e.Item.Path.Substring(e.Item.Path.LastIndexOf('.')).ContainsAny(extensionExclusions))
.Where(e => e.Item.Path.Substring(e.Item.Path.LastIndexOf('.')).ContainsAny(fileExtensionToInclude))
.GroupBy(g => g.Item.Path)
.Select(d => new { File = d.Key, Count = d.Count() })
.OrderByDescending(o => o.Count)
.Take(maxResultsPerPath);
//Write top items for each path to the console
Console.WriteLine(rootDirectory); Console.WriteLine("->");
foreach (var change in changes)
{
Console.WriteLine("ChangeCount: {0} : File: {1}", change.Count, change.File);
}
Console.WriteLine(Environment.NewLine);
}
}
}
OPTION 2B
Note: This solution is very similar to OPTION 2A with the exception of a workaround implemented to fix a limitation in the REST client API library at time of writing.
Brief summary - instead of invoking client api library to get changesets this example uses a web request direct to the REST API to fetch changesets, thus additional types were needed to be defined to handle the response from the service.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.TeamFoundation.SourceControl.WebApi;
using Microsoft.VisualStudio.Services.Client;
using Microsoft.VisualStudio.Services.Common;
using System.Text;
using System.IO;
using Newtonsoft.Json;
public async Task GetTopChangedFilesUsingDirectWebRestApiSO()
{
var tfsUrl = "https://<SERVERNAME>/tfs/<COLLECTION>";
var domain = "<DOMAIN>";
var password = "<PASSWORD>";
var userName = "<USERNAME>";
var changesetsUrl = "{0}/_apis/tfvc/changesets?searchCriteria.itemPath={1}&searchCriteria.fromDate={2}&searchCriteria.toDate={3}&$top={4}&api-version=1.0";
//Criteria used to limit results
var directoriesToScan = new List<string> {
"$/projectdir/subdir/subdir/subdirA/systemnameA",
"$/projectdir/subdir/subdir/subdirB/systemnameB",
"$/projectdir/subdir/subdir/subdirC/systemnameC",
"$/projectdir/subdir/subdir/subdirD/systemnameD"
};
var maxResultsPerPath = 10;
var fromDate = DateTime.Now.AddDays(-120);
var toDate = DateTime.Now;
var fileExtensionToInclude = new List<string> { ".cs", ".js" };
var folderPathsToInclude = new List<string> { "/subdirToForceInclude/" };
var extensionExclusions = new List<string> { ".csproj", ".json", ".css" };
var fileExclusions = new List<string> { "AssemblyInfo.cs", "jquery-1.12.3.min.js", "config.js" };
var pathExclusions = new List<string> {
"/subdirToForceExclude1/",
"/subdirToForceExclude2/",
"/subdirToForceExclude3/",
};
//Get tfvc client
//Establish connection
VssConnection connection = new VssConnection(new Uri(tfsUrl),
new VssCredentials(new Microsoft.VisualStudio.Services.Common.WindowsCredential(new NetworkCredential(userName, password, domain))));
//Get tfvc client
var tfvcClient = await connection.GetClientAsync<TfvcHttpClient>();
foreach (var rootDirectory in directoriesToScan)
{
var changeSets = Invoke<GetChangeSetsResponse>("GET", string.Format(changesetsUrl, tfsUrl, rootDirectory,fromDate,toDate,maxResultsPerPath), userName, password, domain).value;
if (changeSets.Any())
{
//Get changes
var sample = new List<TfvcChange>();
foreach (var changeSet in changeSets)
{
sample.AddRange(tfvcClient.GetChangesetChangesAsync(changeSet.changesetId).Result);
}
//Filter changes
var changes = sample.Where(a => a.ChangeType != VersionControlChangeType.Lock || a.ChangeType != VersionControlChangeType.Delete || a.ChangeType != VersionControlChangeType.Property)
.Where(e => e.Item.Path.ContainsAny(folderPathsToInclude))
.Where(e => !e.Item.Path.ContainsAny(pathExclusions))
.Where(e => !e.Item.Path.Substring(e.Item.Path.LastIndexOf('/') + 1).ContainsAny(fileExclusions))
.Where(e => !e.Item.Path.Substring(e.Item.Path.LastIndexOf('.')).ContainsAny(extensionExclusions))
.Where(e => e.Item.Path.Substring(e.Item.Path.LastIndexOf('.')).ContainsAny(fileExtensionToInclude))
.GroupBy(g => g.Item.Path)
.Select(d => new { File = d.Key, Count = d.Count() })
.OrderByDescending(o => o.Count)
.Take(maxResultsPerPath);
//Write top items for each path to the console
Console.WriteLine(rootDirectory); Console.WriteLine("->");
foreach (var change in changes)
{
Console.WriteLine("ChangeCount: {0} : File: {1}", change.Count, change.File);
}
Console.WriteLine(Environment.NewLine);
}
}
}
private T Invoke<T>(string method, string url, string userName, string password, string domain)
{
var request = WebRequest.Create(url);
var httpRequest = request as HttpWebRequest;
if (httpRequest != null) httpRequest.UserAgent = "versionhistoryApp";
request.ContentType = "application/json";
request.Method = method;
request.Credentials = new NetworkCredential(userName, password, domain); //ntlm 401 challenge support
request.Headers[HttpRequestHeader.Authorization] = "Basic " + Convert.ToBase64String(Encoding.UTF8.GetBytes(domain+"\\"+userName + ":" + password)); //basic auth support if enabled on tfs instance
try
{
using (var response = request.GetResponse())
using (var responseStream = response.GetResponseStream())
using (var reader = new StreamReader(responseStream))
{
string s = reader.ReadToEnd();
return Deserialize<T>(s);
}
}
catch (WebException ex)
{
if (ex.Response == null)
throw;
using (var responseStream = ex.Response.GetResponseStream())
{
string message;
try
{
message = new StreamReader(responseStream).ReadToEnd();
}
catch
{
throw ex;
}
throw new Exception(message, ex);
}
}
}
public class GetChangeSetsResponse
{
public IEnumerable<Changeset> value { get; set; }
public class Changeset
{
public int changesetId { get; set; }
public string url { get; set; }
public DateTime createdDate { get; set; }
public string comment { get; set; }
}
}
public static T Deserialize<T>(string json)
{
T data = JsonConvert.DeserializeObject<T>(json);
return data;
}
}
Additional References:
C# REST and SOAP (ExtendedClient) api reference
REST API: tfvc Changesets
TfvcChangesetSearchCriteria type #MSDN

Update VSO history and add attachments

How do I add history records and attachments to a workitem in VSO 2015?
So far I have succeeded in logging in, running a query and retrieve the workitems from the query result.
But the workitem objects doesn't have properties for history or attatchments:
WorkItemTrackingHttpClient witClient = new WorkItemTrackingHttpClient(new Uri(collectionUri), new VssAadCredential("first.last#blablabla.com", "secret"));
List<QueryHierarchyItem> items = witClient.GetQueriesAsync(teamProjectName, QueryExpand.All,2).Result;
QueryHierarchyItem myQueriesFolder = items.FirstOrDefault(qhi => qhi.Name.Equals("Shared Queries"));
if (myQueriesFolder != null)
{
string queryName = "All Workitems";
QueryHierarchyItem newBugsQuery = null;
if (myQueriesFolder.Children != null)
{
newBugsQuery = myQueriesFolder.Children.FirstOrDefault(qhi => qhi.Name.Equals(queryName));
}
WorkItemQueryResult result = witClient.QueryByIdAsync(newBugsQuery.Id).Result;
if (result.WorkItemRelations.Count() > 0)
{
int skip = 0;
const int batchSize = 100;
IEnumerable<WorkItemLink> workItemRefs;
do
{
workItemRefs = result.WorkItemRelations.Skip(skip).Take(batchSize);
if (workItemRefs.Count() > 0)
{
// get details for each work item in the batch
List<WorkItem> workItems = witClient.GetWorkItemsAsync(workItemRefs.Select(wir => wir.Target.Id)).Result;
foreach (WorkItem workItem in workItems)
{
// write work item to console
if ((string)workItem.Fields["System.WorkItemType"] == "Requirement")
{
Console.WriteLine("{0} {1}", workItem.Id, workItem.Fields["System.Title"]);
//workItem doesn't have properties for history or attachmens...
WorkItemHistory history = (WorkItemHistory) workItem.Fields["System.History"];
}
}
}
skip += batchSize;
}
while (workItemRefs.Count() == batchSize);
}
}
In order to retrieve the history you need to loop through the revisions of a work item (WorkItem.Revisions) and output the history field for each revision.
You can't use WorkItem.Revision and instead should use WorkItem.Fields["System.History"].value to retrieve it.
Attachments are in WorkItem.Attachments for both read and write.
Using namespace:
Microsoft.TeamFoundation.Client
Microsoft.TeamFoundation.WorkItemTracking
Refer to following code for details:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Net;
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.WorkItemTracking.Client;
namespace TFSAPI
{
class Program
{
static void Main(string[] args)
{
string project = "https://xxxxxxxx.visualstudio.com/defaultcollection";
NetworkCredential nc = new NetworkCredential("username","pwd");
BasicAuthCredential cred = new BasicAuthCredential(nc);
TfsClientCredentials tc = new TfsClientCredentials(cred);
tc.AllowInteractive = false;
TfsTeamProjectCollection tpc = new TfsTeamProjectCollection(new Uri(project), tc);
WorkItemStore workItemStore = tpc.GetService<WorkItemStore>();
Project teamProject = workItemStore.Projects["Sonar"];
WorkItemType workItemType = teamProject.WorkItemTypes["User Story"];
int workitemid = 2;
WorkItem wi = workItemStore.GetWorkItem(workitemid);
//Update history
wi.Fields["History"].Value = "You comments";
//Add attachment
wi.Attachments.Add(new Attachment("E:\\a\\1.txt"));
wi.Save();
}
}
}

how to execute the .cs file generated using codeDom

Here is my source code.....
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Reflection;
using System.IO;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
namespace CodeDOM
{
class Program
{
private string m_strFileName;
private string m_Suffix = ".cs";
public Program(string FileName)
{
m_strFileName = FileName;
}
public void CreateCodeDom()
{
Stream codeFile = File.Open("c:\\" + m_strFileName + m_Suffix, FileMode.Create);
StreamWriter sw = new StreamWriter(codeFile);
CSharpCodeProvider cscp = new CSharpCodeProvider();
ICodeGenerator codeGenerator = cscp.CreateGenerator(sw);
CodeGeneratorOptions cgo = new CodeGeneratorOptions();
CodeSnippetCompileUnit cscu = new CodeSnippetCompileUnit("imports System");
codeGenerator.GenerateCodeFromCompileUnit(cscu, sw, cgo);
CodeNamespace cnsCodeDom = new CodeNamespace("SampleNamespace");
CodeTypeDeclaration clsDecl = new CodeTypeDeclaration();
clsDecl.Name = "Sample";
clsDecl.IsClass = true;
clsDecl.TypeAttributes = TypeAttributes.Public;
cnsCodeDom.Types.Add(clsDecl);
CodeConstructor clsConstructor = new CodeConstructor();
clsConstructor.Attributes = MemberAttributes.Public;
clsDecl.Members.Add(clsConstructor);
CodeMemberField clsMember = new CodeMemberField();
clsMember.Name = "myname";
clsMember.Attributes = MemberAttributes.Private;
clsMember.Type = new CodeTypeReference("System.String");
clsDecl.Members.Add(clsMember);
CodeMemberProperty property = new CodeMemberProperty();
property.Name = "MyName";
property.Type = new CodeTypeReference("System.String");
property.Attributes = MemberAttributes.Public;
property.HasGet = true;
property.GetStatements.Add(new CodeSnippetExpression("return this.myname"));
property.HasSet = true;
property.SetStatements.Add(new CodeSnippetExpression("this.myname=value"));
//property.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "this.myname")));
//property.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "this.myname"), new CodePropertySetValueReferenceExpression()));
clsDecl.Members.Add(property);
CodeMemberMethod mtd = new CodeMemberMethod();
mtd.Name = "Print";
mtd.ReturnType = null;
mtd.Attributes = MemberAttributes.Public;
mtd.Statements.Add(new CodeSnippetStatement("Console.WriteLine(myname)"));
clsDecl.Members.Add(mtd);
codeGenerator.GenerateCodeFromNamespace(cnsCodeDom, sw, cgo);
sw.Close();
codeFile.Close();
}
static void Main(string[] args)
{
Program m_obj = new Program("Sample");
m_obj.CreateCodeDom();
Console.ReadLine();
}
}
}
from this i got sample.cs file.now i want to execute that sample.cs file.wil u plz suggest me how to do it?
If it is an application, in a single source file, you can simply do:
csc sample.cs && sample.exe