Using Subversion for the first time brings with it some surprises. Matching-but-not-identical repository URLs can cause hard to understand problems with baffling error messages. And, not understanding the importance of Subversion’s local meta-data can get things off to a bad start. The hacks described below can help you diagnose and fix some of these problems. The discussion below describes these problems on a Windows machine, but the problems are very similar on unix-like systems as well.
I was new to Subversion. I created a repository, then created a “projects” subdirectory in the repository via the TortoiseSVN repo-browser. This was a mistake, but I didn’t know it at the time - it was a mistake because Subversion didn’t get the opportunity to write its hidden meta-data files to my local “projects” subdirectory, but I had no idea. I then added four projects to Subversion one by one. The directory structure on disk matched the structure in Subversion, so I figured everything was ok. However, as far as Subversion was concerned, my local projects were all totally independent, it didn’t know what to do with the parent directory since it had never been added directly, and trying to synchronize all of the projects from the parent directory resulted in a “not versioned” error. I had created a mess.
Subversion keeps meta-data on the local file system in a hidden directory called “.svn”. This meta-data tells Subversion whether or not the files in that directory really are under control of Subversion. If the meta-data doesn’t contain information about the local files, Subversion won’t let you update or commit those files, even if an exact match already exists in the repository. Since I had created my “projects” directory in the repository, and not “added” the directory from my local filesystem, the local meta-data for the “projects” directory was not there.
I attempted to add the “projects” parent directory non-recursively to Subversion, thinking that would solve the problem. However, doing an update from the parent directory now resulted in a “file already exists” error for every project subdirectory, even though those projects were versioned in Subversion and matched the Subversion repository.
Some quick searching on this problem turned up some suggested solutions that were usually a combination of stating the obvious plus an IT-related insult, like “Just delete everything and update from Subversion. If it doesn’t work, you do have backups, don’t you????” However, when an entire subdirectory and everything below it causes the “file already exists” error, it’s not so easy to just blow everything away. There are likely hundreds of files in those subdirectories that aren’t in the repository, such as compiler output (that may have taken a long time to generate), notes that are in text files that I don’t want to delete yet, and perhaps graphics or source code that is in a half-baked state that should probably be cleaned out but that I don’t feel comfortable cleaning out just yet. And if that subdirectory contains a few gigabytes of output data, backing it up and restoring it isn’t fun when I’m just trying to get Subversion to work.
Luckily, there is a way to make the project parent directory know about the subprojects. Note that this is a hack, and I don’t recommend it, though it worked like a charm. I was unable to find a useful “correct” way to fix this problem from the svn command line, so we have to manually make things right.
If we want to tell a parent directory about sub-projects that it should know about, but doesn’t due to my incompetence in setting up the projects originally and Subversion’s reluctance to fix my mistakes for me, we do a simple text-editing hack. The hack involves editing a file in the parent directory’s “.svn” subdirectory. The file is called “entries” and it tells Subversion what it knows about that directory.
If you happen to have a local project checked out from Subversion, go look for the hidden “.svn” directory. You should find the “entries” file there. Look inside it. It is an easy to read XML file and it can be edited with any text editor (on Windows, the command-line “edit” works great and handles unix end-of-lines nicely). Saving the file with “Windows” style end-of-lines didn’t seem to cause any trouble, so don’t worry about that.
In the “entries” file, there are “entry” tags for each file and subdirectory that is under version control. Many of those entry tags refer to the individual files in the directory. We can’t easily recreate those entries, because they include a lot of information about the file, like dates, checksum, and more. Don’t touch those entries. For individual files that give you a “file already exists” error, the best approach really is to delete the local file and then update the local file from the repository, assuming again that the file in the repository is up to date in the first place. The “entry” will then be created for the file, including the proper dates and checksum.
For directories, we’re in luck. The entry for a directory looks like this:
< entry name="directoryname" kind="dir" />
So, my situation was this. I had a project directory called “projects”, and four subprojects called “potatogun”, “plasticwrap”, “sinker” and “oatmeal”. My subversion repository had the same structure - all four projects were in a “projects” folder in the repository. The local subversion “entries” file for the “projects” directory just didn’t know it.
By editing the “entries” file in the “projects/.svn” directory and adding four “entry” lines exactly like the one above, but with “potatogun”, “plasticwrap”, “sinker” and “oatmeal” in place of “directoryname”, I magically had everything back to normal. I could update the subprojects with no problem. All was well.
Except that I left out one major detail. In two of the subprojects, I somehow told Subversion that my repository was called “file:///C:/svn”. In the other two projects, I had called the repository “file:///c:/svn” - note the lower-case “c” in the second version. Of course, on a Windows machine, these two are the same thing. As far as Subversion is concerned, however, these two things are different.
Subversion this time tells us that “svn: ‘file:///c:/svn/projects/potatogun’ is not the same repository as ‘file:///C:/svn’” - again, note the case difference in the drive letter.
So the parent project thinks the repository is located at C:/svn while the subproject thinks the repository is located at c:/svn. Subversion believes these two are different, and I have no problem with that. I just need a way to fix it. There are some explanations out there in the Googleverse about why the problem happens, but no good solutions.
The answer again lies in the “entries” file. We know perfectly well that our projects are all coming from the same repository. Subversion does a direct case-sensitive string compare when determining when two repositories are the same, so two URLs that mean the same thing will generate the “not the same repository” entry if the urls are entered in slightly different ways. There are many examples of this, not just case-sensitive problems. The “entries” file stores the URL of the repository that was specified when the project was first checked out of Subversion, and all we have to do is edit all of our “entries” files and make them all match.
The first “entry” tag in the entries file contains two items that need to be changed. One is the “url=” line, and the other is the “repos=” line. The “url=” line contains the URL that points to the subversion subfolder that contains the file. The “repos=” line points to the parent repository.
So, in my example, in my “projects/.svn/entries” file, the lines look like this:
url=”file:///C:/svn/projects”
repos=”file:///C:/svn”
The “projects/potatogun/.svn/entries” file has lines that look like this:
url=”file:///c:/svn/projects/potatogun”
repos=”file:///c:/svn”
Again, note the lower-case drive letter in the potatogun entries. Subversion thinks these two projects came from two different repositories, so we need to make them match. The convention is to use upper-case drive letters. You can choose to use lower-case if you like, Windows understands both, but if you ask Windows what the drive letter is, it will give you an upper-case response.
We now edit “/projects/potatogun/.svn/entries” with a text editor so that the lines look like this:
url=”file:///C:/svn/projects/potatogun”
repos=”file:///C:/svn”
It is important to change BOTH the “url” and “repos” entries so that the repository paths match. If there is an inconsistency between those two lines, Subversion will think it didn’t read the “entries” file correctly and it will get really confused.
We also have to do this for any subdirectories in project potatogun, and for any other subprojects that might have mismatched repository URLs. If you have the means to do a case-sensitive, recursive search-and-replace on all “entries” files, do that. Of course, if the source repositories REALLY WERE different in the first place, this hack isn’t going to help, but if you’re still reading at this point, you know that already.
Now we can go to the “projects” directory and type “svn up” - instead of getting the “not the same repository” error, we get a nice “At revision 492.” message like we should.
Now that my projects are all nicely set up, I know enough about Subversion to do it right the first time. But for Subversion newbies, equivilent-but-not-equal repository URLs can be very frustrating. It’s also surprising when Subversion can’t figure out that the local file structure exactly matches the repository. Then, when you try to let Subversion know that the local files match the repository, the “file already exists” errors are even more frustrating. But with a little hacking of the “entries” file, we can fix our mistakes and get back to work.