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.
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.
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...