Monticello -> Git
Back in the dark years of 2003, Avi Bryant and Colin Putney tried to use CVS to version their Smalltalk code and failed dismally. They decided to scratch their itch, and Monticello was born, a DVCS centred around managing the structured text of Smalltalk code.
Years have passed, and nowadays we have Mercurial and Git providing succour to so many. How can Smalltalk leverage these tools, instead of maintaining its own custom version control system?
First, some background: Monticello versions collections of classes, identified by an
. The various versions of this collection of source code are stored as
instances, stored in an
of some kind. There are quite a few kinds, too: directories, WebDAV, SMTP, in-memory repositories, GOODS repositories, …
Dale Henrichs recently released a project called Filetree, implementing a new kind of Monticello repository. It uses git as a glorified object database, storing individual methods, class definitions, metadata like class comments and so on in separate files. Git’s very handy, of course: for the price of some FFI you get a heavily tested version control system and access to an entire ecosystem of handy tools like GitHub. Not only that, but because Filetree is just another
, Dale has preserved the utility of the Monticello user interface: browsers for repositories, versions and the like.
Further, Filetree permits one to version a collection of Monticello packages together, so you can track changes across several packages. This is something that Monticello simply can’t do.
But if you’ve got loads of libraries stored in Monticello repositories, how can you make use of these tools? Easily, as it turns out, thanks to an expressive API. Let’s look at an example: my Control API is quite small, but still interesting, because it has multiple packages.
Tim Felgentreff’s Gitocello supplies most of the bits we for writing to the source repository.
Since I have local copies of the various Control versions, we’ll just use the local cache,
(Squeak’s equivalent of Maven’s
The standard form for a Filetree repository is a base directory, and storing the actual code under a “packages” subdirectory. In our example we’ll thus have a structure as follows:
And so to some code. The idea is simple: for every interesting package, and for every version stored in the source repository for that package, copy the version to Filetree. This maps the Smalltalk code to the filesystem. After each version copy, commit the changes to the file system.
| commitMsg git repoPath destRepo sourceRepo packages versionsInOrder |
repoPath := '/home/frank/Documents/squeak/package-cache/git-repositories/Control/'.
git := GCGitWrapper new.
git localPath: repoPath.
commitMsg := [ :mcversion | | info |
info := mcversion info.
(info message, Character lf, Character lf,
info name, Character lf,
info dateAndTime printString) copyReplaceAll: '"' with: '\"'].
sourceRepo := MCCacheRepository default.
destRepo := MCFileTreeRepository path: (repoPath, 'package').
packages := #('Control' 'ControlTests' 'ConfigurationOfControl').
versionsInOrder := (packages collect: [:pkg |
((sourceRepo versionNamesForPackageNamed: pkg)
collect: [:v | sourceRepo versionNamed: v])]) flatten
sort: [:a :b | a info dateAndTime < b info dateAndTime].
versionsInOrder do: [:ver |
destRepo storeVersion: ver.
commitAll: (commitMsg value: ver)
dated: ver info dateAndTime.].
The above script does have a few non-standard bits of code: I added
SequenceableCollection >> #flatten
, and extended Gitocello’s
This isn’t a complete solution, mind you:
- We ignore branching
- If you’re missing versions in your
sourceRepo, tough luck.
But it’s pretty good!