0

I need to download selected folders from a TFS Source control to local file folders. I am able to perform that using following script:

Add-PSSnapin Microsoft.TeamFoundation.PowerShell
$securePass = ConvertTo-SecureString -AsPlainText -Force -String "mypassword"
$uri = "serverURL"
$cred =  New-Object System.Management.Automation.PSCredential("myusername",$securePass)

$tfsServer = Get-TfsServer -Name $uri -Credential $cred
$structureService = $tfsServer.GetService("Microsoft.TeamFoundation.Server.ICommonStructureService")

$versionControlService = $tfsServer.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")

$pathsToDownload=[System.Collections.ArrayList]@()
$pathsToDownload.add("$/myfirstPath")
$pathsToDownload.add("$/mysecondPath")
$pathsToDownload.add("$/mythirdPath")

$localTFSRoot = "c:\tfs"
$serverRoot = "$/"
foreach ($serverPath in $pathsToDownload) {
  write-host "Working with $serverPath"
  $items = Get-TfsChildItem -Server $tfsServer -Item $serverPath -Recurse
  foreach ($item in $items) {
    $destinationPath=$($Item.ServerItem.Replace($serverRoot,$localTFSRoot)).replace("\","/")
    write-host "Downloading $destinationPath"
    if ($item.ItemType -eq "Folder") {
       #create directory if it doesn't already exist
       if (-Not (Test-Path $destinationPath -PathType Container -IsValid)) {
          New-Item -ItemType Directory -Path $destinationPath -Force
       }
    } else {
       $versionControlService.DownloadFile($item.ServerItem,$destinationPath)
    }
  }
}

However, this script downloads all the files every time. I would like to download the files only if there is change in the file. Is there a way to perform such operation through powershell operations? I am not sure what does getItem do versus downloadfile.

Thank you for your help.

UPDATE *** I was able to download using the workspace route but I haven't tried if it works only for update. However, the workspace.get() is downloading without any verbose output. Is there a way to get it to list the files it is working with so that user doesn't think it is hanged while it is downloading?

if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null ) {
 Add-PSSnapin Microsoft.TeamFoundation.PowerShell
}
$securePass = ConvertTo-SecureString -AsPlainText -Force -String "mypassword"
$uri = "https://myserver"
$cred =  New-Object System.Management.Automation.PSCredential("myusername",$securePass)

$tfsServer = Get-TfsServer -Name $uri -Credential $cred
$structureService = $tfsServer.GetService("Microsoft.TeamFoundation.Server.ICommonStructureService")
$versionControlService = $tfsServer.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")

$workSpaceName = $env:USERNAME + "-IMAG"

$localTFSWorkspace="c:\tfswspace"
$serverRoot = "$/myproject"
$pathsToDownload=[System.Collections.ArrayList]@()
$pathsToDownload.add("$/myproject/path1")
$pathsToDownload.add("$/myproject/path2")

$testPath = $($pathsToDownload[0].Replace($serverRoot,$localTFSWorkspace)).replace("/","\")

if (!(Test-Path $localTFSWorkspace -PathType Container)) {
  New-Item $localTFSWorkspace -ItemType Directory -Force
}

# Check if workspace already exists
     $workspace = $versionControlService.TryGetWorkspace($testPath)
     if ($workspace -eq $null) {
       #Workspace doesn't exist, we need to create one
       $workspace = $versionControlService.CreateWorkspace($workSpaceName,$cred.UserName, 'Workspace for Repository Sync')
     }

 #create mappings if these don't exist 
 foreach ($serverPath in $pathsToDownload) {
      $localPath = $($serverPath.Replace($serverRoot,$localTFSWorkspace)).replace("/","\")
      if (!(Test-Path $localPath -PathType Container)) {
         New-Item $localPath -ItemType Directory -Force
      }

    $workingFolder =  $workspace.TryGetWorkingFolderForServerItem($serverPath)

    if ($workingFolder -eq $null) {
      #create mapping here
      $workspace.Map($serverPath,$localPath)
    }
 }
 # Now mappings are done -- get items now
 $workspace.get()
sohail
  • 589
  • 1
  • 11
  • 26
  • What version of tfs are you using? I have a feeling you should be including a VersionSpec set to latest somewhere. – Mike Cheel Jun 19 '18 at 18:18
  • It is TFS 2013. My understanding is that downloadFile will always get the latest version. However, it doesn't "know" that there is a local copy already existing. – sohail Jun 19 '18 at 18:22
  • It has been a while since I have used the TFS API stuff but have you tried downloading a file, wait a couple of minutes, do it again and then compare the time stamps from the first to the second download? Typically TFS avoids downloading files to a workspace if it thinks they have already been downloaded. – Mike Cheel Jun 19 '18 at 18:36
  • True but in my script, I haven't created or defined a workspace. It is just a local directory. – sohail Jun 19 '18 at 18:41
  • That MAY be the source of your problems. It isn't tracking the files. – Mike Cheel Jun 19 '18 at 18:56
  • @MikeCheel That is *absolutely* the problem. – Daniel Mann Jun 19 '18 at 18:57
  • I'm guessing but if you can't do a workspace you will have to track the files yourself (perhaps some sort of sha hash or something like git does) but I personally would look at a workspace related option. – Mike Cheel Jun 19 '18 at 19:01
  • I am totally new to TFS and its concepts. Any code sample to create a local workspace would be great. Also, does that allow me to selectively download only some folders or would I need to download the full tree – sohail Jun 19 '18 at 19:04
  • Updated question with workspace route solution and posted followup question in update. Is there a way to get output from workspace.get() command – sohail Jun 20 '18 at 23:05
  • @sohail After performing `Get`, you'll see all files are downloaded in the workspace. If the files in the workspace are latest, no file will be downloaded. – Cece Dong - MSFT Jun 21 '18 at 09:48

2 Answers2

0

You need to create a workspace to to get all files. When you perform Get, you'll see all files are downloaded in the workspace. If the files in the workspace are latest, no file will be downloaded. Once there are new updated files in TFS, after performing Get, only updated files will be replaced. Here is a C# code snippet about how to create a workspacce:

            TfsTeamProjectCollection tfs = new TfsTeamProjectCollection(new Uri("http://tfs:8080/tfs/defaultCollection"));
            var versioncontrols = tfs.GetService<VersionControlServer>();

            var workspace = versioncontrols.CreateWorkspace("workspacename", "workspaceowner");

            String ServerFolder = @"$/xxxx/xxxx";
            String LocalFolder = @"E:\test";

            WorkingFolder workfolder = new WorkingFolder(ServerFolder, LocalFolder);
            workspace.CreateMapping(workfolder);

            workspace.Get();
Cece Dong - MSFT
  • 29,631
  • 1
  • 24
  • 39
  • Thanks. Yes, I was able to perform that using PS (please review the updated question). However, when it is executed first time, it downloads a lot of files and takes around 15 minutes. There is no feedback displayed until the operation is over. If we do same operation in Visual Studio, it shows a list of files that it is downloading. Is there a way to display that list in Powershell as it is downloading the files? – sohail Jun 21 '18 at 12:55
  • You may need to add a "Write-Host". For example: `Write-Host "TFS item to download:" $($item.ServerItem) -ForegroundColor Blu` Check the answer in this case: https://stackoverflow.com/questions/23739499/copy-files-from-tfs-versioncontrol-to-directory-with-powershell – Cece Dong - MSFT Jun 22 '18 at 10:04
  • The example is for downloadfile method. I am using Get method. How can I can write-host in Get method. – sohail Jun 22 '18 at 16:41
  • I've tested on my side, it's not able to see the get log with workspace.get. You may try with [tf command](https://learn.microsoft.com/en-us/vsts/tfvc/get-command?view=vsts) in your script to see whether you can see the log. – Cece Dong - MSFT Jun 27 '18 at 06:58
  • Is there a way to preserve time stamps from server using Get instead of current timestamp? – sohail Jun 29 '18 at 21:01
0

I was able to get the code using workspace option using the code sample below.

if ( (Get-PSSnapin -Name Microsoft.TeamFoundation.PowerShell -ErrorAction SilentlyContinue) -eq $null ) {
     Add-PSSnapin Microsoft.TeamFoundation.PowerShell
    }
    $securePass = ConvertTo-SecureString -AsPlainText -Force -String "mypassword"
    $uri = "https://myserver"
    $cred =  New-Object System.Management.Automation.PSCredential("myusername",$securePass)

$tfsServer = Get-TfsServer -Name $uri -Credential $cred
$structureService = $tfsServer.GetService("Microsoft.TeamFoundation.Server.ICommonStructureService")
$versionControlService = $tfsServer.GetService("Microsoft.TeamFoundation.VersionControl.Client.VersionControlServer")

$workSpaceName = $env:USERNAME + "-IMAG"

$localTFSWorkspace="c:\tfswspace"
$serverRoot = "$/myproject"
$pathsToDownload=[System.Collections.ArrayList]@()
$pathsToDownload.add("$/myproject/path1")
$pathsToDownload.add("$/myproject/path2")

$testPath = $($pathsToDownload[0].Replace($serverRoot,$localTFSWorkspace)).replace("/","\")

if (!(Test-Path $localTFSWorkspace -PathType Container)) {
  New-Item $localTFSWorkspace -ItemType Directory -Force
}

# Check if workspace already exists
     $workspace = $versionControlService.TryGetWorkspace($testPath)
     if ($workspace -eq $null) {
       #Workspace doesn't exist, we need to create one
       $workspace = $versionControlService.CreateWorkspace($workSpaceName,$cred.UserName, 'Workspace for Repository Sync')
     }

 #create mappings if these don't exist 
 foreach ($serverPath in $pathsToDownload) {
      $localPath = $($serverPath.Replace($serverRoot,$localTFSWorkspace)).replace("/","\")
      if (!(Test-Path $localPath -PathType Container)) {
         New-Item $localPath -ItemType Directory -Force
      }

    $workingFolder =  $workspace.TryGetWorkingFolderForServerItem($serverPath)

    if ($workingFolder -eq $null) {
      #create mapping here
      $workspace.Map($serverPath,$localPath)
    }
 }
 # Now mappings are done -- get items now
 $workspace.get()
sohail
  • 589
  • 1
  • 11
  • 26