Get-TfsItemProperty is the bridge between server and local objects

Most of the real-world examples of the TFS powershell tool revolve around querying (and possibly updating) the state of the server. 

And rightfully so: most of the operations you want to do locally are already possible without any special tools.  The “Powershell snap-in system” we know and love today was created to help IT admins manage their servers – even today, only a brave few developers have completely replaced cmd.exe at their daily workstation.

Nevertheless, we shouldn’t be satisfied to have our TFS cmdlets live in their own little world.  Luckily, all the info we could ever want about a TFS version controlled item – including its local state – comes packaged in the ExtendedItem object.  Even more fortuitously, if you revisit the exploration of QualifiedItemSpec with an eye for detail, you’ll notice that one overload of ToQualifiedItem() is not like the others.*  In the constructor where ExtendedItem decays into QualifiedItem, the properties saved into the resulting tuple include LocalItem and VersionLocal instead of more familiar ones like ServerItem.  This is not a coincidence :) 

Let’s sum up what we know so far.  Native TFS objects are funneled down the pipeline by stripping them to bare essentials about the items they represent (QIs); these serve as a common interface between cmdlets.  When an ExtendedItem is stripped down, the next cmdlet will use the local filename & local version info as its parameters.  Get-TfsItemProperty is the cmdlet that returns ExtendedItems.  Capiche?

Time to put our newfound knowledge into action.  If you’ve also been following the last several posts on file manipulation in powershell, these examples will hopefully inspire an “aha!” moment.

001
002
003
004
005
# find all C# makefiles with HintPath elements, check them out, and open them in my text editor
tfprop $/project/*csproj -r | get-fullpath | ss hintpath | tfedit -passthru | ue

# find my shelvesets related to powershell, unshelve them, and open every file in the ISE
get-tfsshelveset | ? { $_.name -like "*powershell*" } | tfunshelve | tfprop | ise

[Note that I have get-tfsitemproperty aliased to ‘tfprop’ rather than ‘tfproperties’ as it appears in a default install of the Power Tools.]

Are we having fun yet?!

So far we’ve only really exercised the filename part of the QI.  Equally interesting things happen when you take advantage of the special ability that Get-TfsItemProperty offers to manipulate the local version.

001
002
003
004
005
006
007
008
009
010
011
012
013
014
015
016
017
018
019
020
021
# Force unshelve to pend changes against your current workspace ("have") version. Ordinarily,
# unshelve rolls the affected files back to the version you had at the time you shelved the
# changes.
# Pro: this ensures your workspace stays in a consistent, buildable state
# Con: you may need to resolve content and/or namespace conflicts
function unpack
{
    [CmdletBinding()]
    param (
        [parameter(Mandatory=$True, Position=0, ValueFromPipeline=$True)]
        [Microsoft.TeamFoundation.PowerTools.Powershell.ShelvesetSpec]$ss
    )
   
    process
    {
        $currentWorkspaceVersions = tfstatus -shelveset $ss | tfprop
        restore-tfsshelveset $ss | out-null
        $currentWorkspaceVersions | tfget
        tf resolve /prompt
    }
}

[Microsofties: think bbpack / jjpack]

Opens up a whole new world of TFVC scripting mania, wouldn’t ya say?

*I’ll admit the one for GettingEventArgs is a little funky as well.  Here, the choice of properties to expose came down to UX / gut feel.  TargetLocalItem is usually what you want to see; it tends to be the “final result” of mainline operations, surviving things like Rename and Undo fairly well.  But it’s not always the best choice.  Obviously when it’s null we need a backup; good old ServerItem will do.  It’s [almost always] guaranteed to be present, but it’s  not always perfect either – now you have the occasional strange appearance of server paths in your list of local files being touched by the Update.  Finding the right info gets really tricky really fast: was TargetLocalItem null because of a pending Delete, because it’s cloaked or otherwise excluded from our workspace, or because we’re undoing a pending Branch or Merge?  That’s just one example.  The logic behind which name tf.exe displays in which scenario is actually one of the more complex pieces of the app.  With powershell we can accept “good enough” since the object model lets you manipulate things to your heart’s content.

Leave a Reply