views:

362

answers:

3

Does anyone have a handy powershell script that gets a set of files from TFS based on a modification date? I'd like to say "give me all the files in this folder (or subfolder) that were modified after X/Y/ZZZZ" and dump those files to a folder other than the folder they would normally go to. I know enough powershell to hack about and get this done, eventually, but I'm hoping to avoid that.

+3  A: 

Make sure you have the October 2008 Team Foundation Power Tools installed. It comes with a PowerShell snapin. You can run the PowerShell console file right from its startup group or you can execute Add-PSSnapin Microsoft.TeamFoundation.PowerShell. Then cd to your workspace and execute:

Get-TfsItemProperty . -r | Where {$_.CheckinDate -gt (Get-Date).AddDays(-30)} | 
    Format-Table CheckinDate,TargetServerItem -auto

CheckinDate           TargetServerItem 
-----------           ----------------
9/14/2009 1:29:23 PM  $/Foo/Trunk/Bar.sln                            
9/29/2009 5:08:26 PM  $/Foo/Trunk/Baz.sln

To dump that info to a dir:

Get-TfsItemProperty . -r | Where {$_.CheckinDate -gt (Get-Date).AddDays(-30)} | 
    Select TargetServerItem > c:\recentlyChangedFiles.txt

To copy those files to another dir (this assumes you have them pulled down locally into a workfolder):

Get-TfsItemProperty . -r | Where {$_.CheckinDate -gt (Get-Date).AddDays(-30)} | 
    CopyItem -Path $_.LocalItem -Destination C:\SomeDir -Whatif

Note this copies files into a flat folder structure. If you want to maintain the dir structure it is a bit more involved.

Keith Hill
It's a good start, and thanks. Now how do I dump that set of files to a directory of my choosing?
jcollum
Can I just say that having to user powershell to do this seems absurd. I've tried to do this through the VSTS interface and really can't get it to do what I want. I can't believe people pay for this thing.
jcollum
Purely TFS, you'll probably have to unmap your standard workspace, map a new one, do your extract, then reverse the workspace mapping. Or you could copy/rename the local folders, do the extract, copy/rename the freshly downloaded stuff, then copy/rename your original stuff back. Or you could remap the drive letter temporarily. "TF workspace -?" for details.
DaveE
+2  A: 

Using Get-TfsItemProperty like Keith doesn't just require a workspace for the file copies. It's the wrapper for GetExtendedItems(), the server query for local info most commonly seen in Source Control Explorer. By relying on the version info it reports, you assume the files themselves were downloaded (more generally: synchronized, in the case of renames & deletes) in the last 30 days. If the workspace is not up to date, you'll miss some files / give them out-of-date names / etc. It's also quite expensive as informational commands go.

Some alternative examples:

# 1
Get-TfsChildItem $/FilesYouWant -R | 
    ? { $_.CheckinDate -gt (Get-Date).AddDays(-30) } | 
    % { $_.DownloadFile(join-path C:\SomeDir (split-path $_.ServerItem -leaf)) }

# 2
Get-TfsItemHistory $/FilesYouWant -R -All -Version "D$((Get-Date).AddDays(-30).ToString('d'))~" |
    Select-TfsItem |
    Select -Unique -Expand Path |
    Sort |
    Out-File c:\RecentlyChanged.txt

The first is a straightforward adaptation of Keith's code, using a cheaper query and eliminating the workspace dependency. It's the best option if you know a high % of the items under that dir were modified recently.

The second option queries the changeset history directly. By letting the Where clause be computed in SQL instead of on the client, this can be an order of magnitude more efficient if a low % of the items were changed recently (as is often the case). However, it will lag the item-based queries if there are lots of large changesets returned, making the server's JOIN to grab the item properties expensive and forcing our client-side duplicate removal to do a lot of work.

[Yes, I know that having -Version require a string is not very Powershell-esque; mea culpa. You could create a DateVersionSpec with new-object and call its ToString(), but that's even more work.]

I didn't show every combination of API call + desired task. Goes without saying you can use #1 to generate the file list and #2 to (re)download by modifying the latter half of the pipeline. You can even combine that copy technique with the efficiency of Get-TfsItemHistory:

# 2b, with local-to-local copying
Get-TfsItemHistory $/FilesYouWant -R -All -Version "D$((Get-Date).AddDays(-30).ToString('d'))~" | 
    Select-TfsItem |
    Select -Unique -Expand Path |
    Get-TfsItemProperty | 
    Copy $_.LocalItem -Dest C:\SomeDir

It's true this makes a 2nd roundtrip to the server, but thanks to the initial query the GetExtendedItems() call will be scoped to the precise set of items we're interested in. And of course we remove any chance that download time becomes the bottleneck. This is likely the best solution of all when the # of changesets is small and the concerns I raised about Keith's workspace synchronization aren't relevant for whatever reason.

Can I just say that having to user powershell to do this seems absurd.

FWIW, I've been involved with TFS from inside & outside MS for 4.5yr and never seen this feature requested. If you could expand on what goal you're actually trying to accomplish, my guess is we could suggest a better way. Don't get me wrong, I wrote the Powershell extensions precisely to handle oddball scenarios like this. But frequently it's a job for another tool entirely, eg: Annotate, MSBuild, DB schema compare...

Richard Berg
In most SCMs that I've used, if I needed to do this I would have changed the working directory, sorted files by date, got files. In VSTS it's much harder to do this - especially since local files keep the retrieval date, not the server date. When I tried to do what I described I ran into a lot of roadblocks This was for a previous job, needed to put the files in a different SC directory for staging and deployment purposes. Not the best system for sure, but that's what the system was. Thanks for your answer, and a thorough one it was.
jcollum
A: 

Can u suggest me how to copy set of files from the server to a temporary folder on the server itself without having them copied to my local workspace..??

barry
barry this isn't an answer, it should be its own question
jcollum