I have a bunch of source control folders for which I want to get rid of all items that are no longer required. These items have been deleted (code has been moved or rewritten) and, because most of us use the 'Show Deleted Items' option by default some of these folders now show more deleted items and folders and legitimate items. I want to make sure all this redundant code is gone, forever - as it most definitely will not ever be required. These are new projects being built from branches of older ones that, as yet, nobody is using.
There's quite a lot of files spread across multiple folders, though, so I'd rather avoid having to do each one individually. I'm at the command-line too, not using the API.
I know ultimately I will need the tf destroy command.
I also know that tf dir [wildcard] /recursive /deleted will return all deleted items within a path (unfortunately alongside all legitimate items).
Can anyone think of a good way of doing this quickly?
I've thought of two solutions:
1) Take the output of the dir command and find all the items that have :Xnnnnnnn after - these are the deleted items; then simply spit out a bunch of tf destroy calls, or construct a response file (not sure about this bit though). This sounds like a potential use for Powershell, but haven't actually done anything with that yet...
2) Get all the projects ready, and then simply destroy them from TFS and then re-add them so that only the required stuff is then in TFS. However, this does remove the branch relationship which could be useful because for a while I'm going to have to maintain two versions of some of these libraries (pre and post upgrade). Not ideal, but nothing I can do about it.
Obviously Option 2 is a cheat but it'll work - I'd just ideally like a reusable script that could be used for any folder in TFS in the future (a couple of other teams have other long-lived projects that could do with a full purge!).
Thanks in advance.
Okay so I wrote a console app (.Net 4):
IT GOES WITHOUT SAYING I OFFER NO WARRANTY ABOUT THIS - IT WILL DESTROY ITEMS IN TFS!!!!
Update (8th May 2012) If you run this on a folder that has masses and masses (I mean thousands or tens-of-thousands) of deleted items it might not complete before the TFS command-line times out. The majority of the time taken by this command is in generating the .tfc script. If you run it and find this happening, try to targetting some child folders first
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.IO;
using System.Text.RegularExpressions;
using System.Diagnostics;
namespace TFDestroyDeleted
{
class Program
{
static void Main(string[] args)
{
if (args.Length < 1 || args.Length > 3)
Usage();
bool prepareOnly = false;
bool previewOnly = false;
if (args.Any(s => StringComparer.InvariantCultureIgnoreCase
.Compare(s, "preview") == 0)) previewOnly = true;
if (args.Any(s => StringComparer.InvariantCultureIgnoreCase
.Compare(s, "norun") == 0)) prepareOnly = true;
string tfOutput = null;
Process p = new Process();
p.StartInfo = new ProcessStartInfo("tf")
{
Arguments = string.Format
("dir /recursive /deleted \"{0}\"", args[0]),
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
RedirectStandardInput = true
};
p.Start();
tfOutput = p.StandardOutput.ReadToEnd();
p.WaitForExit();
string basePath = null;
string nextDelete = null;
List<string> toDelete = new List<string>();
using (var ms =
new MemoryStream(Encoding.Default.GetBytes(tfOutput)))
{
using (StreamReader sr = new StreamReader(ms))
{
while (!sr.EndOfStream)
{
nextDelete = null;
string line = sr.ReadLine();
if (string.IsNullOrWhiteSpace(line))
basePath = null;
else
{
if (basePath == null)
{
if (line.EndsWith(":"))
basePath = line.Substring(0, line.Length - 1);
else
continue;
}
else
{
nextDelete = Regex.Match(line, #"^.*?;X[0-9]+").Value;
if (!string.IsNullOrWhiteSpace(nextDelete))
{
toDelete.Add(
string.Format
(
"{0}/{1}", basePath,
nextDelete.StartsWith("$") ? nextDelete.Substring(1)
: nextDelete
));
}
}
}
}
}
}
using (var fs = File.OpenWrite("destroy.tfc"))
{
fs.SetLength(0);
using (var sw = new StreamWriter(fs))
{
//do the longest items first, naturally deleting items before their
//parent folders
foreach (var s in toDelete.OrderByDescending(s => s.Length))
{
if (!previewOnly)
sw.WriteLine("destroy \"{0}\" /i", s);
else
sw.WriteLine("destroy \"{0}\" /i /preview", s);
}
sw.Flush();
}
}
if (!prepareOnly)
{
p.StartInfo = new ProcessStartInfo("tf")
{
Arguments = string.Format("#{0}", "destroy.tfc"),
UseShellExecute = false
};
p.Start();
p.WaitForExit();
}
p.Close();
}
static void Usage()
{
Console.WriteLine(#"Usage:
TFDestroyDeleted [TFFolder] (preview) (norun)
Where [TFFolder] is the TFS root folder to be purged - it should be quoted if there are spaces. E.g: ""$/folder/subfolder"".
norun - Specify this if you only want a command file prepared for tf.
preview - Specify this if you want each destroy to be only a preview (i.e. when run, it won't actually do the destroy) ");
Environment.Exit(0);
}
}
}
You must pass the TFS folder to be deleted e.g '$/folder'. If you just pass that, then all matching deleted items will be detected and destroyed, one by one.
For some reason - if you accidentally pass a folder that doesn't actually exist then the operation takes forever. A CTRL+C will stop it, of course.
The app does a recursive dir on the folder, with the /deleted switch.
It then runs through each line in the output, looking for the delete hint, i.e. items with ;Xnnnnnnn. If found, it adds the full tfs path for that item to a list.
After complete, the list is sorted by length in descending order and the contents written out to a tfc response file for the tf.exe command line.
If the preview option is specified then the tf commands are written out with the /preview switch (see TFS Destroy on MSDN)
Then the deletions aren't actually performed.
Finally, you can specify norun which causes the tfc file to be created, but not actually run.
I know it is an old question but I think it can be helpful.
We have an old collection with 20+ team projects under VSO and really needed to clean up our team projects. This code worked perfectly for us.
using Microsoft.TeamFoundation.Client;
using Microsoft.TeamFoundation.VersionControl.Client;
static void Main(string[] args)
{
TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("COLLECTION_URL")); //Example: https://xxxx.visualstudio.com
var versionControl = tfs.GetService<VersionControlServer>();
ItemSpec spec = new ItemSpec("$/", RecursionType.Full);
var folderItemSet = versionControl.GetItems(spec, VersionSpec.Latest, DeletedState.Deleted, ItemType.Folder, true);
DestoryItemSet(versionControl, folderItemSet);
//Delete remaining files
var fileItemSet = versionControl.GetItems(spec, VersionSpec.Latest, DeletedState.Deleted, ItemType.File, true);
DestoryItemSet(versionControl, fileItemSet);
}
private static void DestoryItemSet(VersionControlServer versionControl, ItemSet itemSet)
{
foreach (var deletedItem in itemSet.Items)
{
try
{
versionControl.Destroy(new ItemSpec(deletedItem.ServerItem, RecursionType.Full, deletedItem.DeletionId), VersionSpec.Latest, null, Microsoft.TeamFoundation.VersionControl.Common.DestroyFlags.None);
Console.WriteLine("{0} destroyed successfully.", deletedItem.ServerItem);
}
catch (ItemNotFoundException) //For get rid of exception for deleting the nested objects
{
}
catch (Exception)
{
throw;
}
}
}
I used Microsoft.TeamFoundationServer.ExtendedClient NuGet package.
Related
I try to get the Full Path of a File. ie. calc
Input: calc
Expected output: C:\WINDOWS\system32\calc.exe
I could find out how to do it with PowerShell:
(Get-Command calc).Source
Or with CommandLine:
where.exe calc
But unfortunately I can not get it done with C#.
The documentation for Get-Command says:
Get-Command * gets all types of commands, including all of the non-PowerShell files in the Path environment variable ($env:Path), which it lists in the Application command type.
So we will need to get the Path environment variable and iterate over the directories it lists, looking for files with extensions that indicate the file is a program, for example "*.com" and "*.exe".
The problem with the Path environment variable is that it can become polluted with non-existent directories, so we will have to check for those.
The case of the filename and extension don't matter, so case-insensitive comparisons need to be made.
static void ShowPath(string progName)
{
var extensions = new List<string> { ".com", ".exe" };
string envPath = Environment.GetEnvironmentVariable("Path");
var dirs = envPath.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries);
foreach (string d in dirs.Where(f => Directory.Exists(f)))
{
foreach (var f in (Directory.EnumerateFiles(d).
Where(thisFile => extensions.Any(h => Path.GetExtension(thisFile).Equals(h, StringComparison.InvariantCultureIgnoreCase)))))
{
if (Path.GetFileNameWithoutExtension(f).Equals(progName, StringComparison.InvariantCultureIgnoreCase))
{
Console.WriteLine(f);
return;
}
}
}
Console.WriteLine("Not found.");
}
static void Main(string[] args)
{
ShowPath("calc");
Console.ReadLine();
}
Output:
C:\WINDOWS\system32\calc.exe
There is always the possibility that the current user does not have permission to list the files from somewhere in the path, so checks should be added for that. Also, you might want to use StringComparison.CurrentCultureIgnoreCase for the comparison.
You can get the Pathenvironment variable, split it with ; as delimiter and loop over that result. Then, check if the file path + #"\" + name + ".exe" exists.
var findMe = "calc";
var pathes = Environment.GetEnvironmentVariable("Path").Split(';');
foreach (var path in pathes)
{
var testMe = $#"{path}\{findMe}.exe";
if (File.Exists(testMe))
{
Console.WriteLine(testMe);
}
}
This outputs :
C:\WINDOWS\system32\calc.exe
I do not know about any way of doing that exact thing from C# either. However the paths are usually well known and can be retreived via the SpecialFolders Enumeration:
using System;
using System.Diagnostics;
using System.IO;
namespace RunAsAdmin
{
class Program
{
static void Main(string[] args)
{
/*Note: Running a batch file (.bat) or similar script file as admin
Requires starting the interpreter as admin and handing it the file as Parameter
See documentation of Interpreting Programm for details */
//Just getting the Absolute Path for Notepad
string windir = Environment.GetFolderPath(Environment.SpecialFolder.Windows);
string FullPath = Path.Combine(windir, #"system32\notepad.exe");
//The real work part
//This is the programm to run
ProcessStartInfo startInfo = new ProcessStartInfo(FullPath);
//This tells it should run Elevated
startInfo.Verb = "runas";
//And that gives the order
//From here on it should be 100% identical to the Run Dialog (Windows+R), except for the part with the Elevation
System.Diagnostics.Process.Start(startInfo);
}
}
}
I did not just use System (37) back then, as I wrote it when x32/x86 Systems were still a thing. You would need to check how it resolves nowadays.
Note that most of those paths are duplicated in the PATH System Variable, so you could look it up: https://www.architectryan.com/2018/03/17/add-to-the-path-on-windows-10/
Path Variables in turn go back to the old DOS days. Basically if you gave the Commandline a command/filename it would try the build-in commands, then Executables in the current working Directory (.bat, .com, .exe), and then go look over the path directories to again look for executeables. And only if all that failed, would it complain.
I finally tried to combine all three answers and came up with this:
I post it here in case someone has the same problem.
public static string[] GetPathOf(string cmd)
{
var list = new List<string>();
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.Machine).Split(';'));
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.Process).Split(';'));
list.AddRange(Environment.GetEnvironmentVariable("path", EnvironmentVariableTarget.User).Split(';'));
list = list.Distinct().Where(e=>Directory.Exists(e)).SelectMany(e=> new DirectoryInfo(e).GetFiles()).Where(e=>Regex.IsMatch(e.Name,"(?i)^"+cmd+"\\.(?:exe|cmd|com)")).Select(e=>e.FullName).ToList();
return list.ToArray();
}
I want to move several files that names are saved in an ObservableCollection<String> _collection with this method:
string firstFolderThatContainsEveryFile = "...\Folder\Files";
string secondFolderArchiv = "...\Folder\Files\Archiv";
foreach (var item in _collection)
{
string firstFolder = System.IO.Path.Combine(firstFolderThatContainsEveryFile, item);
string secondFolder = System.IO.Path.Combine(secondFolderArchiv, item);
File.Move(firstFolder, secondFolder);
}
This works at the first time, but if i load new files into firstFolderThatContainsEveryFile and try to use my move method i get an exception:
File already in Use by other process
This are the steps:
I open the programm -> use the move method -> success -> close the programm -> fill the folder with new files -> open the programm -> use the move method -> exception!
How can i get the processname or processID to close the process before i use my move method, or is there even a better way to get around this?
To figure out which process are using your file, with the solution proposed by this, you can use a tool Handle of Microsoft and this code C# to invoke the tool.
public void ViewProcess(string filePath)
{
Process tool = new Process();
tool.StartInfo.FileName = "handle.exe";
tool.StartInfo.Arguments = filePath + " /accepteula";
tool.StartInfo.UseShellExecute = false;
tool.StartInfo.RedirectStandardOutput = true;
tool.Start();
tool.WaitForExit();
string outputTool = tool.StandardOutput.ReadToEnd();
string matchPattern = #"(?<=\s+pid:\s+)\b(\d+)\b(?=\s+)";
foreach (Match match in Regex.Matches(outputTool, matchPattern))
{
try{
Console.WriteLine(match.Value); // this is the process ID using the file
}
catch(Exception ex)
{
}
}
}
If the file is using by the others program, you should figure why they use it, if by your program, so recheck your code to understand why.
I am working on windows application project and from that project want to build different multiple c# projects which are in one solution of visual studio 2015 and also want them to be build programmatically individually using MSBuild tool without using command prompt and finally want to show the output in log file not in command prompt (means those project is building successfully or having any errors like this message in log file)
Do I need to use any MSBuild API and how to add in this project?
I have seen many questions like this (not exactly same) but it didn't work for me. please can anybody help me with this?
using Microsoft.Build.Evaluation;
using Microsoft.Build.Execution;
using Microsoft.Build.Logging;
...
public static BuildResult Compile(string solution_name, out string buildLog)
{
buildLog = "";
string projectFilePath = solution_name;
ProjectCollection pc = new ProjectCollection();
Dictionary<string, string> globalProperty = new Dictionary<string, string>();
globalProperty.Add("nodeReuse", "false");
BuildParameters bp = new BuildParameters(pc);
bp.Loggers = new List<Microsoft.Build.Framework.ILogger>()
{
new FileLogger() {Parameters = #"logfile=buildresult.txt"}
};
BuildRequestData buildRequest = new BuildRequestData(projectFilePath, globalProperty, "4.0",
new string[] {"Clean", "Build"}, null);
BuildResult buildResult = BuildManager.DefaultBuildManager.Build(bp, buildRequest);
BuildManager.DefaultBuildManager.Dispose();
pc = null;
bp = null;
buildRequest = null;
if (buildResult.OverallResult == BuildResultCode.Success)
{
Console.ForegroundColor = ConsoleColor.Green;
}
else
{
if (Directory.Exists("C:\\BuildResults") == false)
{
Directory.CreateDirectory("C:\\BuildResults");
}
buildLog = File.ReadAllText("buildresult.txt");
Console.WriteLine(buildLog);
string fileName = "C:\\BuildResults\\" + DateTime.Now.Ticks + ".txt";
File.Move("buildresult.txt", fileName);
Console.ForegroundColor = ConsoleColor.Red;
Thread.Sleep(5000);
}
Console.WriteLine("Build Result " + buildResult.OverallResult.ToString());
Console.ForegroundColor = ConsoleColor.Gray;
Console.WriteLine("================================");
return buildResult;
}
This is some old code I had lying around.
I use this to programatically build solutions and C# Projects. The output will be a BuildResult.Success or BuildResult.Failure.
The variable buildLog will contain the build output.
Note - the only way to access the build output that I am aware of is to use the above methodology of having a log file generated and then reading it in your C# code.
One thing to be aware of and I never did find a fix for this, is that the application that runs this code, may keep dll's it loads into memory from nuget package directories in memory. This makes deleting those directories problematic. I found a work around by having my application run as a MS Service - it seems when it runs as a local service, it has enough permissions to delete files held in memory.
Brief Summary
I am creating a lightweight IDE for NASM development in C# (I know kind of an irony). Kinda of like Notepad++ but simpler but with features that make it more than source editor. Since Notepad++ is really just a fancy source editor. I have already implemented features like Project creation (using a project format similar to how Visual Studio organizes projects). Project extension .nasmproj. I am also in the works of hosting it in an open-source place (Codeplex). Although the program is far from finish, and definitely cannot be used in a production environment without proper protection and equipment. In addition, I am working alone with it at this moment, more like a spare time project since I just finished my last Summer final taking Calculus I.
Problem
Right now I am facing a problem, I can build the project but no output from NASM is being fed into the IDE. I have succesfully built a project, and I was able to produce object files. I even tried producing a syntax error to see if I finally see something come up but none and I check the bin folder of the test project I created and I see no object file creating. So definitely NASM is doing its magic. Is it because NASM doesn't want me to see its output. Is there a solution? Any advice would be great. Here is the code which I think is giving Trouble.
Things to Note
I have already checked if events have been invoked. An yes they have but they return empty strings
I have also checked error data and same effect.
Code
public static bool Build(string arguments, out Process nasmP)
{
try
{
ProcessStartInfo nasm = new ProcessStartInfo("nasm", arguments);
nasm.CreateNoWindow = true;
nasm.RedirectStandardError = true;
nasm.RedirectStandardInput = true;
nasm.RedirectStandardOutput = true;
nasm.UseShellExecute = false;
nasmP = new Process();
nasmP.EnableRaisingEvents = true;
nasmP.StartInfo = nasm;
bool predicate = nasmP.Start();
nasmP.BeginOutputReadLine();
return true;
}
catch
{
nasmP = null;
return false;
}
}
//Hasn't been tested nor used
public static bool Clean(string binPath)
{
if (binPath == null || !Directory.Exists(binPath))
{
throw new ArgumentException("Either path is null or it does not exist!");
}
else
{
try
{
DirectoryInfo binInfo = new DirectoryInfo(binPath);
FileInfo[] filesInfo = binInfo.GetFiles();
for (int index = 0; index < filesInfo.Length; index++)
{
try
{
filesInfo[index].Delete();
filesInfo[index] = null;
}
catch
{
break;
}
}
GC.Collect();
return true;
}
catch
{
return false;
}
}
}
}
using (BuildDialog dlg = new BuildDialog(currentSolution))
{
DialogResult result = dlg.ShowDialog();
dlg.onOutputRecieved += new BuildDialog.OnOutputRecievedHandler(delegate(Process _sender, string output)
{
if (result == System.Windows.Forms.DialogResult.OK)
{
outputWindow.Invoke(new InvokeDelegate(delegate(string o)
{
Console.WriteLine("Data:" + o);
outputWindow.Text = o;
}), output);
}
});
}
Edits
I have tried doing synchronously instead of asynchronously but still the same result (and empty string "" is returned) actually by debugging the stream is already at the end. So looks like nothing has been written into the stream.
This is what I tried:
string readToEnd = nasmP.StandardOutput.ReadToEnd();
nasmP.WaitForExit();
Console.WriteLine(readToEnd);
And another interesting thing I have tried was I copied the arguments from the debugger and pasted it in the command line shell and I can see NASM compiling and giving the error that I wanted to see all along. So definitely not a NASM problem. Could it be a problem with my code or the .Net framework.
Here is a nice snapshot of the shell window (although not technically proof; this is what the output should look like in my IDE):
Alan made a very good point, check the sub processes or threads. Is sub process and thread synonymous? But here is the problem. Almost all the properties except a select few and output/error streams are throwing an invalid operation. Here is the debugger information as an image (I wish Visual Studio would allow you to copy the entire information in click):
Okay I finally was able to do it. I just found this control that redirect output from a process and I just looked at the source code of it and got what I needed to do. Here is the the modified code:
public static bool Build(string command, out StringBuilder buildOutput)
{
try
{
buildOutput = new StringBuilder();
ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe");
startInfo.Arguments = "/C " + " nasm " + command;
startInfo.RedirectStandardError = true;
startInfo.RedirectStandardOutput = true;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
Process p = Process.Start(startInfo);
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
if (output.Length != 0)
buildOutput.Append(output);
else if (error.Length != 0)
buildOutput.Append(error);
else
buildOutput.Append("\n");
return true;
}
catch
{
buildOutput = null;
return false;
}
}
Here is how the output is formatted like:
I also wanted to thank Alan for helping me debug my code, although he didn't physically had my code. But he really was helpful and I thank him for it.
I'm attempting to pull the latest version of source code out of TFS programmatically using the SDK, and what I've done somehow does not work:
string workspaceName = "MyWorkspace";
string projectPath = "/TestApp";
string workingDirectory = "C:\Projects\Test\TestApp";
VersionControlServer sourceControl; // actually instantiated before this method...
Workspace[] workspaces = sourceControl.QueryWorkspaces(workspaceName, sourceControl.AuthenticatedUser, Workstation.Current.Name);
if (workspaces.Length > 0)
{
sourceControl.DeleteWorkspace(workspaceName, sourceControl.AuthenticatedUser);
}
Workspace workspace = sourceControl.CreateWorkspace(workspaceName, sourceControl.AuthenticatedUser, "Temporary Workspace");
try
{
workspace.Map(projectPath, workingDirectory);
GetRequest request = new GetRequest(new ItemSpec(projectPath, RecursionType.Full), VersionSpec.Latest);
GetStatus status = workspace.Get(request, GetOptions.GetAll | GetOptions.Overwrite); // this line doesn't do anything - no failures or errors
}
finally
{
if (workspace != null)
{
workspace.Delete();
}
}
The approach is basically creating a temporary workspace, using the Get() method to grab all the items for this project, and then removing the workspace. Is this the correct way to do this? Any examples would be helpful.
I ended up using a different approach that seems to work, mainly taking advantage of the Item.DownloadFile() method:
VersionControlServer sourceControl; // actually instantiated...
ItemSet items = sourceControl.GetItems(sourcePath, VersionSpec.Latest, RecursionType.Full);
foreach (Item item in items.Items)
{
// build relative path
string relativePath = BuildRelativePath(sourcePath, item.ServerItem);
switch (item.ItemType)
{
case ItemType.Any:
throw new ArgumentOutOfRangeException("ItemType returned was Any; expected File or Folder.");
case ItemType.File:
item.DownloadFile(Path.Combine(targetPath, relativePath));
break;
case ItemType.Folder:
Directory.CreateDirectory(Path.Combine(targetPath, relativePath));
break;
}
}
I completed and implemented the code into a button as web asp.net solution.
For the project to work in the references should be added the Microsoft.TeamFoundation.Client and Microsoft.TeamFoundation.VersionControl.Client references and in the code the statements using Microsoft.TeamFoundation.Client; and using Microsoft.TeamFoundation.VersionControl.Client;
protected void Button1_Click(object sender, EventArgs e)
{
string workspaceName = "MyWorkspace";
string projectPath = #"$/TeamProject"; // the container Project (like a tabel in sql/ or like a folder) containing the projects sources in a collection (like a database in sql/ or also like a folder) from TFS
string workingDirectory = #"D:\New1"; // local folder where to save projects sources
TeamFoundationServer tfs = new TeamFoundationServer("http://test-server:8080/tfs/CollectionName", System.Net.CredentialCache.DefaultCredentials);
// tfs server url including the Collection Name -- CollectionName as the existing name of the collection from the tfs server
tfs.EnsureAuthenticated();
VersionControlServer sourceControl = (VersionControlServer)tfs.GetService(typeof(VersionControlServer));
Workspace[] workspaces = sourceControl.QueryWorkspaces(workspaceName, sourceControl.AuthenticatedUser, Workstation.Current.Name);
if (workspaces.Length > 0)
{
sourceControl.DeleteWorkspace(workspaceName, sourceControl.AuthenticatedUser);
}
Workspace workspace = sourceControl.CreateWorkspace(workspaceName, sourceControl.AuthenticatedUser, "Temporary Workspace");
try
{
workspace.Map(projectPath, workingDirectory);
GetRequest request = new GetRequest(new ItemSpec(projectPath, RecursionType.Full), VersionSpec.Latest);
GetStatus status = workspace.Get(request, GetOptions.GetAll | GetOptions.Overwrite); // this line doesn't do anything - no failures or errors
}
finally
{
if (workspace != null)
{
workspace.Delete();
Label1.Text = "The Projects have been brought into the Folder " + workingDirectory;
}
}
}
Your approach is valid.
Your error is in your project path. Use something like this instead:
string projectPath = "$/PathToApp/TestApp";
I agree with Joerage that your server path is probably the culprit. To get more insight into what's happening, you need to wire up some events on the VersionControlServer object. At minimum you'll want Getting, NonFatalError, and Conflict.
Complete list: http://msdn.microsoft.com/en-us/library/microsoft.teamfoundation.versioncontrol.client.versioncontrolserver_events(VS.80).aspx
I had a similar situation where I needed to download contents of 'a' folder from tfs into an existing workspace, without creating a new workspace. With help from the above answers, I was able to put something together that works fine for me as of now. There is however a limitation. This works for contents of 'a' folder with just files and not another folder inside it -I have not tried that out. Maybe that would involve some minor updates. Sharing code, just in case someone is searching for this. I really like the fact that this approach does not deal with workspace [-create and delete], since that is not desired.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Configuration;
using Microsoft.TeamFoundation.VersionControl.Client;
using Microsoft.TeamFoundation.Client;
using System.IO;
namespace DownloadFolder
{
class Program
{
static void Main(string[] args)
{
string teamProjectCollectionUrl = "http://<YourTFSUrl>:8080/tfs/DefaultCollection"; // Get the version control server
TfsTeamProjectCollection teamProjectCollection = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(teamProjectCollectionUrl));
VersionControlServer vcs = teamProjectCollection.GetService<VersionControlServer>();
String Sourcepath = args[0]; // The folder path in TFS - "$/<TeamProject>/<FirstLevelFolder>/<SecondLevelFolder>"
String DestinationPath = args[1]; //The folder in local machine - "C:\MyTempFolder"
ItemSet items = vcs.GetItems(Sourcepath, VersionSpec.Latest, RecursionType.Full);
String FolderName = null;
foreach (Item item in items.Items)
{
String ItemName = Path.GetFileName(item.ServerItem);
switch (item.ItemType)
{
case ItemType.File:
item.DownloadFile(Path.Combine(DestinationPath, FolderName, ItemName));
break;
case ItemType.Folder:
FolderName = Path.GetFileName(item.ServerItem);
Directory.CreateDirectory(Path.Combine(DestinationPath, ItemName));
break;
}
}
}
}
}
While running this from the command prompt copy all the supporting dlls along with exe
cmd>> DownloadFolder.exe "$/<TeamProject>/<FirstLevelFolder>/<SecondLevelFolder>" "C:\MyTempFolder"