Thursday, November 12, 2009

TFS Destroy – friend or foe?

While everyone else is blogging about VS 2010 Beta 2, I thought it still may be worth publishing this post that talks about VS 2008 behavior (yes, the old release ;).

One of the features missing in VS 2005 and added in VS 2008 was destroy command; people wanted to get rid of the source control artifacts for good and were unable to do so.

Interestingly, once the command become available it did not become too popular. Come think of it, there are very few cases where one can afford permanently deleting data; after all, source code is most valuable asset any software company has.

But should you decide on using destroy command, there are few important points to keep in mind:

1. Before executing destroy command, you might consider deleting item first. Leaving the item in “quarantine” while deleted for a week or so makes sure nobody uses the item (for example, as part of automated build) will miss it once it is completely gone. And once you are ready to delete it, use /preview switch to double check what files you are going to permanently wiped out

2. If you use destroy, destroy all versions of the item and do not fall for /keephistory option (with or without /stopat flag):

tf destroy $/Project/FolderOldName;C123 /stopat:C156 /keephistory

This option would destroy all (or some as in example above) versions of the item while retaining item’s history. It may be tempting to clean up database from old revisions leaving the history intact; the problem with this usage is that you will not be able to distinguish the revisions deleted when viewing the item’s history. That may lead to the following message when trying to view seemingly valid history:

3. When you execute destroy command, data is not deleted from DB immediately. There is TFSVersionControl Administration job running on TFS Data Tier at scheduled interval that takes care of actual DB purging. You can trigger the job run immediately by using /startcleanup option (or running on SQL Server manually). The job does not take care of cleaning up the warehouse,  it will get updated at warehouse processing scheduled intervals.

On a personal note, my usage of destroy was limited to removing sample & test TFS projects content; I never was able to get enough justification to permanently delete source code, however unused it may be. But your mileage may differ – if you do decide to get into destruction business, there are couple of very useful resources on TFS destroy that are not immediately discoverable through simple search; summary MSDN article and screencast How Do I: Use the TF Destroy Command in Visual Studio Team System 2008? by Richard Hundhausen.

Mirror from my MSDN blog

Friday, April 24, 2009

Branching off renamed trunk

Recently I got asked a small but unobvious branching question. Suppose you have a folder named FolderName, and for some reason you have renamed it to NewFolderName. All is well, but now you decided you want to create a branch from that folder, and to branch from the version prior to renaming.

Due to the reasons detailed in my older post, you will not be able to use branching UI for the operation. The only way to achieve that is to use tf command-line client branch command where you will explicitly specify version you branch from and the folder name at that revision

tf branch /Project/FolderName /Project/Branch /version:C123

Typical mistake people make is to use current item name, NewFolderName instead of the name that existed in the past(i.e. FolderName at the time of changeset 123).

Mirrored from MSDN blog

Saturday, March 28, 2009

TFS Administrator chores – dealing with the space offender

These are the days of cheap storage - but even the cheap storage may run out. And running Team Foundation Server storing artifacts in its (multiple) databases may use up your space rack faster than you might have expected (and if you want to know what to expect, refer to this classical post by Buck Hodges on database size calculations).

If that happens, the most probable culprit is version control database (TfsVersionControl) – in other words, all these files that people check in into version control. The size of the file matters because TFS stores difference only for each new revision of “small” files but for the “large” files every new revision gets full-blown copy (by default TFS considers the file to be large if it is over 16 Mb - read more on that topic in my previous post).

There are several ways of making sure that your users do not fill up your version control with memory dumps, images of installation CDs and such. Mind you – I am not saying that large files do not belong to version control; I am saying that the addition of large files should be a) conscious step and b) “revisionless” (i.e. with no versioning).

Myself, I have been always ambivalent about storing large binary thingies in source control – on one hand, you get all content in one place (which is mighty convenient for builds etc.), on the other hand, many users will probably check in the content that does not belong in source control. So here is my hit list of  measures to deal with large files in version control

  • Educate your user – make sure your average user understands that DVD ISO added to version control ends up being transmitted and stored in the database; perhaps what the user is looking for is file server, not version control
  • Make user aware of his actions – it is possible to write check-in policy that would alert the user at the time of check-in, that the files being checked in are large and perhaps should not be in version control. And then, even if the user decides to override the policy you may run report on policy overrides
  • Monitor your storage – if high level prevention and low level prevention fail, you can query the database to identify the offending files. The query below (with usual caveats – it is AS IS etc.) will give you a list of large files in the database (it will not take into account the summary size of all versions, only the latest version):
DECLARE @LargeFile int;
-- return files larger than 16 Mb
SET @LargeFile = 16 * 1024 * 1024; 
 
USE TfsVersionControl; –– use source control DB 
SELECT -- item path 
    Versions.ParentPath + Versions.ChildItem AS ItemPath,
    -- size of latest version in DB 
    Files.CompressedLength AS DatabaseSize, 
    -- size of original file
    Files.FileLength AS [Size], 
    -- whether item deleted
    CASE WHEN Versions.DeletionId = 0 THEN 0 
        ELSE 1 END AS Deleted 
FROM tbl_File Files, tbl_Version Versions
WHERE -- get item latest version 
    Versions.VersionTo = 2147483647 
    -- join to table with sizes
    AND Versions.FileId = Files.FileId 
    -- return only large files
    AND Files.CompressedLength > @LargeFile 
ORDER BY ItemPath;

I would be happy to hear your horror stories of the application of the above query; mine was nothing more than a bunch of ISO images checked in :)


Thanks for reviewing the query go to Chandru Ramakrishnan


Mirrored from MSDN blog

Tuesday, December 09, 2008

Do you get Git?

In the spirit of providing links instead of content, here is another link – comparison of Git with other source control systems popular in OSS world.

And if you do not know what Git is, it is well worth a look (perhaps that comparison will excite you enough to have a look).

Monday, October 20, 2008

Back To Future - Short File Names in TFS

Today I got reminded of something that I wanted to write about quite a while ago (when TFS 2005 was all the rage).

If you have legacy projects, chances are you have some DOS-style 8.3 file names. Not that there’s anything wrong with that... However, you might hit a problem when you start putting these files into TFS. That is, if you have certain file (say parameters.xml) under source control, and have another file with short file name (i.e. parame~1.xml) under the same folder – then you have a problem, since TFS 2005 will become confused when you get files from that folder (that’s how I know about these SF names – in my case there was a file and a subfolder with the same short name). There is wonderfully explicit knowledge base support article (KB 947649) on the topic.

Interesting fact that I did not know before today is that in TFS 2008 you will not have that problem anymore, since you will not be able to add SFN files. However, the data that already is in the repository can still exhibit the weird behavior.

Now, you are going to say that the scenario described above is extremely rare occasion. Yes, it is and this is a good news. But if you have some legacy DOS 8.3 file names stored up in VSS, conversion from VSS to TFS 2008 will not work now, since the short file names are not supported anymore. There is workaround for that described in another wonderfully explicit knowledge base support article (KB 951195).

And finally, there is a whole lot of useful (if a little bit outdated) information on Microsoft Support [make a note to check there often for 2008 stuff].

Saturday, August 30, 2008

Getting Latest in VS2008 (addendum)

One thing I did not describe my previous post is the actual user experience in VS IDE. So as a footnote, it is worth to note that when get latest is performed as part of check out (due to either VS IDE or Team Project settings), you will be presented with the following dialog:

The good part of it is that now you are aware of what is happening; the bad part is that you cannot cancel and having additional dialog pop up is somewhat disruptive.

Friday, August 29, 2008

Two flavours of “Get Latest On Check-out” in VS 2008

While in TFS/VS 2005 there was no option to get latest on check out, in 2008 version there is not one but two different ways to configure that feature (Disclaimer: I am not endorsing getting latest on check out but just trying to reach sort of closure of TFS/VS 2008 featureset).

First option is to enable this setting per workstation, using Visual Studio TFS source control provider settings (available through “Tools->Options” menu):

This option is fully controlled by the user in his environment, and does not affect other users in any way.

Second option is to configure “Get Latest On Check-Out” per Team Project (using “Team->Team Project Settings->Source Control” menu):

Since the option is set for the Team Project, it can be enabled by the administrator and will affect all users working with the project files.

Thus in VS 2008 one has a choice of having “Get Latest On Check-out” option enabled either for all developers working at the project (using Team Project settings) or a developer can enable that option for himself (by using VS Source Control provider settings).

From the “best practices” standpoint, I’d like to note once again that getting latest on check-out is very disruptive, evil and outdated practice. While I am highlighting those features, I am neither a fan or a user of those.

Consider the following typical scenario – you have checked out file in VS project. Since get latest is performed, you just got yourself the latest version of that file. If that latest version contains changes that are incompatible with the other files’ versions in your workspace (say, dependencies on new interfaces that are not yet in your workspace), then you are screwed. That is, to make things tick now you will have to get latest versions of all relevant files in your workspace (hello and welcome back, VSS!).

And besides, Team Project setting somewhat smells of dictatorship, since it will force everyone on team to conform to VSS-like mode of operation. Not a good thing in today’s flexible world.

Related posts:
- Get latest on check-out in TFS 2008
- (Not) getting latest on check out – a bug?

Sunday, August 24, 2008

Editing files in VS2008 SP1

As a follow up to a previous post on file handling in VS2005/VS2008, I thought it is worth to mention another big difference coming as part of VS2008 SP1.

Pre-SP1, if you edit a source controlled file that is not a part of currently loaded solution, VS will not prompt you to check out this file (and will not check it out automatically, if that is what you configuration settings).

However, if you work with files in Source Control Explorer in SP1, your experience will be pretty much identical to Solution Explorer experience, even if the file is not part of the current solution. That is, editing file will check it out the file (if that is your VS settings – Source Control Explorer behavior is defined by the same set of settings as Solution Explorer; namely, “Tools->Options->Source Control->Environment” tab).

Together with the change mentioned in my previous post, this small tune-up should significantly decrease the number of local changes that never made it up to the repository (that is, if you are tweaking files locally and modify them out of solution context).

Monday, August 18, 2008

Editing writable files in VS2008

One interesting change of TFS source control provider behavior in Visual Studio 2008 is the handling of writable files.

In VS2005, if you make certain source controlled file writable locally, editing it will not cause check out (you will have to explicitly check the file out); of course that assumes that VS is set either to explicitly or automatically check out file on edit or save.

With the same VS settings in VS2008, you will still be prompted to check out the file, even if it is writable. TFS source control provider tracks all controlled files in the solution, regardless of their read-only status.

The rational behind this change is clear – changing files locally without referencing source control repository may lead to changes never propagating to repository at all (and thus problems of “I have changed the file and it was not checked in” kind may arise).

However, there are some interesting problems you might encounter with that new behavior. Let’s say certain file is locked by someone else (with exclusive check-out lock). In VS2005 you would make this file writable locally, and VS would be happy to let you edit the file. In VS2008, however, VS will first check the status of the file in source control, and seeing that it is locked won’t allow you to edit this file.

There is workaround to this (aside from not ever messing up with local files modifications :); “Tools->Options->Source Control->Environment” tab in Visual Studio may be used to tweak the options. Setting checked-in items “Editing” behavior to “Do nothing” will allow you to edit file regardless of its status in source control (setting “Saving” behavior to “Save As” will allow you to save it). But keep in mind that this setting is probably very unproductive choice for day-to-day work.

Thanks for this tip go to Richard Berg.

Monday, August 04, 2008

Check in your stuff now or else!

How often do you check in? Do you have organization-wide policy mandating the maximum check in period? And should you care at all about those pending check ins?

As the general wisdom has it, you should check in “often”. In the past, I myself was quick to cite that maxima, but thanks to several discussions around this issue I have been swayed and now believe that "often" is not a right qualifier' rather that checking in often, one should check in when “ready”. Indeed, when you think about it, committing new revision of code to the repository is (or should be) driven by the code readiness rather than by the arbitrary time period.

But that raises another question – what is code “readiness”? While “often” is easy to define (“Thou shalt check in code once in a fortnight!”), ready to check in code is trickier and depends on your company practices. Code readiness may include one or more of the following:

  • Code compiles (poor man testing)
  • Code compiles and all code dependencies compile (poor man integration testing)
  • Code satisfies (static) code analysis rules
  • Code passes unit tests
  • Code passes integration tests
  • Code passes code review
  • Code & its unit tests pass unit test review

It is at that stage that many decide to go back to “check in often” principle, since making sure that the code being checked in is ready code is much more complex than making sure the code is checked in every three days.

However, if you are unable or unwilling to define code check in criteria, that says a lot of (bad) things about your development process. Basically, check in should be used for committing snapshot of the development; but not just any snapshot. If the only thing definite about the code revisions checked in is that it is checked in with daily intervals, the usefulness of your source code control repository is very limited - try to rollback or go to certain state of the repository in the past, when the revision is synonymous with the date. So establishing at least elementary criteria for check in (read "code compiles") is a good start and is preferable on the face of any time based criteria.

One other argument against "check-in-when-ready" and in favour of "check-in-often" is backing up the code revisions (“when your workstation crashes, we have the copy in the repository”). With the modern SCM solutions the problem is easily solvable; for example, TFS provides shelving functionality that ought to make “check in for backup” thing of the past.

And here I am going to contradict myself a bit and say that even when you set the check in criteria, having “check in often” policy is still valuable (with “often” set to 5+ days) – but only as additional measure. That way you may discover long lasting development effort (“it is still not ready, we need another week, since if it is checked in now, everything will break” sort of effort), the effort that should not be a single check in unit anyway (probably TFS branch construct is the one to use in such cases). By the way, another interesting outcome of that policy may be to discover that granularity of development tasks assigned is too coarse. In that case working on breaking up development into smaller pieces may mitigate the check ins problem.

So to conclude my somewhat rambling post, here is my “pending check ins manifesto”

  1. Check in when the code is ready to check in. Establish you criteria for “code readiness”
  2. If you are not checking in, back up the code you work on daily
  3. Enforce the policy “check in once in a X”, but only as additional measure. Make sure nobody is forced to check in; try to understand the original cause

But hey, what about about the initial question – whether one should care about those pending check-ins at all? Hopefully, the discussion above makes it somewhat clearer, I believe that yes, one should care about check-ins left floating around. When somebody checks out the file(s), he essentially makes the statement “I am about to modify these files”. From that point there are two ways - that person may decide otherwise and do not commit any changes (undo) or make and commit the changes. So by using pending check-ins indication you have very simple and yet powerful tool to monitor the state of software development and to improve the process if needed.

And it is a shame that sometimes simplistic view of Configuration Management concepts prevails and makes lots of people unhappy. Let us deliberate before enforcing any policy - perhaps we could do better?

Wednesday, July 09, 2008

What TFS SDK samples do you need?

If by any chance you read my blog and do not read Brian Harry’s blog (probably it is other way around), make sure you download and preview new TFS extensibility sample and provide him feedback what is missing from this sample and what samples you would like to have in general. The sample is hot from the oven, and expands on the details from classic Ed Hintz post.

Personally, I’d be interested to know how many people would like to extend Team Explorer and what extensibility scenarios you are looking to implement in general.

Thursday, June 26, 2008

Advanced workspace cloaking in TFS 2008

I stand corrected (thanks Richard!); there are two new workspace mapping features available in TFS 2008: wildcard mapping and root level cloaking.

The advanced cloaking schema supports scenario where the high level folder is cloaked, and some of the sub-folders are explicitly mapped. It is useful in the scenario where you need contents of one folder out of say, hundred (alternative would be to map high level folder and cloak ninety-nine sub-folders).

The application of this mapping schema is somewhat tricky (and initially it got me thinking that the feature ended up outside of the RTM version) – the cloaked folder must have higher level folder mapped.

That is the following will not work (displaying error “The item XXX may not be cloaked because it does not have mapped parent”):

tf workfold /cloak $/Project/Src /workspace:Test
tf workfold /map $/Project/Src/Bin c:\Project\Src\Bin /workspace:Test

To make it work you need to have the following setup:

tf workfold /map $/Project c:\Project /workspace:Test
tf workfold /cloak $/Project/Src /workspace:Test
tf workfold /map $/Project/Src/Bin c:\Project\Src\Bin /workspace:Test

In this last example, recursive get latest in Test will get all files and sub-folders under $/Project, except for $/Project/Src sub-folder. This sub-folder will be cloaked and only files under $/Project/Src/Bin will be retrieved.

Wednesday, June 25, 2008

Wildcard workspace mapping syntax in TFS 2008

Just a footnote on workspace mappings in TFS 2008 – while initially there were couple of features planned for Orcas release, only one feature has made it into RTM. Correction: both features are available, see this post discussing the second feature.

In TFS 2008 it is possible to map only one level of the folder hierarchy using wildcard syntax

tf workfold /map $/Project/Source/* c:\src\Project /workspace:SampleWorkspace

The mapping above will map only one level of files and folders under $/Project/Source and will not retrieve files contained in sub-folders.

This syntax is nice to have, though I am not sure how useful it is. The same effect can be achieved by using cloaking on sub-folders (though of course wildcard is much more elegant way).

Tuesday, June 24, 2008

Automate workspace creation with MSBuild

And now as promised I shall script workspace creation. I will use MSBuild for this exercise (since it is way better looking and more convenient than batch files, and more standard than PowerShell); if you shied away from MSBuild previously may be it is time to get better acquainted :)

The task at hand is pretty simple: create new workspace and define set of mappings as specified per script; the user will pass workspace name and root folder for the mappings as an arguments to the script.

I will use tf command-line client with workspace and workfold commands for the purpose [while there are CreateWorkspace MSBuild tasks shipped as part of VSTS 2005/2008, they are not suitable for the purpose as the mappings cannot be specified as parameters to the task; 2005 version uses XML file and 2008 version uses Team Build database].

One thing to note before going into further details – when you use tf workspace /new to create a workspace, the default mapping is created (yes, no one asked for that, but tf does so nevertheless). That necessitates removal of that default root mapping as a first step after workspace creation.

So here goes the script (it is a longish one, but it is pretty self-descriptive):

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="CreateWorkspace"
  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Default values for the properties the script  uses -->
  <PropertyGroup>
    <RootPath></RootPath>
    <WorkspaceName></WorkspaceName>
    <Tf>tf</Tf>
  </PropertyGroup>
  <!-- Workspace mappings to create -->
  <!-- Customize at will -->
  <ItemGroup>
    <WorkspaceMapping Include="$/Project1/Source">
      <LocalPath>$(RootPath)Source</LocalPath>
    </WorkspaceMapping>
    <WorkspaceMapping Include="$/Infra/Bin">
      <LocalPath>$(RootPath)Common</LocalPath>
    </WorkspaceMapping>
  </ItemGroup>
  <!-- Main target -->
  <Target Name="CreateWorkspace">
    <!-- Checking input parameters -->
    <Error Condition="$(WorkspaceName) == ''" 
          Text="Please specify WorkspaceName property"/>
    <Error Condition="$(RootPath) == ''" 
          Text="Please specify RootPath property"/>
    <Error Condition="!HasTrailingSlash('$(RootPath)')" 
          Text="Please make sure RootPath is slash terminated"/>
    <!-- Create new workspace-->
    <Exec Command="$(Tf) workspace /new /noprompt 
                    &quot;$(WorkspaceName)&quot;" />
    <!-- Remove default mapping -->
    <Exec Command="$(Tf) workfold /unmap 
                  /workspace:&quot;$(WorkspaceName)&quot; $/"/>
    <!-- Create new mappings (uses MSBuild batching) -->
    <Exec Command="$(Tf) workfold 
                /map &quot;%(WorkspaceMapping.Identity)&quot; 
                    &quot;%(WorkspaceMapping.LocalPath)&quot; 
                /workspace:&quot;$(WorkspaceName)&quot;"/>
    <!-- Great success! -->
    <Message Text="Workspace '$(WorkspaceName)' created sucessfully"/>
    <!-- List created mappings -->
    <Exec Command="$(Tf) workfold 
                /workspace:&quot;$(WorkspaceName)&quot;"/>
  </Target>
</Project>

To execute the script, fire up “VS 200x Command Prompt” and type the following:



msbuild CreateWorkspace.proj /p:WorkspaceName=VistaDevt /p:RootPath=c:\Vista\


Only caveat is that the folder the script located in cannot be mapped anywhere (as I noted above, tf workspace /new will try to map it and will fail).

If you are convinced that using script to create workspace is better than typing in the mappings, CreateWorkspace.proj is available for download here.

For completeness sake, it is worth mentioning /template argument of tf workspace command. If you want to copy somebody else’s workspace it is pretty attractive choice (or if you do not want to remember that somebody’s workspace name and AD user name, you can use Workspace Sidekick UI for the same purpose)

If you want to have “single-click” build, you may want to add to this script a) getting everything once workspace is created and b) building everything once get latest is finished. MSBuild makes both tasks very easy to achieve.

Workspace mappings best practices

Recently, I was asked if there are any best practices when defining workspace mapping. While I did not have any ready best practices, after some careful (late evening) thought I came up with several “best” practices that seemed worth sharing.

In ideal world, one would have a single mapping in a workspace. However in most projects single folder will either contain too many items or only part of the files required for specific project development/build.

In the former case, it is possible to cloak the redundant folders and thus optimize the time required to get files (cloaking folders excludes them and all their contents from get operation).

In the latter case, there is no escape but to map several folder in one workspace. Generally, there is no problem (other than managing multiple mappings) with this scenario; but the multiple mappings require certain degree of common sense, as illustrated in the example below. 

Consider the following folder structures:

 $/
Project1
Sources
Common
Bin
 $/
Project2
Sources
Bin

Let’s suppose the following set of mappings get created in a workspace:

 $/Project1/Sources -> c:\src\Project1 
$/Project2/Sources/Bin -> c:\src\Project1\Common\Bin

In generally, this setup is perfectly valid and works fine, but two folders hierarchies above have some common folders between them when mapped; namely, $/Project1/Sources/Common/Bin is implicitly mapped to the same local folder that $/Project2/Sources/Bin is explicitly mapped to.

Can you guess what will happen when “get latest” is performed on that workspace? Let me tell you what will happen (I failed the test myself) – only explicitly mapped folder contents will be retrieved from source control.

That said, here are my top five best practices on workspace mappings

1. Minimize number of mappings in single workspace. The more complexity is there, the more mistakes end users will make. Take that into account when you design state-of-the-art CM process that requires 50 workspace mappings per project

2. Map all related folder in single workspace; do not mix - create another workspace for every related subset of folders. Giving the workspaces descriptive names and making sure the mappings under workspace related to the name will save you a lot of time, especially when you switch between multiple projects daily

3. Cloak sparingly and only when performance is affected. The same complexity rule as in 1 applies. In my experience, cloaking is appropriate only in few cases, and when it is required it is usually a sign of certain problems with folders structure in the source control repository (not to mention that you will have to explain cloaking to end users)

4. When defining multiple mappings to create complex folder structure, make sure the folders with the same name from different mappings are not mapped to the same location. The discussion above is the basis for that rule - do not be afraid to create local hierarchy using mappings, but put some thought into it. I usually use that kind of mappings for external dependencies (and works pretty well), for example

 $\Project\Sources -> c:\src\Project 
$\ThirdParty\Component\1.1\Bin -> c:\src\Project\Bin\Component
$\Infrastructure\Ongoing\Bin -> c:\src\Project\Bin\Infrastructure

5. If you have more than three mappings per workspace, automate workspace creation.  This one is a topic in its own right and deserves a blog post – but in a nutshell, just consider how are you going to set up workspace mappings on your co-worker machine

Sunday, June 15, 2008

Configuring check-in policies

One interesting moment to be aware of in custom check-in policies implementation is how to implement the policy configuration. Overall, it is rather easy to implement custom check-in policies and there is a whole lot of documentation available (I especially like and highly recommend excellent article in MSDN Magazine by Brian Randell).

The configuration is initially performed when you define check-in policy; later, the configuration can be performed using “Edit” button in Source Control Settings dialog. Good example of configurable check-in policy is Code Analysis policy, since there user has to specify set of rules applicable in check-in policy evaluation.

When you create your custom policy, you override Edit method from IPolicyDefinition interface to display your own custom configuration dialog. But what do you do with the values specified by the user in the dialog (no matter whether it is elementary type or custom data structure)?

None of IPolicyXXX interfaces you implement in your custom policy provides any special methods for storage or retrieval of configuration. TFS serializes the instance of your custom check-in policy class, so to make sure your custom configuration is available, you need to expose it as the class member variable. Since you policy class  is marked as Serializable, the value will be persisted and available when the policy is evaluated at check-in time.

The mechanism is very simple; only caveat is that you must make sure that any internal variables that are not to be persisted are marked as NonSerializable. Here is small example:

[Serializable]
public class Policy : 
   Microsoft.TeamFoundation.VersionControl.Client.PolicyBase
{
    // Configuration to be serialized – may be used in Evaluate etc.
    private string _configuration;
 
    // Required for internal logic; do not serialize
    [NonSerialized]        
    private _DTE _dte;
 
    //...
}

Now, what if the policy configuration should be global and easily changeable? Then the mechanism above for all its simplicity is not very suitable (since once you change the configuration, the data gets serialized into bowels of TFS and is not readily available). Another problem you might face is the versioning of the policy – since the policy is defined on per Team Project basis, when you release a new version and it contains breaking changes to configuration, you will not only have to redeploy it on all client workstations, but also re-add it in all Team Projects.

What I did in such cases (and mind you, this is only one possible approach) is to make configuration file external to the policy implementation (meaning that policy will only consume the configuration but will not edit it). It may be tempting to make that configuration local, but that will actually make things worse (think about synchronizing configuration across all workstations). The easiest solution I have found so far is to put the configuration files somewhere on TFS server and make them accessible over http (using URL similar to “http://tfsserver01/configuration/policy_v1.xml”). Since in most cases TFS server URI is readily available from insides of check-in policy code, this location can be considered well-known. Of course, you would not want to put any security related data (passwords etc.) in that configuration file, but for most custom check-in policies that should not be a concern.

Using that approach, to change the configuration for your policy, you’d modify the external XML file. It may be one global file, or one per version of the policy or even one per Team Project (depending on your custom logic) – but since it will be external to serialized policy, the configuration is easily versioned and changeable without ever touching serialized data in TFS database.

Sunday, March 16, 2008

Branching to desired target path is easy

One would expect that creating a branch is pretty well trodden path today; that's why it was kind of shock to me that there are still people out there, that do not know how to do that efficiently.

The problem is simple – you want to branch folder "$/Project/Ongoing" to "$/Project/Branches/1.1". And here is the wrong way of doing it:

  1. Invoke Branch dialog by right-clicking the selected path and selecting "Branch…" menu
  2. Select the desired destination path for new branch in Target control
  3. Hit OK to perform branching


The problem is, you end up with new branch created at "$/Project/Branches/Ongoing" (as illustrated below).


And since what you actually wanted is not a branched folder named "Ongoing", but one named "1.1", you end up renaming newly created branch. Red light flashing - wrong answer!

Here is the correct walkthrough:

  1. Invoke Branch dialog by right-clicking the selected path and selecting "Branch…" menu
  2. Select the desired destination path for new branch in Target control
  3. Add desired name for the target branch folder to the specified path in Target ("$/Project/Branches/1.1")
  4. Hit OK to perform branching


And voila! The desired branch folder is created


Since the behavior does not seem to be very intuitive to me (and to some people I know), I thought it is worth blogging about.

Thursday, March 13, 2008

Label scope revealed

One not very well-known feature of Team Foundation version control label, is the ability to scope the label by the project (or, generally, by any folder path). By scoping I mean the following – the label is identified not only by name, but also by so-called "scope", where scope is the actual path within which the label name is unique.

Thus if your label scope is "$/", that means that you essentially have one global label; when scope is "$/Project1", the label name is unique within Project1.

Using UI (Source Control Explorer) you cannot fine tune the scope; generally, when you label set of items the scope will be defined according to the following rules

  • If the items being labeled belong to the same Team project (say Project1), the scope will be "$/Project1"
  • If the items labeled belong to several Team projects (say Project1 and Project2), the scope will be "$/"

The scope of the label can be viewed through command line client (for example, for label SampleLabel, tf labels SampleLabel /format:detailed) or using version control object model (as is done in Labels Sidekick).

However, when one uses command line to label files certain complications may occur if the label is not specified. Let's say that you are creating cross project label (that is label that contains set of files from Project1 and Project2). Since the label contains two sets of files that should be done through two calls to tf label

> tf label SampleLabel $/Project1/Source/* /recursive /server:TFS1
Created label SampleLabel@$/Project1

> tf label SampleLabel $/Project2/Source/* /recursive /server:TFS1
Created label SampleLabel@$/Project2

As you can see from the output, instead of creating one label with two sets of files, two separate labels were created: those labels have the same name but different scope (symbol at @ is used by tf to display the label's scope – thus the scope of the first label is $/Project1 and of the second one $/Project2).

To create one label, the scope needs to be explicitly specified (using @ syntax):

> tf label SampleLabel@$/ $/Project1/Source/* /recursive /server:TFS1
Created label SampleLabel@$/

> tf label SampleLabel@$/ $/Project2/Source/* /recursive /server:TFS1
Updated label SampleLabel@$/

Specifying explicit scope (common for the two sets of files – in our case, root folder $/) achieves the initial goal.

In my opinion, the scenario above would be the most common usage for the scope. While not recommended, the scope may be used to create same name labels within the same project as well (for example, specifying SampleLabel@$/Project1/Source will limit uniqueness of the label name to the folder $/Project1/Source). I attach "Not recommended" to this practice because of the relative invisibility of the scope in the SCE; not all users will be aware of the command line or third party tools and several labels with the same name in UI may be confusing.

Tuesday, November 06, 2007

Changeset - a unit or set of items?

I came across this post on forums, and thought it is worth to elaborate a bit.

In a nutshell - when you merge from path X to path Y, and specify certain changeset to merge, will all files in the changeset will be merged from X to Y? The answer: no, only files that both under X and in the changeset will be merged.

From one point of view, it is not very logical, since when you select a changeset you probably trying to merge all changes in it. On the other hand, you explicitly specify source and destination path and expect to merge items only under path, so if you look at changeset as a specification of versions to include in the process, rather than as a "container" unit of sorts, the results are logical.

Another changeset usage that I have found to be puzzling for some users, is specifying changeset for "Get specific version". Again, many people assume that changeset here behaves as a unit, and expect to get only files included in the changeset. But changeset instead behaves as mere date/time specification, and therefore versions relevant for that changeset timestamp will be retrieved for all files in the specified path.

Overall, those nuances are pretty important to be aware of (especially if explained to the users in advance).