Tuesday, July 31, 2007

Read-Write Local Trees in TFS

One rather annoying feature of TFS is that all files are marked read-only in your local tree until you checkout a file, at which point it becomes read-write. Its not hard to find other people who also find this very annoying.

I was hopeful MS would make read-write local trees an option in the Orcas release of TFS, but recently came across this post by Buck Hodges where he explains it won't be a feature in Orcas.

I was annoyed enough to add support for read-write files in version 0.5.0 of tf4mono.
To enable it just type
tf config File.ReadWrite true
Then from that point on tf get, tf undo, tf checkin, tf add and friends will do the right thing.

If you know what you're doing you can compile and run the tf4mono client on Windows, either against the MS TFS assemblies or with the tf4mono implementation of these assemblies.

I didn't want to lose this flexibility but to really do this right API level support would be required. Subsequently, I ended up teaching the tf4mono TFS assemblies to read the TF client XML config file. This is slightly distasteful but the standard assemblies already read the workspace cache XML file so I can live with it. I really didn't want to bolt this read-write stuff on top of the existing code, because that would have meant alot more stat's and chmod's under the hood.

Anyway to really make this work I also had to beef up the tf online command, but that will have to wait for another blog post.

Monday, July 16, 2007

Mono.GetOptions working overtime

When I started tf4mono, the command line Team Foundation client, I didn't really want to invent yet another option parsing library and it seemed like Mono.GetOptions would fit the bill.

Over time though I found myself adding more and more options to my Options.cs file, which really got out of hand. Finally, I found myself in a hotel room in Tulsa, Oklahoma with no internet access - a perfect time for code cleanups!

I ended up keeping global options in my Options.cs and moving all command specific options into each *Command.cs file, then I give both the global driver and the desired command a crack at parsing the arg array. With a little extra hackery to support command chaining, I ended up being able to do a huge code cleanup and really enhance the builtin tf help command.

While I was mucking around in there, I added a custom "Command" attribute to decorate my Command classes. Now each command in the tf client looks something like this:

[Command("history", "Display changelog history for specified file.")]
class HistoryCommand : Command
{
[Option("Recursive", "R", "recursive")]
private bool OptionRecursive = false;

[Option("Stop After", "", "stopafter")]
private int OptionStopAfter = -1;

With a little reflection, the tf client now has a reasonable tf help <cmd> feature, which gives you:
(/usr/local/src/tfs/tools/tf) tf help hist
history (alias hist)
Display changelog history for specified file.

Valid options:
/format:ARG Format "brief" or "detailed" (also /F:ARG)
/recursive Recursive (also /R)
/stopafter:ARG Stop After
/workspace:ARG Workspace name (also /W:ARG)
Now I just need to figure out a way to generate the asciidoc manpage source file from the builtin help and life will be golden.