It’s got a good deal of the functionality I want.
• I can see the state of the files in my working copy: modified, added, removed.
• It supports visual diffs, and for extra points, it supports all the major external apps that provide this.
• For a repository, it can show me all the files affected by a particular check-in. It supports deleting, moving, and copying files in the repository.
All in all, it’s a workable replacement for Xcode’s Subversion support. That’s how I use it, as an occasional replacement, to deal with the Subversion work I need done for files that aren’t in my Xcode project.
Now, before I get to what I don’t like about it, let me acknowledge two things.
One, svnX has been released under the GPL. I could dive into the code myself and fix the problems I’m going to describe. Unfortunately, I’m already in the middle of a project – that’s what I’m keeping in my Subversion repository, and why I need a Subversion client! That said, I may take this up at some point.
Two, I want to keep my criticism of the application separate from the application authors. I treat the failings of a project like this, done for free and presumably in one’s spare time, far differently than I would treat even a shareware application.
I said svnX had most of the functionality I need. But in the realm of polish, it is disappointing, shocking even.
Have a look at the screenshot of the repository window. Notice anything strange in the uppermost table? What’s that first column of radio buttons doing there? To do a diff between two check-in versions, I shit you not, you need to select one of them via the radio button column, and the other by a more normal selection highlight, and then click the FileMerge button.
Why is the UI set up in this bizarre manner? I don’t know for sure, but my suspicion is because it was easier. In Cocoa nibs, tables can be set to allow only single-row selection, or multiple-row selection. Multiple-row selection, as you can imagine, involves a lot more work to handle all the different possibilities. But it’s what users expect.
Actually, I simplified things a bit. Hitting the FileMerge button doesn’t go directly to a file merge. It actually brings up a sheet with exactly the same two tables that are at the top of the window the sheet is attached to, from which you have to select the versions you want to compare. What does that sheet refer to? It turns out, it refers to the file selected in the third table on the main window, though the sheet gives you no indication of that. You can’t multiple-select in that file table, either, so you can’t do several file comparisons at once.
svnX has separate windows for Subversion repositories versus Subversion working copies. You can see a screenshot of the working copies window here. You’ll notice this window is much more simple. The repository history is completely unavailable. All you can know from this window is whether any files have had changes made to them since the last check-in.
Again, I’m assuming the reason this was done was to make things easier. It would be much harder to create an intuitive user interface where the information about a working copy is overlaid on the repository itself…but that’s what I want.
I want to be able to compare my working copy file’s condition against what was checked in 10 revisions ago. I want to be able to see what check-ins were made on the files that I have edited, in the same table – a column you can hide, perhaps, so it’s not always cluttering up the window?
I want to be able to perform file and folder moves in the working repository before checking them in: right now, you can only do that in svnX in the repository window, where changes are immediate.
I want to be able to go from the row representing the file to the file in the Finder. Contextual menus seem totally unsupported.
There’s probably more, but this will do. Some of this stuff is small potatoes, but if I were to work on this, I’d want to tackle the big stuff first, which would probably mean rewriting a lot of the UI code. Not something to be undertaken lightly.
Before I go, I’m going to have to take issue with the name. “svnX”? You name a GUI application after Subversion’s crypticly-named command-line tool plus an “X” to show it’s on Mac OS X? Is that the best you can do?
Here are a few suggestions, marked as (taken) if I found an app with that name in http://VersionTracker.com:
“Subversion” sounds like “Subversive”, right? So how about: Saboteur (probably not), Instigator, Agitator, Provocateur (I like that one).
You’re gaining access to an otherwise unreachable destination, like a castle, right? So how about: Drawbridge, Bridge, even Moat.
It’s a record of changes, so how about: History (taken), Historian (taken), or Archiver. You use it to find or figure things out, so maybe: (Code) Detective, Codehound, maybe even Code Thief.
Finally, I’ll end on a positive note: I like the swirly blue icon.
One of the things that sank my MacTADS project was that I let my perfectionist tendencies take over. It wasn’t enough to have things work: the files had to be formatted exactly the same way, had to use the exact same techniques everywhere, had to have exactly the right behavior.
I didn’t use an SCM system then.
Now that I’m using Subversion, I’m finding something pretty cool: the rewriting tendency is more easily resisted.
Instead of letting one thing lead me to another, and then to another (“OK, I’ve fixed that bug, but I’ve noticed this other inconsistency, I’ll just do a big find-and-replace to regularize that….”), I now have to stop and think, what kind of comment would I check that in with? Does it really make sense to lump together this two, or three, or five different changes? When I started out only wanting the first? All right, then, undo the beginnings of the other changes, check in what I have, and call it a night.
Now, if I didn’t care about the organization of my Subversion repository, this wouldn’t be a problem. I might not even use comments for my checkins! (There’s a circle of Hell reserved for such people, btw.) But I’m a perfectionist, remember?
Now, I haven’t yet turned this into greater productivity. I’m not getting things done faster. But I do think I’m avoiding getting things done even slower….
In a particular directory in my Subversion repository, I had made maybe ten subdirectories whose names were version numbers, missing their decimal points. Version 2.5.8 was directory name "258", 2.5.9 was "259", etc.
This stopped sorting correctly with version 2.5.10. D’oh!
So I wanted to change all the directory names to their full decimal point equivalents, and then commit the lot of ’em.
Trouble was, I mistyped one of my
svn rename changes. I set one directory’s new location to be inside another directory’s new location. Quick, how could I undo that!?
I undid it by using the
svn revert command on both the new location and the old location. But what about the case where one was inside another? I had a bad feeling about it, and I was right.
I wound up with Subversion thinking that one of the new locations was (a) an existing, checked-in directory, and (b) being moved to another branch (?). Like so:
Multiple subsequent uses of
svn revert were unable to change Subversion’s mind.
Now, the easiest way to undo all this is probably to nuke the working directory and check it out again.
But I’m never one for easy, so I decided to try mucking with the contents of the local
.svn/entries file has a list of all the files, with changes. Each file is represented by an
<entry/> tag. The
schedule attribute seems to describe the
rename directives I’ve been making.
But hand-editing this file didn’t change the result of a
svn status call.
I didn’t want to screw around with it, so I nuked the directory and started over. This time I set up the
svn rename calls ahead of time in a text file and double-checked them, so I would get them right the first time.
Has anyone had success hand-edited these
Inside those version number directories mentioned above are code archives. I needed to update my code directories with files from within those the archives. Replace both files and files within subdirectories and files with subdirectories of subdirectories.
In fact, under normal circumstances, I would have just taken the directory that each archive expanded to and dragged that over to replace its code directory twin, because my code directories have the same layout as the archives.
But now that my files are in a Subversion repository, I had to worry about all those little hidden
.svn subdirectories. If I just replaced a top-level directory in the repository with a directory from elsewhere via the Finder, all the
.svn instances would vanish.
So: is there a utility that will do such a nested copy without actually replacing the directories themselves (and thus deleting their hidden contents)?
Once the new files had been copied over, I needed to add them all to the repository. Oops!
svn add requires you to specify the exact file (or maybe the location of the files?) to be added. But I’ve got a hundred of new files in a score of different directories.
Text files to the rescue again. I copy the results of an
svn status call into BBEdit and sort the lines, so all the lines that start with
? come first, then manipulate the text a little more to get a list of
svn add commands.
Is there an easier way?
The reorganization of source files and their enclosing directories in large commercial projects tends to be nasty, in my experience.
1) On Macs at least, they use CVS as their source code management (SCM) system. CVS can’t handle moving files and folder gracefully. At best, you delete the files in one place and check them in somewhere else, which abandons their history in the original location. And you can’t delete directories at all. Instead, you leave the old, dead, empty directories there and instruct everyone to use
-P when they do their checkouts and updates, so that empty directories are pruned for local repositories.
2) The major IDEs on the platform treat the visual representation of the Groups & Files in your project as something entirely different than the actual files and directories on disk.
“Groups” (yellow folders) in Xcode are only meaningful as an in-project organizing principle. Moving them, or the file representations within them, around in your project window, won’t change what’s on disk.
And conversely, if you move files and directories around on disk, the project won’t automatically be updated to the new arrangement.
So for a reorganization, after you move everything around on disk, you can look forward to doing the exact same thing again in your IDE user interface. This, as you can imagine from how I’m describing it, frustrates me to no end. But it gets worse.
For newer projects, you can fix 1) by switching to Subversion, where both files and directories can be moved around easily with complete retention of their history. But you can’t somehow execute the moving around Subversion commands in the Xcode UI itself, because of 2).
Why did anyone think keeping Groups & Files completely unrelated to disk directories and files was a good idea? Every commercial project I’ve ever worked on has made its Groups & Files tree mirror the on-disk directory structure whenever possible – the only times they diverged was due to maintenance error.
The only good reason I’ve had for a different Groups & Files structure was for a library where all the library headers needed to be available in the same top-level directory. So on disk, it was an undifferentiated list, but I used a 3-4 level deep group hierarchy in the project itself to be able to organize the files better conceptually. But, as I said, I’ve never seen this in the wild.
I’ve just seen hours upon hours spent maintaining hierarchies in two places, both for project creation and the inevitable reorganizations that followed.
(See bottom for updates.)
You’ve followed my Subversion posts so far; you have an Xcode 1.* or 2.0 project you’ve checked into your Subversion repository.
Now, you’ve updated to Xcode 2.1, and you want to open your
In previous versions of Xcode, the application allowed you to update your project in-place, which made source control maintenance easy. But now, Xcode will prompt you to “Upgrade a Copy,” and will require you to use a new project suffix,
Xcode projects are really folders, a.k.a. bundles. So a project name change means a folder name change. In CVS, this is suboptimal. In Subversion, not so much, but Xcode’s upgrade process complicates matters. Here are the steps that I used:
1) Open the project in Xcode 2.1. Click the “Upgrade a copy” button, and choose a temporary project name, like
2) Go to Terminal, type in
svn rename MyProject.xcode MyProject.xcodeproj and then
svn commit -m "My message" MyProject.xcode MyProject.xcodeproj, where “MyProject” is the actual name of your project, and “My message” is your commit message.
You may need to use
svn revert MyProject.xcode/* first to make sure any trivial project changes made during your last 2.0/1.* session, but after your last commit, don’t prevent the rename.
3) Copy all the gunk inside the
Temp.xcodeproj bundle to the renamed project bundle. The most important thing will be the
project.pbxproj, but there will be other files that start with your or other people’s username and end in various suffixes that you can copy and commit at your discretion.
MyProject.xcodeproj again. Now, from within Xcode itself, you’ll be able to do the rest of your Subversion commits, namely the
project.pbxproj and other
myusername.* files. This is most easily done from the SCM smartgroup visible under Groups & Files in the Default and All-in-One Xcode layouts. Select the files, then Control-click them and choose “Commit Changes…” from the contextual menu.
You couldn’t commit them from within Xcode before because you didn’t have the proper
.svn folder in the same place as the changed files.
Basically, Xcode wants to be in charge of the project rename, but Subversion also wants to be in charge of the project rename. To satisfy both, you have to do it twice, and then do your own manual (but simple) “merge”.
And you will have to do this for every Xcode project in your Subversion repositories.
The good news is that, once you’re done, your project history will remain uninterrupted.
Update #1: One thing that doesn’t work is changing the suffix to
.xcodeproj first and then opening the project in Xcode 2.1. The Xcode UI still requires you to upgrade a copy; it will still not upgrade the project in-place.
Update #2: Another way to do this is to skip 2) completely. The resulting
MyProject.xcode will still open in Xcode 2.1 fine, and you won’t have to worry about renames in your repository.
The Xcode 2.1 UI doesn’t give you the choice to use the
.xcode suffix for the upgraded project, but the fact is all historical suffixes are still valid for 2.1 projects, even
.pbxproj. Xcode examines the actual contents of a project to determine whether it needs to be upgraded, not its suffix.
If I’m remembering it correctly, Luis de la Rosa, whom I met at the WWDC 2005 Weblogger Dinner tonight, mentioned how, when he searched on “Tiger” and “Subversion” in Google, instead of getting Apple documentation on using Subversion with Mac OS X 10.4 “Tiger,” he got…me!
Aww! You like me, you really really like me!
Or, y’know, maybe not. So for those of you coming from Google (hi!), here’s a link to the very helpful Xcode 2.1 User Guide chapter on using Subversion with Mac OS X 10.4 “Tiger”.
Sentiments like that can only end in tears, I tell ya!
There’s already a Subversion bug covering one part of the behavior I saw, 1830: Dump format does not handle newlines in filenames.
In the comments for that bug, the developers seem to have come to the conclusion that they would fix the problem by disallowing files with newlines.
And they seem to have succeeded! In trying to reproduce the bug, I found that I could not add an “Icon\r” file to a repository either by
import or by
add. In both cases, I got the error message:
Invalid control character '0x0d' in path 'xxx/Icon
Now, I know I never added the “Icon\r” files explicitly myself. I thought I might have done it in the initial import, but the tests I ran above say no.
Although…this could be a difference between the Subversion 1.1.4 package (or maybe even earlier) I originally used and the Subversion 1.2.0rc4 package I upgraded to when trying to investigate the problem.
That seems like the most probable, not to mention hopeful, explanation, so I’m going to leave it at that.
Note: With this post, I have used four words from RhymeZone that rhyme with “subversion,” out of a total of twelve. Eight to go!
I’m now also using subtitles, so the post names are at least somewhat comprehensible. And no, your memory isn’t playing tricks: I retroactively added subtitles to the first two Subversion posts.
When I got back to my personal project recently and tried to commit some files to its Subversion repository, I started getting an
Abort Trap error message. At this point, I was told by Subversion that I needed to use
svn cleanup in the local copy and
svnadmin recover /My/Repository on the repository to make things right.
I couldn’t find anything online about other people having these problems.
So I decided, if the repository was bad, I would dump and reload it, and maybe that would fix the problem. I used the Subversion commands I described in this previous post to do it.
What I discovered was that the reload process halted at an “Icon” file. Actually, the name of the original file was not “Icon”, but rather “Icon\r”, where the “\r” indicated a carriage return a.k.a. ASCII value 13 character, which is the old Macintosh linebreak character.
As described at on this page (but curiously, nowhere that I could find on Apple’s ADC Web site), “Icon\r” files are the way to specify that the enclosing folder should have a custom icon, and to specify what that custom icon should be. Such files are invisible due to a particular bit set in their HFS information. Update: in the original version of this post, I said they were just the OS 9 way of doing this, but I was wrong; OS X still uses this technique for everything except volume icons. (Thanks, Uli!)
In any case, they must have been sucked into the Subversion repository I made. If I used my original repository dump file, I could even make the “Icon\r” files show up in the Finder, since the hidden bit is not preserved by Subversion. But any attempt now to commit any changes while these “Icon\r” files existed made my repository go boom. (Why didn’t this show up during earlier commits? Dunno that, either.) This included any attempt to delete those “Icon\r” files. I was stymied.
However, there was a way to modify the dump file I had been able to make (though not re-load). Subversion dump files are human-readable text files, but the file I had was 80 Mb., basically unreadable in any text editor on my machine without a lot more RAM. Luckily, the Subversion authors saw just this possibility, and made a utility,
svndumpfilter, that can be used to filter specific files out of a dump file.
Simply give it either a list of paths you wish to keep, or a list of paths you wish to not keep, then pipe your repository dump data through this filter. The result will be a modified stream of dump data that contains only the versioned paths you (explicitly or implicitly) requested.
See the rest of the manual for more details: it’s quite nicely written.
Using that utility, I was able to remove the “Icon\r” files completely from my dump file (as if they had never existed), load the repository in again, and go from there, with no loss of history for the files I hadn’t removed.
One nice thing was that once I’d removed the “Icon\r” files from the
trunk area of my repository, they were also gone from the one
tag area I’d made. I didn’t have to manually remove the files from both places.
Updated: made corrections suggested by Uli in the comments.
I’ve spoken to two developers I know about my Subversion repository, and the reaction from both of them was:
You’re using the filesystem version, not the Berkeley DB version, right?
But while the first developer (hi, Wolf!) couldn’t give me a good reason not to use the default Berkeley DB version, which per the manual is better tested, the second developer (hi, Uli!) could.
On Mac OS X 10.3 Panther, Berkeley DB has a known corruption bug.
Good enough for me.
The manual actually does a really good job of explaining the steps needed to export your repository and then import it again in another format. Here are the steps I used:
svnadmin dump /My/Repository/ > dumpfile
// Move old repository somewhere else
svnadmin create --fs-type fsfs /My/Repository/
svnadmin load /My/Repository/ < dumpfile
I just rebuilt my entire project and ran it, and it looks great.
At least one of my commenters has mentioned various Subversion GUI-based clients. For now, I’m just using Xcode 1.5’s Subversion plugin. For the most part I like it, but there are two issues:
- Xcode expects the Subversion utilities in yet another location,
/usr/local/subversion/bin/. So I made that work.
- Xcode’s Subversion plugin doesn’t handle bundles like nib files correctly. Known problem. So I have to check in nib files from the command line.
Another commenter asks, why Subversion?
I’ve heard both good and bad things about Subversion. The good: it discards a lot of the cruft of CVS, and it has some new features, esp. atomic multiple commits. The bad: I vaguely remember reading someone’s opinion that as far as going in a new direction, Subversion gets it all wrong. Was that in a weblog somewhere?
I guess my main reason for trying it was that it looked like it would be easier than CVS.
Oh, and if I did run into problems, the Subversion partisans I know might be more enthusiast about helping me because it promotes a product they like.
Nobody likes CVS.
I’ve set up my first personal Subversion repository.
By all accounts, Subversion is much easier than CVS. But setting up a personal repository on Mac OS X involves little steps that nobody seems to have written down anywhere yet.
First of all, don’t bother with all the packages listed in the MacDevCenter.com article “Making the Jump to Subversion”. For 10.3 “Panther,” all you need is one of these two packages:
- Martin Ott’s packages containing statically linked binaries for Mac OS X 10.3 Panther
- Metissian’s Mac OS X Subversion Packages
Alas, neither works out of the box without further fiddling. What I discovered from Metissian’s README file is that it doesn’t install its utilities under any of the standard 10.3
PATH environment variable locations. Type
echo $PATH in a Terminal window to see these locations for yourself. Instead, without any explanation (though I’m sure some Unix expert could give me theories), it puts them in
/usr/local/bin. Under a standard 10.3 configuration, such utilities are not conveniently accessible.
There are perhaps half a dozen ways to add to your
PATH environmental variable. Ask the above-mentioned Unix experts about their various merits if you want to while away an hour or two. The one I use is a
.profile file in my home directory. See this sample profile.txt file. (Update: I accidently deleted the sample file. See the comments for other options.)
Now you can use Subversion utilities like
svn from a Terminal window. Have a look at the Subversion book to see the steps needed from there to set up and access a same-machine Subversion repository.
I’ve heard rumors you need a
~/.subversion/config file to prevent a Subversion import from sucking in
.DS_Store files, but my admittedly simple tests tonight show that this fear is unfounded.
I’m sure I’ll have more to say about Subversion, esp. about sample
config files, but for now, that’s it!