Disclaimer: I am not an investment advisor. When I describe my own trading activities, it is not intended as advice or solicitation of any kind.
Showing posts with label McHouse. Show all posts
Showing posts with label McHouse. Show all posts

02 August 2012

Lucky or Unlucky?

My wife and I recently bought a house halfway across the country, realizing a lifelong dream to live in the mountains. It was not without its scary moments, but the end result was amazing and fulfilling. Looking back, many of the facets of the transaction that looked like a deck stacked against us were, in fact, the best possible things that could have happened. Seldom do I treat directly with real Risk of Ruin, but today I will.

After a couple years of looking online and informally looking at neighborhoods and houses while vacationing in Colorado, D and I finally narrowed our search down to the foothills west of Fort Collins. My employer had agreed to let me work remotely, and after finishing up her PhD in Biology, D planned to look for either forestry or university work. Fort Collins is a good location for both of those things. For my part, I needed somewhere with a solid broadband connection so I could do my job, and D needed to be not-too-far from Fort Collins since she would probably be commuting. After our "drive-by stalking" adventures, we knew we wanted mountain property with lots of trees, but some open areas, too. And we wanted at least 10 acres of space that we could easily use without climbing gear -- too many houses in the mountains are perched on a cliff and crammed into a corner of their lot, leaving the residents unable to get to the rest of the property because of the terrain.

Broadband, short drive-time, trees, meadows, mountains, and lots of land. Not an easy combination of things to come by. We had several candidates favorited on real estate websites like Zillow and Redfin, but of course you can't buy real estate online. So in the early spring a high school friend of D's that had moved to Fort Collins recommended an agent, and home shopping began in earnest.

On our first trip to meet with the agent, he had about 10-15 properties for us to look at, and we visited them all (with one exception). Some of them were already on our favorites lists, and three in particular we recognized and were especially interested in. Of those three, we didn't get to see one because a tree fell on the current owner the day before the showing.

Another wasn't nearly as good in person as online -- there were a lot of things that looked like they could be maintenance nightmares lurking under the surface, especially the garage, which had excavated top-soil pressing against its back wall. That house will come up later as the Garage House. Almost none of the houses we looked at had high-speed internet.

The third house, though, was much better than the pictures. It was on a large square lot with both forests and meadows, it was right off a main road only about 20 minutes from town, and the owner had gone to the expense of a major internet upgrade that he put in for himself so that he and his wife could work effectively from home whenever they wished. We returned to Chicago, and after thinking about and talking about this property for a week or so, we ran out of reasons not to put an offer on it. And so we did.

A quick digression about regional weather patterns: there were record-low snowfalls in the Colorado Rockies over the 2011-2012 season. Since melting snowpack is where most of the moisture that feeds the region comes from, an extremely dry summer with very high fire danger was expected. Worse, the Rocky Mountain Pine Beetle epidemic is sweeping across the forests of northern and central Colorado, leaving millions of dead pine trees in its wake. As soon as it looked like we might be buying a property in the Colorado mountains, I started reading up on protecting my property from wildfires by removing the dead and infested trees. I knew that I was moving into a high-risk zone, and I wanted to be as prepared as possible. The volunteer fire department is located only about 1/3 mile away, which is very encouraging, but preventing fire is always preferable to fighting it. 

Our initial offer went to the sellers on May 12, 2012. On May 14, 2012, the Hewlett Gulch fire started on the north side of the Poudre Canyon, about 4 miles from the house. D and I watched carefully as it was fully contained in about a week, thankful that there was a river, two roads, and a ridge-top between the fire and the property we hoped would soon be ours. Fire fighters managed to bring the fire under control without losing a single structure. The fire was the largest that Larimer County had ever had, at 7,685 acres.

We returned to the property on June 2 to do the inspection. Encouraged by the results, we returned home elated, and put our own house on the market. On June 9th, we spent the day visiting with D's family, who threw her a going away party. There was much interest in our plans, and the Hewlett Gulch fire, so we happily chattered about how excited we were about the new property, and how relieved we were that the horrible fire hadn't gotten too close. During the party, D received a text from our Fort Collins friend -- she didn't read it until we got home that evening, and it went something like this: just thought you guys should know there's another fire burning near your new place. That fire, it turned out, was the High Park Fire.

Over the course of the next three days, the High Park Fire exploded in size, screaming across the entire Rist Canyon area. In the initial days of the fire, getting everyone to safety was the first priority. This meant that the limited resources already in the area couldn't do much to protect structures. Structure protection was much more effective later after the personnel fighting the fire increased from 250 up to its high somewhere near 2500. We watched in horror as the fire seemed to circle our new house, expanding first to the northeast, then the south, then back to the west again. Entire neighborhoods of houses were wiped out, for a total of 118 in the first few days. The Garage House survived, but the garage itself was destroyed and nearly all the trees on the property were reduced to blackened toothpicks. The sellers' agent also lost her house during those first few days. Eventually, the fire completed its circuit around and mostly filled a more-or-less square area 41,142 acres in size. Right in the center of that area was a little diagonal patch of unburned ground running from northwest to southeast along the road. In the middle of that patch was our house.

High Park Fire as of June 12
Bear in mind, we were still negotiating the contract when the fire broke out. Our earnest money was sitting in an escrow account protecting our offer, and things like the inspection and water potability test had been removing our excuses for canceling the contract. Of course, having a fire come through and destroy the house would certainly be a good excuse. But the earnest money was only 1% of the purchase price, so we could afford to walk away at any time if we decided we weren't comfortable, without any explanation. And that topic definitely came up in our conversations, especially the morning of June 13, when we saw that the unburned patch of ground had shrunk overnight, and now our new property was within the burn area. Both D and I started preparing ourselves emotionally for what we were sure was coming: a phone call from our agent informing us that the house had been heavily damaged or destroyed.



But that call didn't come. Instead, the seller -- part of the volunteer fire department battling the blaze -- reached out to me directly via email, sending me pictures that painted a very different story than the doom-filled media reports of walls of flame 200 feet high marching inexorably across the landscape. Those flame-walls were real, but they weren't representative of every part of the burn area. In many places, the fire contented itself with ground fuels, leaving the tops of the trees untouched while consuming the fallen needles, branches, scrub, and grass. Looking at the pictures, I almost couldn't believe my eyes. Instead of the blackened moonscape I expected to see, I saw green grass, green trees, and occasional black stripes on the ground where the fire sent out a finger, testing the defensible area around the house. The pictures were strategically shot, showing me bits of burn area but mostly focusing on the green stuff. I can't really blame the seller for that, he was trying to keep me from panicking and killing the deal -- and he surely knew that we would be visiting prior to closing and would see the whole story, so I view his shot selection as reassuring, rather than misleading.


On June 15, we learned that the major insurance carriers had all frozen the writing of new policies in the area indefinitely, as of the start date of the fire -- June 9. We obviously couldn't imagine buying a house with no hazard insurance, and the bank requires hazard insurance in order to approve a mortgage, so that had the potential to kill the deal. On June 16, after making some calls to find out more details, we discovered that our insurance policy had been delivered to our loan underwriter on June 8, one day before the freeze went into effect. Our agent promised to head up into the canyon as soon as the roads were reopened to take some pictures and video so that we could see the full extent of the situation and make a decision about the closing.

Despite our house being spared, the fire was nowhere near done. The management team spent the next two weeks scrambling to create and defend a line of containment around this fire, as record high temperatures, 30-50mph winds, and single-digit humidity levels all contributed to make the fire extremely aggressive and difficult to predict. Many stories have been written about the High Park Fire, so I won't repeat them except to give the final stats. By the time full containment was reached on July 1st, it had burned 87,284 acres making it the second-largest in Colorado history. 259 homes where destroyed in the High Park Fire, making it the most destructive fire in Colorado history (that record was broken immediately by the Waldo Canyon Fire outside Colorado Springs). Not including the property damages, the actual fighting of the fire cost $39.2 million.

Progression through June 23. Expansion in WSW corner is not mapped.

We had a closing date of June 29 scheduled, but our agent couldn't get into the canyon to view the house because the road hadn't been reopened. After much soul searching, we decided to fly out anyway, and to be very thorough with the final walk-through, since that would be our last chance to pull out. As the day drew nearer and nearer, we started questioning the prudence of closing on a house that remained evacuated and in an active fire zone. On June 28 in the late afternoon, as we were boarding our plane to Colorado, the evacuation was lifted. Only residents could pass the National Guard checkpoints, but we had the selling agent to drive us up, so that was no problem.

D and I wandered around the property and the house with our agent for about two hours that morning. We saw lots of devastation in the surrounding forest, but we saw plenty of hope, too. The views from the house are largely intact, with the worst destruction occurring deep in the forest away from the house. Most of the burn visible from the house is limited to grass damage, which will repair itself in a matter of months. There was minor smoke damage to the interior walls, but the seller's insurance covered its professional mitigation before we moved in. We found many green shoots pushing through blackened vegetation. But in the process of walking that property and surveying the situation, as well as conversations with the seller before, during, and after closing, we learned some terrifying details about the events of June 12.

It turned out that our seller/firefighter was defending his real estate agent's house and had to let it go because a huge wall of flame was sweeping up the hill from the road. He and his partner barely made their escape and were forced to cut the hose for lack of time. Despite this near-death experience, he chose to spend the night in his/our house so he could defend it. In the dusk, he and fire personnel from 3 other trucks felled trees, laid out hoses, and plowed dirt rings between the forest and the house -- effectively fortifying what was already a defensible zone. About 2am, he awoke to see an orange glow in the west-facing windows, so he wearily donned his protective gear and headed out expecting to watch the fire die at the fire break. Instead, he discovered that it had jumped the break and was within 10 feet of the western wall of the house. With his pre-laid hose, he was able to shut it down and contain the remainder of the fire behind the fire break. At some point he also had to turn his attention to the detached garage, which was closely threatened on its north and east sides. Propane tanks for both the garage and house show burned grass beneath them.

Many, many friends and relatives have tactfully or tactlessly asked us if we're crazy for completing this transaction. Our logic ran like this: the entire Front Range is experiencing record-dry drought conditions this year, making the fire danger extreme throughout all of our potential home sites. Now that the High Park Fire has burned away most of the fuel in Rist Canyon and the surrounding areas, another major fire in that area is unlikely for the next 20 years or so. So which is better? an area that has some black eyes but won't burn again for 20 years, or a pretty tinder box that could go up at any moment?

Our luck was magical: one day later with the insurance, and the bank would have killed the deal. Ours was the first real estate transaction to close after the fire. One day earlier with the closing date, and the evacuation order would have remained in place and our agent would have talked us out of closing. Ten feet more from the fire, and our beautiful new house would have been damaged or destroyed, and our seller would have been in grave peril. We also learned later that the Garage House was sold and closed on June 8. So those poor buyers, the very next day, found themselves thrust into an evacuation situation. We were spared that, since we were still in Chicago and in the stressful but safe situation of being able to simply cancel the deal if the house was damaged. All the other homeowners in the area had to wait and see if their summers would be spent rebuilding -- we could wait to see if we had to restart the real estate shopping or not.


Words cannot express my gratitude to the previous owner of our new house, as well as all the men and women on the many fire-fighting agencies that came together to contain this fire and protect the structures within it. But especially the seller, because I know he risked his life for the property, and then thought to take pictures to reassure us the next morning. He was an absolute prince of a man throughout the entire transaction, and I like to think that I can now number him as one of my friends.



26 January 2012

Document Scanning in Linux Using Perl

I decided I wanted to take all the old bank statements, credit card bills, and paystubs that we keep in a file cabinet, and scan them into digital format - PDF, to be precise. This is more secure, because we can easily make backup copies and encrypt anything sensitive. And it's less clutter, because even a small hard drive can hold many lifetimes of statements, records, and bills. It was a pretty straightforward plan:
  1. Buy a scanner
  2. Scan all the documents
  3. Shred all the documents
Of course, being me, I had to improve the process a little.

First off, we chose the Fujitsu ScanSnap S1500. There is also an identical S1500M, the only difference being whether the bundled software is written for Windows (S1500) or Mac (S1500M). Notice there is no S1500L (L is for Linux, boys and girls!). No big deal, though, because after only a few minutes of Googling I was able to be pretty certain there were drivers on my real operating system that would handle it. It was a bit more expensive than I would have liked, but having used it for a while now, I couldn't be happier with it. It scans fast, it scans well, and it does multiple pages and full duplex like a champ. Oh, and it opens like a frickin' Transformer. Awesome!

It uses USB, and I use Linux, so I politely ignored all the step by step instructions and software, and instead powered it on and jammed the USB cable into my machine to see what Ubuntu thought of it. Ubuntu thought it looked like a scanner, and it might like to do some scanning with it. No downloads, no crapware, no driver hell, and no problems. Nice. After a few test scans to get my resolution, orientation, and whatnot the way I liked it, I grabbed a handful of monthly bank statements and started scanning.

OK, that's tedious, and I hated having to re-type the damn file name every time. Chase_2007-01.pdf, Chase_2007-02.pdf, Chase_2007-kill-me-now.pdf. It would be nice to simply tap a key on the keyboard every time I get a document ready. I don't need a GUI, so maybe scripting is the right answer... more Googling.

To take input from a scanner and turn it into a multi-page PDF, (at least) 3 steps are necessary:

Step 1: Grab Images From the Scanner
On the Linux command-line, there's a great command called scanadf that will scan all the pages in an automatic document feeder (ADF) and store them as PNM files. The command for doing that for the May 2007 Chase bank statement is:

scanadf -o Chase_2007-02_%d.pnm --source "ADF Duplex" --mode Lineart --resolution 150

Notice the "%d" in the file name. If it's a multiple page statement, each page will be a separate PNM file, and each file name will replace the "%d" with sequential page numbers. PNM files are essentially graphics files.

Step 2: Convert the PNM Files to PostScript
This is a dumb intermediate step, in my opinion. I don't see why no one has simply written a direct PNM-to-PDF conversion utility. But whatever, another command called pnmtops gets this done:

pnmtops -noturn -rle Chase_2007-02_1.pnm > Chase_2007-02_1.eps

This command gets repeated once for each page, varying the file name like scanadf did. The result is a bunch of Encapsulated PostScript files, one for each page.

Step 3: Convert the EPS Files to a Single PDF
I had never used GhostScript before, but I had certainly heard of it. It always sounded kind of glamorous and mysterious - it's definitely mysterious. I tried to read its man-page and got lost nearly immediately (Adobe's docs always do this to me, too), so eventually I just found a recipe for doing what I wanted and stopped asking questions:

gs -q -dSAFER -dNOPAUSE -dBATCH -sOutputFile=Chase_2007-02.pdf -sDEVICE=pdfwrite Chase_2007-02_*.eps

With all these new-found powers at my disposal, I wrote a Perl script. I had it bump the month up by one and scan in all the pages every time I hit the enter key. I gave it the ability to pass in the starting month/year and the base file name ("Chase") on the command-line. Then I found I was missing the odd statement now and then, so I gave myself the ability to type "skip 1" instead of just hitting enter - this skips a month (guess what "skip 4" does) and continues scanning. Then I came across a monthly statement that was in color and on a single side of each page, so I added more command-line arguments to switch to "Photo" mode at 300dpi and to use "ADF Front" instead of duplex. Then I started scanning statements from a bank that for some reason liked to end its months on the 15th. Being a precise kind of guy, I added the ability to optionally include the day in the statement date: Chase_2007-02-15.pdf.

Then I ran into a brokerage statement that wanted to be landscape.

Step 1.5: Rotate the Damn Page 90 Degrees
Believe it or not, you need another program for this, called unpaper. This is a powerful and feature-rich utility, but I only use it to rotate the page. So the script only executes the following command if rotation is selected:

unpaper --pre-rotate -90 --no-processing 1 Chase_2007-02_1.x.pnm Chase_2007-02_1.pnm

Notice that the input file has that extra ".x." in it - I add that to the output of scanadf if rotation is selected.

What else could this amazing script possibly need? Well, I didn't like that the title displayed in my PDF Viewer was "Chase_2007-02_1.eps" - that seemed a little amateur. So the script also creates and then uses a PDFMarks (link is a PDF) file that sets the Title to "Chase_2007-02", and also sets the CreationDate property to February 15, 2007, because really, why not.

Perfect. I will never need to change this script again. Now let's scan some bi-weekly paystubs. Oops.

The Wrong Way
OK, I'm a professional developer, and I'm pretty good at what I do. I know the right thing at this point would have been to change the script to be able to specify the period. But at that moment, for some reason, I chose to copy the entire script, and change that copy into a bi-weekly one. Ugh. 

Later that same day I realized that manually scanning even the annual statements from retirement accounts and whatnot was kind of cumbersome. So of course that's when I fixed my mistake, combined the two scripts, and added annual periodicity, right? No. I cloned the script again. I'm so ashamed.

I lived with this abomination for nearly a week before its software equivalent of screamed obscenities in a silent church was finally too much for me to handle. Never mind that it was perfectly functional - that's not the point. It needed refactoring. Since it is just a little utility script, after all, I compromised on the perfection. There are still three separate scripts, but they now only do the monthly/yearly/bi-weekly work. They use a common module that does the actual scanning and common option management, so while the design is still terrible, at least I have some code reuse.

I'm publishing the full source for it right here (zip) in case anyone wants to use it, adapt it, or improve it. Let me say one thing up-front, though: I am a terrible Perl programmer. My syntax is nearly non-existent, requiring many trips to perldocs to figure out how to do the simplest things. And I realize that my Perl code looks like a C++ developer wrote it - there's a good reason for that. So if you've stumbled on this blog looking for scanning info, and you're a Perl master, have a good laugh at my expense. But please don't tell me about it.

Now if you'll excuse me, I have some quarterly (oh crap!) bills to scan.

31 January 2011

Happy 1976

I first wrote in November, 2010, about my Year-a-Month project.  It all started when I found vast holes in the McHouse Worldwide Music Library, especially from my childhood before I knew what was cool.  For example, calling myself a heavy metal fan without any Black Sabbath, Judas Priest, or Iron Maiden in my collection is a tad on the naive side, if not downright hypocritical.

January would have been 1976, but I ended up skipping last month because by the time I had recovered from my sinus infection, returned from Utah, and resolved the NAS hard drive scare (hopefully the hard drive scare of 2011), it was late January already.  So, no big deal, we push everything back a month.

In 1976, artists on my list released the following albums:

Technical Ecstasy, by Black Sabbath - I couldn't find this one available in MP3 downloadable format, so it is on its way from Amazon on a CD. 

Rainbow Rising, by Rainbow - This is a tiny little 6-song album wherein the first song didn't thrill me, so I just picked up 2-6.  I discovered Dio about the time of his death, and realized that his voice lurked in many of my hard rock memories.  Although Rainbow is a little (just barely - nothing like Deep Purple) on the 70s-pop side for me, it's still worth picking up for the music history if nothing else.

Presence, by Led Zeppelin - yet another Led Zeppelin album I'm shocked that we don't have.  It absolutely rocks, and I'm glad I found it.

Sad Wings of Destiny, by Judas Priest - I really only am familiar with the very biggest of the Judas Priest hits, but I'm finding a lot of things to love in these full albums from the early years.  It's almost an early groove-metal sound.

Free-for-All, by Ted Nugent - the physical CD was cheaper than the MP3 download, go figure.  This one is also on its way.

A Mini-Rant
OK, Amazon, I get that you like money, and I have no problem with that.  But when you sell a 7-song album for $9.99, and then hold back one of the 99c-each songs so that I have to get the whole album to get it at all, it seems petty and money-grubbing.  7 songs for $9.99 is a lot like $1.43/song, so make the songs $1.45 and let me download any and all of them.  Oh wait, if you did that you couldn't advertise that you sell songs for 99c each.  Right, so obviously the right thing to do is bundle them and make me rant (if this weren't a family-friendly blog I would point out that this is called "bundle-fucking me").  My blood pressure thanks you.

12 October 2010

Rsync Magic

I do not pretend to be anything other than an rsync tourist.  But I have had need of its services on two occasions, today being the latest, and I found it frustrating how difficult it is to get simple and clear information on how to set up its filtering to get all the files you want and nothing else.  Both times I've needed it, I've had to do a lot of relearning and searching.

I won't be solving the world's rsync problems on this blog.  But if nothing else I'll have another page to find on the internet the next time I need rsync, and with any luck it will answer my questions.  So here are the two ways I've used rsync, and how I set it up.

Task #1: Back up certain files to the NAS
The McHouse Enterprise Computing Cluster includes a machine running FreeNAS where I store all of our backups, MP3s, ISO images, and other assorted stuff I or the bintgoddess might need in our constant quest for entertainment.  I use Bacula to do full hard drive backups of the Windows 7 laptop she uses at school, her Windows XP desktop, and my Ubuntu desktop.  This is great, but certain files need more frequent backups than this, such as my machine's home directory. 

To do this, first I enabled the rsync service in FreeNAS.  Within FreeNAS' configuration web page, I set up an rsync path called HildeMark for my home directory.  In the image below, I've cropped off the stuff I didn't change.

Settings for the home directory rsync path

Then on my machine, I added the following command to my crontab to run at 4:15pm Monday-Friday:
/usr/bin/rsync -aFx --delete /home/mark/ kinakuta::HildeMark

For space considerations, refer to the rsync manpage for the precise meanings of each of these parameters.  In a nutshell, however, the contents of HildeMark's physical location should look exactly like my home directory right after the transfer completes.

But then I noticed that I was copying a bunch of stuff I didn't want, like gigantic source trees from work - those are backed up at the office, no need for me to do it again here - and Firefox's cache, which likewise doesn't need backing up.  That's where the .rsync-filter file comes in.  In each directory traversed by rsync, you can optionally create a file called .rsync-filter containing exclusions of things that should not be backed up at or below the current level.  For example, my very simple ~/.mozillia/.rsync-filter excludes Firefox's cache from the backup:
exclude Cache/
Similarly the .rsync-filter in my ~/src directory excludes anything that is source-controlled, since the SVN server's storage is already backed up and that's the master copy.  I can adjust any directory's .rsync-filter without having to worry about whether my changes have unintended side-effects on some other directory - it only affects file selection within that directory sub-tree.

Task #2: Roll Out Updates to a Customer
I just started a new project at the office wherein I am writing library code to be immediately used by another developer.  For reasons upon which I cannot elaborate, this developer cannot simply be given SVN access to the full source code tree.  He gets headers and libraries only for the things he needs to build.  Everything else is held back.  Additionally, since he will be developing against library code that I am actively writing, I can't just let him have whatever file I just saved... I need to at least make sure it compiles before I give it to him.  Likewise, he needs to control when he updates his copy of my library so that he can reach a good stopping place in his own code before having the library change on him.

This guy is basically a customer, so I need a staging area where I place the files he is allowed to see.  I do this when it is appropriate, and then when he's ready he copies everything from that staging area to his own development machine.  Most times, a small subset of files - or a small portion of a file - have changed.

Again I started by setting up an rsync daemon, but this time I didn't have the luxury of using FreeNAS' administrative web page.  On my development workstation, I created the following configuration in /etc/rsync.config:
lock file = /var/run/rsync.lock
log file = /var/log/rsync.log

[snapshot]
path = /staging
uid = nobody
gid = nobody
read only = yes
list = yes
hosts allow = 172.0.0.99
This creates a visible path called "snapshot" with storage at /staging.  I made it read-only since there is no reason for him to upload to me, and gave his IP address access to it without him needing a password to my machine.  The rest is pretty boilerplate.

Next I started rsync in daemon-mode with the logical syntax of:  rsync --daemon

To copy into this location, I needed to reproduce my source tree but redact those things that he didn't need - namely unrelated modules and all C++ implementation files.  The paths to various headers should not change so that cascading #include directives continue to work nicely.  I also needed to take all the libraries scattered throughout my source tree and assemble them in a single directory to make it easier for him to link to them.  To do these two things, I wrote a little script:
#!/bin/bash
rsync --progress --stats --recursive --times --delete \
      --exclude-from=$HOME/rsync/filter.txt \

      $HOME/src/proj1/source/ /staging/source
find $HOME/src/proj1/source -name "*.a" -exec cp -p {} /staging/lib/ \;
The rsync command gives me progress and statistics, deleting any files from the target directory that I remove from my source directory, and filtering based on the rules in ~/rsync/filter.txt.  I'll get to that in a moment.

The find command returns all the files ending in ".a" under ~/src/proj1/source, which happen to be exactly the static libraries I want him to be able to link against.  Using find's -exec syntax, it copies each file to /staging/lib while preserving the modified date/time.  This is important, because otherwise when I run this script it will make it appear as though I changed all the libraries when maybe I only changed one or two.

And here's a representation of the filter.txt file with some details removed for confidentiality.
- **/.svn*
- CMakeFiles*
- /build
- /secret1
- /Libs/secretLib
- /secret2
+ /examples/example1/main.cpp
+ /Tools/tool1/*.cpp
+ /Tools/toolsuite1/*/*.cpp
+ **/
+ **/*.h
+ **/*.inl
- *
  • Leading forward-slashes (/) refer to the top of the source directory, not the top of the volume.  So "/build" really means "~/src/proj1/source/build".
  • "-" at the beginning of the line means "exclude stuff matching this pattern."
  • "+" at the beginning means "include stuff matching this pattern."
  • The "**" token means: "match against every subdirectory".  So "- **/.svn*" will exclude x/.svn-info, x/y/.svn-stuff, and x/y/z/.svn.
  • "- CMakeFiles*" excludes all files and directories starting with "CMakeFiles" - this is where a lot of the temporary build scripts go, so this line removes a ton of useless gunk.
  • The next four exclusion lines keep rsync from descending into the named directories.  It is free to descend into Libs, just not Libs/secretLib.
  • The next two lines explicitly add some C++ implementation files he's allowed to have and might find useful.  The third inclusion line adds C++ implementation files from all the directories within toolsuite1.
  • "+ **/" is a catch-all rule to say: descend into every directory from here if you haven't matched an exclusion rule yet.  It only matches directories, so it controls where files are taken from, not which files.
  • "+ **/*.h" and "+ **/*.inl" explicitly specify that all files ending in "*.h" and "*.inl" in all subdirectories (if not previously excluded) should get copied.
  • "- *" means "and nothing else".
Whew!  That's only half the job: getting the files from my work area to the staging area when I deem them ready for roll-out.  Thankfully the other half of the job is a single command, and much simpler.

On the other developer's computer, he executes the following to copy everything down from my staging area to his library area:
rsync --progress --delete --recursive dev-mark1::snapshot ~/src/markLib/
The dev-mark1::snapshot notation, which is in the form machine::path, is interesting.  The double-colon (::) indicates that his rsync client should attempt to connect to a remote rsync daemon running on dev-mark1, and then request files from its snapshot path.  Since I set snapshot up to point to my staging area, this gives him two directories under ~/src/markLib/: source/ and lib/.  Source contains a full source tree with only headers in it, and lib contains all the libraries I copied into it using find.

Now whenever I feel like sending out a mini-release, I run my script.  Whenever he wants to check for a mini-release, he runs his rsync command.  Rsync and find take care of the rest.  Voila!