SUBVERSION'S COPY AND MOVE FUNCTIONALITY Please direct all comments and suggestions to C. Michael Pilato . -- We have four use cases for 'svn cp' now. A. svn cp wc_path1 wc_path2 This duplicates a path in the working copy, and schedules it for addition with history. (This is partially implemented in 0.6 already.) B. svn cp URL [-r rev] wc_path This "checks out" URL (in REV) into the working copy at wc_path, integrates it, and schedules it for addition with history. C. svn cp wc_path URL This immediately commits wc_path to URL on the server; the commit will be an addition with history. The commit will not change the working copy at all. Possible use case: A user is working on a bug in some new code. He'd sure like some help, so he copies his work-in-progress tree of source to some URL so that others can check out that URL and help him debug. D. svn cp URL1 [-r rev] URL2 This causes a server-side copy to happen immediately; no working copy is required. [Note: we're using the phrase "tag" to mean "branch or tag"; they're the same thing, and for now we're assuming that per-installation administrative policy and/or ACLs will bother to differentiate. The svn filesystem certainly doesn't.] So how do I create a tag? Assume that the repository has a layout like this: /project1/trunk/ /project1/tags/ In the simplest case, if I want to tag the HEAD of trunk, I don't even need a working copy. I use case (D) above: svn cp http://foo.com/repos/project1/trunk \ http://foo.com/repos/project1/tags/milestone-6 Voila, no working copy needed. A "cheap" (constant-time) directory copy is made on the server. In a more complex case, suppose the state of my tree (mixed revisions and all) is exactly what I want the tag to look like. In that case, I use case (C): cd top/of/my/wc svn cp . http://foo.com/repos/project1/tags/milestone-6 I should mention that as a rule, cases (A) and (C) always notice mixed revisions when committing. -- Second, we have a new command: 'svn switch URL [-r rev]' This command performs an update on your working copy, making it reflect a new URL. This is how you "move" your working copy to a branch or tag. Really, 'svn up' is just a special case of 'svn switch', where the URL is assumed to be the one you already have. There's nothing magical about this command -- it will be fairly easy to write, we hope; instead of calling svn_repos_dir_delta() on two identical paths, the paths will be different now. The good news is that _dir_delta doesn't care one bit. It examines node-rev-ids anyway, and notices relationships between them. If, when updating your working copy from a trunk to a branch, it discovers that 80% of your files are already correct, then it won't send them. (However, if you ask to switch your working copy to a completely unrelated URL, then dir_delta probably *will* do something as extreme as removing and re-checking out a new working copy.) Also -- if the user has local mods that conflict with the switch, one may very well get an 'obstructed update' error. An update is an update, after all. Let the user beware; if she wants to switch her WC to a branch cleanly, she should make sure her WC is clean to begin with. :-) ------------------------------------------------------------------------ Here is how I plan to handle the four basic cases for `svn copy/move', as determined by the type of paths supplied as the SRC and DST arguments to the copy command (see the use cases above). * Case A - SRC is working copy path, DST is working copy path: I don't care actually care about this case. Ben Collins-Sussman (and svn_wc_copy) is handling this. :-) * Case B - SRC is repository URL, DST is working copy path: Treat this is a special checkout of SRC (at the optionally supplied revision, even), except that once the checkout is complete, you have DST scheduled for commit as a copy. Moves are disallowed in this case. * Case C - SRC is working copy path, DST is repository URL: To accomplish this operation, we drive the commit crawler/editor in pretty much the same way we would if performing an import, except we are using an existing working copy to determine the items being imported instead of disk dirents. All items in SRC tree are added, either implicitly (as a side effect of their parents having been added) or explicitly (at the top of the SRC tree, or because they have a different revision from that of their parent). Also, local modifications to items in SRC are transmitted as part of the commit as well. Moves are disallowed in this case. * Case D - SRC is repository URL, DST is repository URL: This is a freaky special commit drive, where we operate purely on our ability to split paths up into components, and then "crawl" those trees based purely on the layout of those path components. Actually, for copies this is pretty much a four-line commit: e->replace_root (dst) e->add_(file/dir) (dst_basename, copyfrom=src) e->close_(file/dir) e->close_dir e->close_edit The part that requires all the path component attention is if this is a move, because we have make sure to anchor the edit at the longest common ancestor of SRC and DST so we can delete SRC as part of the same transaction as our addition of DST. e->replace_root (longest_common_path (src, dst)) [a bunch of e->replace_dirs to get to dst's parent] e->add_(file/dir) (dst_basename, copyfrom=src) e->close_(file/dir) [a bunch of e->close_dirs back up the stack] [a bunch of e->replace_dirs to get to src's parent] e->delete_entry (src_basename) [a bunch of e->close_dirs back up the stack] e->close_dir e->close_edit