I just shipped SRC 0.9, and you no longer need to be adventurous to try it. It has a regression-test suite and real users.
Remarkably, SRC has had real users since 0.3, two days after it was born. Even more remarkably, the count of crash reports and botched operations from those users is zero. Zero. This is what you can gain from keeping code simple – I have has a couple of bug reports but they were both about filename quoting in the fast-export code, which is not a central feature.
Next, I’ll make a couple of what I think are important points about writing for zero defects. Then I’ll talk about a subtle issue or two in the design, and our one known behavioral glitch.
If you read the source code for SRC, you are rather likely to think “Well, no wonder it has not had any reported defects. This is trivial! There’s nothing here!” If that is your reaction, you are skirting a large truth. The precisely important thing about SRC is that it adds a lot of value to RCS at the same time that it is so simple that you can easily see all the way down to the bottom of the code. Achieving that took harder work and deeper thought than would have been required for a program that was fussier, more complex – and more bug-prone.
Too often programmers succumb to the temptation to be clever rather than simple. Edsger Dijkstra used to bang on about this, and he was right. In SRC I strove for simplicity, not just in implementation (which is why I re-used RCS rather than writing my own storage manager) but in design as well, all the way out to the level of the user interface.
The result is, alas, not perfect. There is at least one wart, and arguably a couple more, in the UI.
Early on I made the basic decision that the commands would have the general form “src verb [modifiers] [range] [filename…]”; this, of course, is a deliberate imitation of CVS/SVN/hg/git designed to make the UI feel instantly comfortable to their users. Unfortunately, this clashed with two other premises: range specifications being optional and named tags being available in range specifications. Consider this:
tag foo bar baz
OK, so we can tell by its position that ‘foo’ is a tag name. But is ‘bar’ a tag naming a revision (the command reads “create a tag named ‘foo’ at the location named by ‘bar’ in file ‘baz’) or is it a filename (‘create a tag named ‘foo’ at the default branch tips in files ‘bar’ and ‘baz’)?
Syntactically there’s no way to tell, so I had to introduce a special prefix ‘@’ meaning “this is a revision specifier even though it looks like a filename”. A wart.
Suppose you have, for some reason, a file named “23”. To say “this is a filename even though it looks like it’s a revision specfier” you could write this:
tag foo -- 23 baz
This seems less warty, if only because there’s precedent in Git and elsewhere for using “–” to mean “options end here, normal operands start afterwards”.
Most of the serious ambiguities were in the ‘tag’ and ‘branch’ commands. Reluctantly, I ended up requiring a qualifier on these commands – in 0.9 you have to say “tag create”, “tag delete” and “tag list”. Otherwise the rules for when a token would be interpreted as a qualifier, when as a named tag, and when as filename just got too easy to screw up. The alternative would have been to drop back to a much more verbose RCS-like UI with switches, switches everywhere.
I still have one serious implementation issue. SRC uses utime(2) to touch both a workfile and then its master on checkout, so we can test for modified status just by comparing modification dates afterwards.
The problem is that utime(2) seems to be prone to flaky failures. Mike Swanson and I have been poking at this and we can’t figure out where the problem is. Python? The kernel? My on-line research has turned up a lot of bug reports about utime(2) that seem vaguely relevant; they hint that the problem might even be in an (unknown) glibc bug that’s tripping up Python.
To be further investigated. I’m seriously thinking about porting the code to another language (Go is the leading candidate – better fit to SRC’s abstractions than Rust) to find out of the bug replicates.