First, a disclaimer: some of the things I describe in this post involve directly changing data in TFS’s databases. This isn’t usually recommended. Also, I was using TFS 2005. I’m not sure how much of this works on other versions.

A couple of weeks ago, while a coworker and I were deploying some new work item types, we created a field as an Integer that we realized needed to be a String. Changing the field’s type in the work item type definition results in the error “TF26038: Field type for My.Custom.Field does not match the existing type. It was Integer, but now is String.”

A quick look on google made it look like there wasn’t a good way to change the field type. I tried updating the Fields table in the database. This looked like it worked, but the actual fields on the work item type didn’t get updated, so we couldn’t only insert integer data. Non-integers failed with a strange error; for me, it said that the TFS server was offline. So the quick-fix in the database was obviously a bad fix.

So now the question was, how do we preserve the existing data in a new field?

Step 1 was to create a new field. The first attempt used the same friendly name, but a different RefName. Team Foundation objected: “Error: Field name ‘Custom Field’ is used by the field ‘My.Custom.Field’, so it cannot be used by the field ‘My.Custom.Field2′. So now, I’m thinking about trying to change the type of the column in the database. Theoretically, SQL Server can do this. If I’d had some planned downtime, I would probably have gone this route. I wasn’t sure that changing the database column type would be the last time I had a problem, so I wanted to use TF’s tools to do most of the work.

Also, around this time, I (re)discovered the witfields tool.

Here’s what I did.

  1. Created a new field, My.Custom.Field.Temp, as a String, and added it to the work item type with witimport.
  2. Looked in the Fields database table for the FldId of the old and new fields. For example, the old field had FldId 10138 and the new one was 10146.
  3. In each of WorkItemsAre, WorkItemsWere, and WorkItemsLatest, I copied everything from Fld10138 to Fld10146.
  4. I used witimport to remove the old My.Custom.Field field from the work item type.
  5. I used witfields to delete the old field from the database.
  6. I updated the work item type with a new field, My.Custom.Field.
  7. I repeated steps 2 and 3 to get the data from the new field to the fixed old field.
  8. I used witimport to remove the temp field from the work item.
  9. I used witfields to remove the temp field from the database.

It looks like this worked, and each step was pretty quick, and I wasn’t too worried about being interrupted between steps. That said, I make no claim that this will work for you. If you decide to try this, definitely read up on the TFS commands you’re using, and quadruple-check the SQL updates you run.

>>> git tfs clone http://team:8080/ $/sandbox sample_for_git_tfs
Initialized empty Git repository in d:/Projects/sample_for_git_tfs/.git/
C4949 = 84cfc504fd85a826ede8d852e3a5e75fc8952bd2
C69840 = fb10351a88a948d07b3eb6ef176b7e3bb1999ecb
C69841 = 764abfc9995945cafce2e23a1d2735aec8d288bd
C69842 = bb87001af0e93de25e13dab4602db2f84f4b1932
C69843 = 664705c06790fbd1da066833e9e81da24c88ea61
C86163 = 99d300275e87adedae65cd3c9a57c9f55c76eb94
C86164 = 2d10de0deb9fe96e22b14eb2e65e2f95ef55c1d6
C86165 = da9da4b79b2d9e16aa17f6a12317bfe3e29687de
C86167 = b3d7f12b3cf39b546cb19ddc37518be1080b86d8
C86170 = 5df88af2597f219a7b4ccf979ff90ff1cc7ed05b
C86209 = 46d814b83a5a75508e1584a8850d8bcb3c9d5a7d
>>> cd sample_for_git_tfs
>>> ls -R
.:
ANewDirectory

./ANewDirectory:
SomeDir

./ANewDirectory/SomeDir:
readme.txt sayhi.bat

>>> echo another line >> ANewDirectory/SomeDir/readme.txt
>>> git commit -a -m "Contributed using git-tfs."
[master a72b561] Contributed using git-tfs.
1 files changed, 1 insertions(+), 0 deletions(-)
>>> git tfs shelve GIT_TFS_SAMPLE
edit ANewDirectory/SomeDir/readme.txt
>>> mkdir ../sample_workspace
>>> cd ../sample_workspace
>>> tf workspace -new -noprompt sample_workspace
>>> tf unshelve -noprompt GIT_TFS_SAMPLE
sandbox\ANewDirectory\SomeDir:
Unshelving edit: readme.txt
>>> tf diff -noprompt
edit: d:\Projects\sample_workspace\sandbox\ANewDirectory\SomeDir\readme.txt
File: sandbox\ANewDirectory\SomeDir\readme.txt
===================================================================
--- sandbox\ANewDirectory\SomeDir\readme.txt;C69843 (server) 1/28/2010 1:02 PM
+++ sandbox\ANewDirectory\SomeDir\readme.txt (local) 1/28/2010 1:02 PM
@@ -4,3 +4,4 @@
----
This line was added for the second checkin.
This line was added after the parent directory was nested inside a new top-level-directory.
+another line
===================================================================

A few years ago, I read about natural distribution of leading digits in a set of natural numbers. The normal use of this rule is to differentiate between data sets with fabricated numbers and those with real numbers.

Today, I ended up with two sets of sixteen numbers, and was curious how the leading digits were spread out.

The first data set had values between 561 and 8224. The second had values between 39 and 576. The second set was a function of the first. The leading digit frequencies were as follows:

First Digit Frequency Benford’s Law
Set 1 Set 2 Total
1 3 8 11 34.4% 30.1%
2 5 4 9 28.1% 17.6%
3 4 1 5 15.6% 12.5%
4 0 0 0 0% 9.7%
5 1 2 3 9.4% 7.9%
6 0 1 1 3.1% 6.7%
7 1 0 1 3.1% 5.8%
8 1 0 1 3.1% 5.1%
9 1 0 1 3.1% 4.6%

I was impressed with how front-loaded that table is, and how closely it tracked with Benford’s law. There doesn’t seem to be any reason for “1″ or “2″ to be more common than, say, “4″ as a leading digit in either set, but in both cases “1″ and “2″ (22% of the leading digits) accounted for more than half of the leading digits (34% and 28% respectively).

I spoke last night at IndyALT.NET about source control systems. I had a pretty good time, and I’d like to thank the group for inviting me. For any who are interested, I put my slides on slideshare. The git repo graphs were generated by git-graph-objects.

I’m trying to get started with Ragel, and found a hello world example for ruby that got me past Go. Since the C# example was a bit different, I thought I’d share what I came up with.


 1 %%{
 2   machine hello;
 3   expr = ‘h’;
 4   main := expr @ { Console.Out.WriteLine("greetings!"); } ;
 5 }%%
 6
 7 using System;
 8
 9 namespace Mab.Test
10 {
11   public class Hello
12   {
13 %% write data;
14     public static void Main(string [] args)
15     {
16       foreach(var arg in args)
17       {
18         Console.WriteLine("***** " + arg + " ******");
19         Run(arg);
20       }
21     }
22
23     private static void Run(String data)
24     {
25       int cs;
26       int p = 0;
27       int pe = data.Length;
28       // init:
29       %% write init;
30       // exec:
31       %% write exec;
32     }
33   }
34 }

To see how it works, save the following code to Hello.rl and run these commands:

ragel -A Hello.rl
csc /t:exe /out:test.exe Hello.cs
test.exe a h z

I’m pretty impressed with Hudson. It’s very easy to administer, and has had a plugin available for everything I’ve wanted to do so far. Despite being from outside of the Windows world, it’s pretty easy to get VS stuff running in it.

However, one thing that would make it better is the ability to watch several source control sources, similar to CCNET’s Multi Source Control plugin. I’m not up for writing an entire plugin. (The one time I tried to do that, I was overwhelmed by the difficulty of creating a plugin from scratch for hudson as compared to CCNET.)

So I patched the Hudson TFS plugin to support multiple paths. So now, in addition to being able to use a simple source control path (e.g. $/myproject/some/directory) that’s mapped to the job’s workspace, you can map several TFS paths into hudson’s workspace (e.g. $/path1 : path1 ; $/path2/a/b/c/d : c\d). You can get this from the dev.java.net issue, github, or download a snapshot build I created.

Ruby objects have a nice-for-debugging method called inspect. Dumping objects isn’t something that I only ever want to do in Ruby. In fact, for the bug I’m looking at right now, I think it’s related to a settings object that doesn’t get updated, and the easiest way to watch the settings object is to Inspect().

So, I present, Inspect.cs (and tests).

I’m sure I’m not the first person to discover this, but it has come up twice in the past few days (once for the code review tool I maintain, and the other for a project-to-be-named later), so I thought I’d toss it out as interesting.

Let’s say you’re writing an app that needs to process TF changesets. You might pull some changesets with a history query …

changesets = VersionControl.QueryHistory("$/Project", VersionSpec.Latest, 0, RecursionType.Full, null, 1, VersionSpec.Latest, int.MaxValue, true, true, true);

… or you might grab the changeset directly …

changeset = VersionControl.GetChangeset(12345);

Regardless, you now have a shiny Changeset object that will tell you all about what happened at that point in time. So you start looking through the changes in the changeset, and you see some Adds, some Renames, some Deletes, some Rename | Edits, etc. So now you can present a list to the user with the things that got updated, one item per change in the changeset. Except…

I found that I need to pay attention to more than just the change type. For example, if you have a directory with a bunch of deleted files, and you move the directory, the changeset for the directory move will include a change with ChangeType.Rename for every file under that directory, including deleted files! The only indication you’ll get is that the change.Item will have a DeletionId value of something other than 0.

I saw this issue on the code review tool when a user asked why an apparent rename was giving a diff of two empty files. The answer was that the file had been deleted before the rename, so it didn’t really exist on either side of the rename. It was just dragged along. So, I should have checked DeletionId!

Also because of this behavior, the project-to-be-named-later ended up adding files to a directory that weren’t actually supposed to be there. Before I write the new version of files in the changeset, I check DeletionId… if it’s not 0, then I ignore the file.

So, if you’re consuming TF changesets, and you want to ignore files that were deleted in the past, be sure to check the change.Item.DeletionId property!

In my earlier post on getting a Ubuntu VM running on VPC, I neglected one important and usually insignificant detail: how to disable udev for the VM’s NICs.

The problem with udev is that it effectively disables the VM’s NIC under certain conditions. To understand why, it helps to make some observations about the various modules involved.

Udev is the device manager for Linux 2.6 systems. It is responsible for populating /dev with only the devices that are present. It provides notification to other software when devices are changed. And, most importantly for this blog post, it provides “persistent naming for devices when they move around the device tree.” So, if you move your USB cabling and routers around, your machine should still be able to identify, say, your printer. Or if you add a new NIC and your system detects it before your existing eth0, your old NIC will still be eth0 and your new one will be eth1. This is pretty awesome, and it helps resolve some of the problems I remember having with old PCs, trying to guess which device name maps to which physical device.

On Hyper-V (or Virtual Server, or Virtual PC), NICs are usually virtual, so they get assigned virtual MAC addresses. The default configuration is to let Windows pick the MAC address. These MAC addresses are typically stable, unless you do something like recreate the NIC or move the VM to a new host.

The scenario that this blog post is concerned about is this: you set up a VM on one host, and then you move it to another host. You might move it for any number of reasons: you set it up on VPC and want to permanently host it on Hyper-V, or your Hyper-V hosting situation changes and you need to migrate VMs around, or whatever. Following the move, your VM is no longer on the network! Bad news.

So, when you’re setting up your VM, you need to consider how udev will interact with Hyper-V. Some possible solutions include assigning a static MAC address to the NIC, or making udev smart enough to realize that if the old NIC went away and a new one is in its place, that it should use the new one in place of the old one. My solution is to make udev ignore eth*, since I usually am configuring a Linux VM to run some particular service, and I will not be changing the network configuration in any significant way. My solution is likely to be affected by upgrades because it modifies files that are generated by scripts.

And now, to the point of this post: how to make udev ignore your eth* devices:

  1. Open /etc/udev/rules.d/75-persistent-net-generator.rules in your favorite text editor.
  2. On line 21, where it says KERNEL!="eth*|ath*… remove eth*|. For example, on my system, I start with
    KERNEL!="eth*|ath*|wlan*[0-9]|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end"
    and end up with
    KERNEL!="ath*|wlan*[0-9]|ra*|sta*|ctc*|lcs*|hsi*", GOTO="persistent_net_generator_end"
  3. If udev has already renamed your nic, you’ll also need to open /etc/udev/rules.d/70-persistent-net.rules and remove any eth* entries you find.

This should work on any Linux with udev (kernel >= 2.6). Line 21 is the right one to change on Ubuntu 8.10 and 8.04.2 systems that I’ve configured this way.

Every once in a while, I need to merge something using clearcase’s command-line cleartool. It always takes me a few minutes to remember how exactly to tell Clearcase to do what I want, so I’m writing it down in hopes that I remember to look here next time I need this.

So, I ran “cleartool ci”, and got an error about the version I was trying to check in not having the right parent version. To fix, I ran

cleartool merge -to my_file.txt my_file.txt@@/main/LATEST

After that, my checkin worked just fine.

Next Page »