I'm working on an application that can create and add items to TFS.
My files get created correctly and in the correct location, however the Workspace.PendAdd method only works for one specific workspace, other workspaces give me the "There is no working folder mapping" error.
The method I am using is PendAdd(string filepath, bool recursive) so I pass in a directory to be added and would expect to add both the directory and its files as a pending add in TFS.
Both workspaces are identical in everything but name, neither have any files currently checked out and neither contain any files.
From my google searches I have found that there may be an issue when adding files to a workspace mapped to $/ but the workspace I am using is mapped to $/TFSFiles/ and so far nothing else seems likely.
The code for my PendAdd method is :
private IEnumerable<PendingChange> PendAddFileToTfs(string newFilename)
{
var previousPendingChanges = new List<PendingChange>(_selectedWorkspace.GetPendingChanges());
var changesAdded = _selectedWorkspace.PendAdd(Path.GetDirectoryName(newFilename), true);
var pendingAdds = new List<PendingChange>(_selectedWorkspace.GetPendingChanges());
var itemsToCheckIn = pendingAdds.Except(previousPendingChanges);
return itemsToCheckIn;
}
The method fails at the _selectedWorkspace.PendAdd line. I debugged and verified it is the correct local path and correct workspace.
Can someone point me in the right direction here?
EDIT:
The _selectedWorkspace variable is set by the user.
I get a list of all available workspaces via this code:
_workspaces = _versionControl.QueryWorkspaces(null, _versionControl.AuthorizedUser,environment.MachineName);
I then show a list of workspaces in a combobox and allow the user to select one.
This calls this method:
public void SetWorkspace(string workspaceName)
{
_selectedWorkspace = _workspaces.FirstOrDefault(ws => ws.Name.Equals(workspaceName));
}
You need to create a local workspace before you can add pendAdd files to TFS. I am not sure where _selectedWorkspace is coming from but it looks like it is not configured properly. Although I don't have a c# version to hand I do have a version in PowerShell that calls c#... should give you the way to go.
function New-TfsTeamProjectRootFolder {
Param(
[Microsoft.TeamFoundation.Client.TfsTeamProjectCollection] $TfsCollection,
[Microsoft.TeamFoundation.Server.ProjectInfo] $TfsTeamProject,
[String] $GlobalEntryValue
)
$TempWorkspaceName = "Create-TfsTeamSourceFolder"
$TempFolder = "c:\temp\$TempWorkspaceName"
$ServerFolder = "$/$($TfsTeamProject.Name)"
$TfsVersionControl = Get-TfsVersionControlServer $TfsCollection
try {
$workspace = $TfsVersionControl.GetWorkspace($TempFolder )
} catch {
$workspace = $TfsVersionControl.CreateWorkspace($TempWorkspaceName);
$workspace.Map($ServerFolder, $TempFolder);
}
$NewFolder = "$TempFolder\$GlobalEntryValue";
try {
$SourceItem = $TfsVersionControl.GetItem("$ServerFolder/$GlobalEntryValue")
} catch {
New-Item -ItemType Directory -Force -Path $NewFolder;
$workspace.PendAdd($NewFolder, $true);
$pendingChanges = $workspace.GetPendingChanges();
$changesetNumber = $workspace.CheckIn($pendingChanges, "Added folder for '$GlobalEntryValue'");
$SourceItem = $TfsVersionControl.GetItem("$ServerFolder/$GlobalEntryValue")
}
$workspace.Delete()
Return $SourceItem
}
Again I am not sure why your code is not working as I think the issue is in the greater context than we can see in the example above.
Related
I am using the TFS API to get latest code files, directories, .csproj files, etc. under a TFS-bound folder.
For the same, I use something like the following:
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new
Uri(ConfigurationManager.AppSettings["TFSUrl"]));
tfs.EnsureAuthenticated();
var vsStore = tfs.GetService<VersionControlServer>();
string workingFolder = #"C:\TFS\SolutionFolder";
Workspace wsp = vsStore.TryGetWorkspace(workingFolder);
if (wsp != null)
{
ItemSet items = vsStore.GetItems(workingFolder, VersionSpec.Latest, RecursionType.Full);
string relativePath = workingFolder + #"/";
foreach (Item item in items.Items)
{
string relativePath1 = item.ServerItem.Replace("$/TFS/SolutionFolder", relativePath);
if (item.ItemType == ItemType.Folder)
{
Directory.CreateDirectory(relativePath1);
}
else
{
item.DownloadFile(relativePath1);
}
}
}
Now, I get the items to download and then download happens. However, I want it to be like how VS handles it - if (and only if) there is a change in a file/folder, then only download the same. With this code, I always get 'n' number of files/folders in that folder and then I overwrite the same. Wrong approach, I know. I can, however, modify this code to check for the folder's or file's last change time and then choose to either overwrite it or ignore it. That's an option, albeit a bad one at that.
Now, what I would ideally like is to get ONLY the list of files/folders that actually need to be changed i.e. the incremental change. After that, I can choose to overwrite/ignore each item in that list. So, in the present case, if a new file/folder is created (or one of the existing ones got changed inside $/TFS/SolutionFolder i.e. in the sever), then only I want to pull that item in the list of files/folders to change(and decide what I want to do with it inside C:\TFS\SolutionFolder).
Also, is using one of the overloads of VersionControlServer.QueryHistory() an option? I had something like this:
(latestVersionIdOf $/TFS/SolutionFolder) - (existingVersionIdOf C:\TFS\SolutionFolder) = (Versions that I'd go out and get back from the server, for that folder)
in mind.
Any pointers will be very helpful. Thanks!
Just use Workspace.Get() or overload method (wsp.Get()), it just update updated files.
I don't think we can achieve that. If the files are downloaded to a folder without in source control, there are no versions compared within the folder, even if the folder is in source control, the behavior is just download also no version compare actions. So, it will download all the files ever time and then overwrite the same ones.
In VS, the files are all in TFS source control system, so when we Get Latest Version the changed/added files will be retrieved from TFS. If you want to get the same behavior as VS handles, you can use the tf get command. See Get Command
You can reference this article to use the tf get command :
get-latest-version-of-specific-files-with-tfs-power-tools
Update :-
var tfs = TfsTeamProjectCollectionFactory.GetTeamProjectCollection(new Uri(ConfigurationManager.AppSettings["TFSUrl"]));
tfs.EnsureAuthenticated();
var vsStore = tfs.GetService<VersionControlServer>();
string workingFolder = ConfigurationManager.AppSettings["LocalPathToFolder"]; // C:\TFS\SolutionFolder
string tfsPathToFolder = ConfigurationManager.AppSettings["TFSPathToFolder"]; // $/TFS/SolutionFolder
Workspace wsp = vsStore.GetWorkspace(workingFolder);
if (wsp != null)
{
ItemSpec[] specs = { new ItemSpec(tfsPathToFolder, RecursionType.Full) };
ExtendedItem[][] extendedItems = wsp.GetExtendedItems(specs, DeletedState.NonDeleted, ItemType.Any);
ExtendedItem[] extendedItem = extendedItems[0];
var itemsToDownload = extendedItem.Where(itemToDownload => itemToDownload.IsLatest == false);
foreach (var itemToDownload in itemsToDownload)
{
try
{
switch (itemToDownload.ItemType)
{
case ItemType.File:
if (itemToDownload.LocalItem != null)
{
vsStore.DownloadFile(itemToDownload.SourceServerItem, itemToDownload.LocalItem);
}
else
{
string localItemPath = itemToDownload.SourceServerItem.Replace(tfsPathToFolder,
workingFolder);
vsStore.DownloadFile(itemToDownload.SourceServerItem, localItemPath);
}
break;
case ItemType.Folder:
string folderName = itemToDownload.SourceServerItem.Replace(tfsPathToFolder, workingFolder);
if ((!string.IsNullOrEmpty(folderName)) && (!Directory.Exists(folderName)))
{
Directory.CreateDirectory(folderName);
}
break;
}
}
catch (Exception e)
{
File.AppendAllText(#"C:\TempLocation\GetLatestExceptions.txt", e.Message);
}
}
}
This code works well, except:
a. Whenever it downloads the latest copy of, let's say a file, it 'checks it out' in TFS :(
b. For some items, it throws errors like 'Item $/TFS/SolutionFolder/FolderX/abc.cs was not found in source control at version T.' - I have to find out what the exact cause of this issue is, though.
Any ideas on how to get around these two issues or any other problems you see with this code? Thanks!
I m trying to commit changes to the project to my svn server but it doesnt seem to work.
The Test:
I run the code below with the folder where nothing has been changed and nothing happens as expected, Then I create a new folder and then run the code again, this time nothing seems to happen, the code runs and returns without the error showing it worked.
public bool svnCommitProject(String project, String message)
{
using (SvnClient client = new SvnClient())
{
try
{
SvnCommitArgs args = new SvnCommitArgs();
args.LogMessage = message;
client.Authentication.ForceCredentials(Properties.Settings.Default.Username, Properties.Settings.Default.Password);
return client.Commit(Properties.Settings.Default.LocalFolderPath + Properties.Settings.Default.Username + #"\" + project, args);
}
catch
{
MessageBox.Show("ERROR");
return false;
}
}
}
Suspected Problem:
From looking at this and google i suspect that the problem exists because the file hasnt been "added" to svn control, but im not sure.
Is this the case? and if so how would I go about adding the files which need to be added? I also assume that something similar would be needed for files which are deleted/modified, is this correct and how would I add this in too?
See Find files not added to subversion
Yes, files just dropped into the local working directory doesn't tell the SVN to commit to.
Collection<SvnStatusEventArgs> filesStatuses = new Collection<SvnStatusEventArgs>();
if (!client.GetStatus(localDir, new SvnStatusArgs
{
Depth = SvnDepth.Infinity,
RetrieveRemoteStatus = true,
RetrieveAllEntries = true
}, out workDirFilesStatus))
{
throw new SvnOperationCanceledException("SvnClient.GetStatus doesn't return anything.");
}
filesStatuses.Where(i => i.LocalContentStatus == SvnStatus.NotVersioned).ToList().ForEach(i => svnC.Add(i.Path));
filesStatuses.Where(i => i.LocalContentStatus == SvnStatus.Missing).ToList().ForEach(i => svnC.Delete(i.Path));
i´m currently working on a programm which updates templates on our companies Team Foundation Server. I am having those new templates locally on my disk and want to replace the existing ones on the server. I was trying different approaches and this is my newest version. The problem is that either
the new file is "in use" when accessing it through coding in c#(while not in use when i try to replace it in runtime using the normal explorer).
the replacement is not appearing in the pending changes, the pendingChanges array is initial.
using (var tfs = TeamFoundationServerFactory.GetServer("myserver"))
{
var versionControlServer = tfs.GetService(typeof(VersionControlServer)) as VersionControlServer;
// Create a new workspace for the currently authenticated user.
var workspace = versionControlServer.CreateWorkspace("Temporary Workspace", versionControlServer.AuthorizedUser);
try
{
// Check if a mapping already exists.
var workingFolder = new WorkingFolder("$serverpath", #"c:\tempFolder");
// Create the mapping (if it exists already, it just overides it, that is fine).
workspace.CreateMapping(workingFolder);
workspace.Get(VersionSpec.Latest, GetOptions.GetAll);
string[] paths = new string[1];
paths[0] = "test.pdf";
workspace.PendEdit(paths, RecursionType.Full, null, LockLevel.None);
// Go through the folder structure defined and create it locally, then check in the changes.
CreateFolderStructure(workspace, workingFolder.LocalItem);
// Check in the changes made.
int a = workspace.CheckIn(workspace.GetPendingChanges(), "This is my comment");
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
finally
{
// Cleanup the workspace.
workspace.Delete();
// Remove the temp folder used.
Directory.Delete(#"C:\tempFolder", true);
}
}
}
static void CreateFolderStructure(Workspace workspace, string initialPath)
{
workspace.PendDelete("$serverpath/test.pdf", RecursionType.None);
File.Copy(#"C:\test\testnew.pdf", #"C:\tempfolder\test", true);
workspace.PendAdd(#"C:\tempfolder\test.pdf");
}
I found a solution to the problem. The workspace which was used by "authorizedUser" was obviously not enough.. I found out that a need a TeamFoundationIdentity to do it. Here is a guide on how to fix the issue.
http://blogs.msdn.com/b/taylaf/archive/2010/03/29/using-tfs-impersonation-with-the-version-control-client-apis.aspx
I need to add a custom activity to the default workflow template to increase assembly versions at the earliest point possible in the build process.
What I would like to achieve is to create and map the exact same workspace (that is be created further down in the workflow) inside my custom activity so that I can check out an xml file, increase the version number held within, write it back to the xml file and check the xml file back in.
I'm aware that this workspace will be created later on in the workflow but that will be too late in the build process for what I'm trying to achieve, so instead of moving any of the activities or duplicating them in a position above my custom activity (this should be ok as this workspace will be deleted and recreated again later)
I think the details I need are the BuildDirectory, WorkspaceName and SourcesDirectory. Can anyone tell me how to achieve the creation of the workspace or how obtain this data in code?
the build will be carried out on a build server, and I am using TFS 2010 and c#.
Thanks in advance
I followed the series of blog articles by Ewald Hofman as a primer and created a custom activity that does the check-out, update and check-in of a GlobalAssemblyInfo file that I parse the current version from. My task is inserted at the top of the "Update Drop Location" which is right after it does the "Get the build" portion of the workflow. I just use require the IBuildDetail and a File Mask as arguments from which you can pull out the VersionControlServer to be able to access TFS. My code is below:
protected override string Execute(CodeActivityContext context)
{
// Obtain the runtime value of the input arguments.
string assemblyInfoFileMask = context.GetValue(AssemblyInfoFileMask);
IBuildDetail buildDetail = context.GetValue(BuildDetail);
var workspace = buildDetail.BuildDefinition.Workspace;
var versionControl = buildDetail.BuildServer.TeamProjectCollection.GetService<VersionControlServer>();
Regex regex = new Regex(AttributeKey + VersionRegex);
// Iterate of the folder mappings in the workspace and find the AssemblyInfo files
// that match the mask.
foreach (var folder in workspace.Mappings)
{
string path = Path.Combine(folder.ServerItem, assemblyInfoFileMask);
context.TrackBuildMessage(string.Format("Checking for file: {0}", path));
ItemSet itemSet = versionControl.GetItems(path, RecursionType.Full);
foreach (Item item in itemSet.Items)
{
context.TrackBuildMessage(string.Format("Download {0}", item.ServerItem));
string localFile = Path.GetTempFileName();
try
{
// Download the file and try to extract the version.
item.DownloadFile(localFile);
string text = File.ReadAllText(localFile);
Match match = regex.Match(text);
if (match.Success)
{
string versionNumber = match.Value.Substring(AttributeKey.Length + 2, match.Value.Length - AttributeKey.Length - 4);
Version version = new Version(versionNumber);
Version newVersion = new Version(version.Major, version.Minor, version.Build + 1, version.Revision);
context.TrackBuildMessage(string.Format("Version found {0}", newVersion));
return newVersion.ToString();
}
}
finally
{
File.Delete(localFile);
}
}
}
return null;
}
Using SharpSvn, how can I get a list of files that need to be committed (the list that you would see if you right click on a folder with tortoisesvn and hit commit)
I tried this:
SharpSvn.SvnClient client = new SharpSvn.SvnClient();
Collection<SvnListChangeListEventArgs> list;
bool result = client.GetChangeList(#"C:\MyProjectPath", out list);
But it seems to be returning a list of every file in the project instead of just the modified ones.
Sander is correct, here is a more complete example of listing modified files:
var statusArgs = new SvnStatusArgs();
statusArgs.Depth = SvnDepth.Infinity;
statusArgs.RetrieveAllEntries = true;
Collection<SvnStatusEventArgs> statuses;
svnClient.GetStatus(#"C:\SVN\stuff\", statusArgs, out statuses);
foreach (SvnStatusEventArgs statusEventArgs in statuses)
{
if (statusEventArgs.LocalContentStatus == SvnStatus.Modified)
Console.WriteLine("Modified file: " + statusEventArgs.Path);
}
The function you're using is for the changelist feature. To see what files are changed use the GetStatus or Status calls. In this case you want to check the LocalContentStatus and LocalPropertyStatus