View this PageEdit this PageAttachments to this PageHistory of this PageHomeRecent ChangesSearch the SwikiHelp Guide
Hotspots: Admin Pages | Turn-in Site |
Current Links: Cases Final Project Summer 2007

Some progress using CVS with Squeak (or: Standing on the Shoulders of Bears)

Blake Bausser: gtg780g
Chris Sidi: gtg303m
Benjamin Donahue: gtg324a
Pritesh Patel: gtg423g

The Bears with Lightsabers recommend you use CVS, and they chose to file out each class as separate .st files.

Our group agrees - you should use .st files. Yeah you could try to use images, and email them to each other, but they're huge. And you can't submit an image - you're supposed to submit .st or .cs (Changeset) files. Might as well test what you're going to be submitting the whole time you're developing, rather than filing out from your image at the end and hoping it works for the TA too. And well, I just don't trust images. I think they get corrupted, but you don't notice right away and find your last X backup images don't work right either.

Now Bears with Lightsaber also say "please don't use the method."

Okay so... use CVS, use multiple .st files - up to one per class. Now we had 34 classes total by our last milestone. How are you gonna handle 34 classes? Click a filename and then select "filein" 34 times? And if you have a power outage, file in again? Naw, if you've got that many classes, you're too likely to miss one. And what about filing out? You gonna file out all 34 often? Trust yourself to fileout only the classes you've modified? Bwhahaha. (That evil laughter is my way of saying that seems error-prone.)

The Power of the Command Line - Less Clicking, More Typing

I believe everything you can do through Squeak's menus you can do from the workspace with just text. Take "quit" on the World Menu. You can type or paste "Smalltalk snapshot: false andQuit: true." in the workspace and "do it" instead.

How'd I figure that out? Well in windows, I clicked the background with the left mouse button to get the World menu up. Then I moved over "quit" and clicked my scroll wheel twice so that a morph menu was up for "MenuItem". Then I clicked the wrench and chose "explore morph".

If you expand the root item and look at the target and argumenets, it's calling quitSession in TheWorldMenu. However that's not a class method, so I don't think you can call it directly from the workspace. (though I guess you could say "(TheWorldMenu new) quitSession"). But look at the code in that method:


snapshot: (self confirm: 'Save changes before quitting?' orCancel: [^ self])
andQuit: true

The capital S in Smalltalk tells you its a class or global object. (Type "Smalltalk" in the workspace and hit Alt-P and you'll see its actually a SystemDictionary - so Smalltalk is a Global SystemDictionary object.)

So replace the "(self confirm..." with "false" and you've got the code to quit.

You can use similiar morph exploration to figure out how to file in and out code. You can launch the file list, select an .st file then middle click over the filein button three times till the PluggableButton is selected. Now its kinda hard to tell, but when you click "filein" in the filelist SimpleServiceEntry>>performServiceFor: is called, which calls class method fileIn: of FileStream.

So instead of using the filelist, you can type/paste "FileStream fileIn: 'C:\'" in the workspace and just "do it".

Launch a code browser, select a class and right click to get the menu up. Explore the fileOut menu option. Again its kinda hard to figure out, but when you click "fileOut" it calls Browser>>perform:orSendTo: which calls Browser fileOutClass. That method ends up executing "self selectedClass fileOut".

That means you can just do "Foo fileOut" to fileout the class Foo to your default squeak directory!

The Power of CVS Merge.

Say two programmers retrieve the latest version of a class at noon. One adds the "foo" method" and submits at 4pm. The other adds the "bar" method and attempts to submit at 5pm. Now if CVS just let him submit his class file the "foo" method added at 4pm would be wiped out. (This is not Smalltalk/Squeak specific - this could have with Java, or any language where 2+ developers are adding code to the same file.)

Instead CVS tells him he doesn't have the latest and needs to update. On update CVS tries to merge the already submitted changes with the programmer's current file. If the merge is successful, the programmer will now have a file with both "foo" and "bar", and can then submit.

CVS merge works on a line-by-line basis, like the program diff. However, Squeak, on Windows at least, likes to generate .st files without a single newline. Open an .st file in about any editor besides Wordpad, even Notepad, and you'll probably see these neverending lines. The unix tool "wc -l" will sometimes tell you an .st file is zero lines long.

So to allow CVS to merge changes, we've got to get Squeak to add newlines to each line. How?

Okay so Class>>fileOut gets called when you select fileOut. Look at that method - it calls fileOutAsHtml: false. fileOutAsHtml: starts by making a new FileStream instance. What if we copied fileOutAsHtml:'s code but used a CrLfFileStream instead? That should get us our newlines. Modify the argument for "newFileNamed: " and you can fileout to any directory, not just your squeak default directory.

And to filein an .st file with newlines? Well instead of "FileStream fileIn: 'C:\'" use "CrLfFileStream fileIn: 'C:\'".

Filing In and Out Whole Directories, Categories.

Okay so we know the code to file in and out. We know how to get linefeeds so CVS can do its thing. How do we file out whole categories but with a separate .st file for each class? How do we file in a whole directory of .st files?

Well after poking around the code for the code browsers, I found I could get a list of classes in a category using SystemOrganization superclassOrder: . Like try pasting "SystemOrganization superclassOrder: 'Kernel-Objects'" in the workspace and "print it" - you get an ordered collection of the classes.

And filing in all the .st files in a directory? Well that's just:

| targetFD |

targetFD _ FileDirectory on: 'C:\myTargetDir'.
(targetFD fullNamesOfAllFilesInSubtree) do: [:aFile |
((FileList suffixOf: aFile) = 'st') ifTrue: [
CrLfFileStream fileIn: aFile.

My Utility Class (file in with CrLfFileStream fileIn:)

So I have a class Gteam. Right now it's set up to fileout anything in the "2340" package - that is, any classes in any category starting with "2340-". If you want to fileout a different package name, modify the initialize class method.

I decided to start a CVS module and put the code for Gteam in the root directory of that module. Then I like to start Squeak with its original image from and paste this in the workspace:

"Step 1 - Highlight and doIt.
(Modify the filepath below if necessary - it should point to

gfile _ 'Z:\cal\'.
CrLfFileStream fileIn: gfile.

"Step 2 - Highlight and doIt."

Gteam setRootDir: gfile.
Gteam setSubDir: 'M2src'.
Gteam fileInDir.

"Step 3 - There is no step 3!
Well okay, there is sorta.
Do the below to fileOut.
Follow that by a cvs commit of the files. (This is done outside of squeak.)
Do both fairly frequently to minimize code merging issues."

Gteam fo.

So first I'm filing in the code, which I filed out with newlines:

gfile _ 'Z:\cal\'.
CrLfFileStream fileIn: gfile.

Then I indicate that the directory I want to file in and out of is Z:\cal\M2src:

Gteam setRootDir: gfile.
Gteam setSubDir: 'M2src'.

(Gteam setRootDir: can take a .st file. It will use the path of the file as the root directory.)

Now to filein all my classes I do:

Gteam fileInDir.

And to file out all my classes I do:

Gteam fo.

I made filing in several more characters than filing out because filing in could be destructive - it could overwrite your latest changes with older code. Whereas it pretty much never hurts to fileout and do a cvs commit - you never lose any code.

Gteam and CVS Usage

So I sit down at a computer and log onto it. First I'd do a "cvs update -d" and get the latest submitted code from CVS. Then I'd start squeak and use Gteam to filein all that latest code. When I reached a stopping point or wanted to quit Squeak I'd fileout all the classes. Then I'd do "cvs add" for any new classes I created, and then "cvs commit" to get my latest code in CVS.

I'd fileout, quit squeak, cvs add and commit, cvs update -d and then restart Squeak and filein pretty often to be sure I'm working with recent code and to avoid merge conflicts.

Remaining Issues

After I developed Gteam, I didn't really get to test it or use it much. My group had already started mailing each other .st files and some of the members hadn't used CVS.

So Gteam and the code may not work as it is now. I foresee two possible issues which I didn't get to test:

1. I think if you filein a subclass before you filein its superclass, Squeak will set the subclasses' superclass to nil. Uhh, I mean say you have class Person and class Employee. Person's superclass is object, and Employee's superclass is Person. If you filein and then, no problem. But if you do it in the reverse order, Employee may get "nil" for its superclass and stuff won't work.

If this is a problem for Gteam, then someone would want to come up with a way to force fileInDir to filein in a particular order. I think since Gteam uses SystemOrganization superclassOrder:, it files out the classes in an order than would be fine for filing in. Maybe Gteam's fo method could be modified to always write out a FileInOrder.txt file, which Gteam fileInDir would read.

Or here's a simpler solution: Maybe you can just call fileInDir twice - by the second pass, every superclass has been filed in so probably all the nils would be set back to the correct superclass. So move fileInDir:'s code to _fileInDir: and have fileInDir: call _fileInDir: twice.

2. Linefeeds and unix. If I'm on my home Windows computer, and am using some Windows CVS client (e.g. Tortoise CVS, as recommended by Bears with Lightsabers), then the Windows CVS client will translate the CRLF ("Control-M\n") line endings to just LF ("\n") line endings as it stores it in CVS I believe. If a group member is doing all his work in Linux, and using Gteam/CrLfFilestream, his work will be stored with CRLF line endings. If I check out his work with my windows cvs client, I believe his CRLF line endings will have a CR added and become CRCRLF line endings. If the linux team member checks out my code, he'll get only LF line endings.

I don't know enough about CrLfFileStream fileIn: to know if it handles LF or CRCRLF line endings just fine. Even if Squeak's fileIn will happily strip whatever line endings it finds, I think there can be merge issues. If one submission has LF line endings and the other CRLF line endings, I believe CVS sees every line as different and can't perform a merge for you.

Even if everyone on the team thinks they're just using windows they're can be problems. Say someone logs onto a CoC cluster machine running Windows. They probably won't be able to install a Windows CVS client, so they'll just ssh to and type their CVS commands there while accessing the files from their Z:\ windows drive. Well in that case they're using Windows Squeak but unix CVS and no line translation will happen for them. If they commit, the file in CVS will have CRLF line endings and when someone checks it out to their windows home computer it have CRCRLF line endings I believe.

Except for issue #1 up there, I think Gteam right now should work if every use of CVS is on the same operating system. For example, if all 4 group members have macs and use Mac CVS clients, Gteam will hopefully work. If all 4 group members have different OSs but all scp their work to helsinki (as binary files) and use CVS only on helsinki, that would work too. But scping all the time would be annoying.

I wanted to support all uses - pure Windows, pure Linux, Windows Squeak but mounted drive and Linux CVS... oh and I guess MacOS too. But I didn't have time to research and test this linefeed issue. If your group knows they want to use CVS, maybe you can come up with some rules like "only use CVS from Windows", or "use scp to transfer the files, and only use CVS on linux". Or if you're willing to give up merge capability, you could add .st files to your module as binary files and avoid lineending translations.

I'd say you could have Linux Squeak file in and out different than Windows Squeak, but someone using Windows Squeak but unix CVS could still mess things up.

Good Luck,
Team Generic Team Name (file in with CrLfFileStream fileIn:)

Links to this Page