Of Anchors and Targets ====================== Definitions (contextually bound, of course): anchor - The name given to the directory at which an editor is rooted. That is to say, the directory baton returned by editor->replace_root() is meant to describe the anchor directory. editor - A function vtable containing methods used to describe changes to a directory tree. [see include/svn_delta.h] target - The file(s) or directory(s), relative to the anchor, designated as the actual intended subject of a given operation (update, commit, etc.). This, in practice, can be NULL if the anchor itself is the intended subject. A Little Background The concept of anchors and targets trickled out of the brains of C. Michael Pilato and Ben Collins-Sussman during the course of debugging the `svn up' command. Updates are not atomic, so each item-to-be-updated ("update target") passed to this command gets its own update procedure. The update procedure involves describing the update target in the working copy to the repository using a "reporter". The repository then, using an editor, modifies the update target in the working copy to look exactly as it does in the repository (usually in the youngest revision, but optionally, at any revision snapshot of the tree). At that time, if the update target was a directory, the editor handed to the repository was rooted at that directory. If the update target was a file, the editor was rooted at the parent directory containing that file. It became apparent rather quickly that the orderly design of the editor apparatus required more precise usage in order to get the desired results. Some of the problems in the original usage are as follows: * For directory updates, it was impossible for the driver of the editor to request that the update target be deleted. The editor's delete_entry() receives as parameters a directory baton, and the name of an entry in that directory to be deleted. If an editor is rooted at the update target, it is impossible for there to exist a directory baton describing its parent, and therefore no way to delete it as a named entry in its parent. Clearly, this limitation to the update command was unacceptable. * For file updates, having an editor rooted at the parent directory without supplying addition information to the editor's driver meant that if siblings of the update target were also "out of date" with respect to the update request, they too would be affected by the editor drive. Clearly it was unacceptable to have items in the working copy modified that should have been considered outside the scope of the requested update operation. And so the notion of anchors and targets was born. The Implementation Anchors had been present all along as the root of the editor drive, but were not going to be chosen in a way that expanded the scope of the knowledge that the editor has about the tree. Targets became the "additional information" passed to the editor driver to restrict the scope of the editor's legitimate activity to only the file or directory intended as the focus of the update. A new function, svn_wc_get_actual_target() was created and given the responsibility of deciding, given an update target path (and access to the working copy administrative directory), what the actual anchor and target of the editor drive would be. The rules are fairly straightforward: * For directory updates, if the parent directory of the update target is a valid place to root an editor, that parent directory becomes the anchor, and the update target itself becomes the target. If the parent directory is not a valid place to root an editor, the update target becomes both the anchor and the target (the target is passed as NULL). Validity of the parent directory in the working copy is determined by whether or not it is also the update target's parent directory in the repository. * For file updates, the update target file's parent directory is the anchor, and the file itself is the target. Shortly after this was implemented for updates, it became apparent that commits needed the same sort of ideology in place. That was implented as well. The Status Quo There currently exist a few kinks in the system, not (in my opinion) in the theoretical design of the anchor/target scheme, but in their handling as those items get passed around through the working copy, RA layer, and filesystem modules. Some complaints have been raised about the theoretical design of the anchor/target scheme, however, such as the need to examine the a directory target's parent, and perhaps a handful of unspecified "spidey-sense" warnings. PLEASE, if you have valid technical complaints, (re-)voice them in reponse to this mail so they can be evaluated more closely, offering better solutions if you can. For example, the editor could be changed so that some flavor of delete_entry() could delete the item represented by the baton given it (perhaps, delete_this()). I believe this to be inelegant. * The only way to get that baton would be to add or replace the file or directory, operations which are obviously tied to entirely different notions. * The only *required* place for this would be in attempting to delete a directory whose parent in the working copy was not also its parent in the repository, which would (in either the update or the commit case), result in the completely destroyed working copy. In the commit case, I suppose this is alright, but it would certainly be strange for a user who had checked out a repository subdirectory which has recently been deleted to run `svn up' and find their working copy missing. * It requires special handling for the directory that maps to the root of the repository, which simply cannot be deleted (theoretical wrongness all over the place). Currently, the solution has no "special cases". In Conclusion There are likely better ideas out there that never crossed my mind. Please submit them for review and discussion! Currently, I suspect that the most of the bugs in the present system exist because the distinction of the anchor and target notions is lost when at some point in the code path they are concatenated back together on the "server" side (or, some place that doesn't have access to the working copy module, since I think there are issues in both ra_dav and ra_local) into a single path.