blob: f5bb91c4e3423f542819dee88f44452ae1b9f04f [file] [log] [blame]
SUBVERSION'S COPY AND MOVE FUNCTIONALITY
Please direct all comments and suggestions to
C. Michael Pilato <cmpilato@collab.net>.
--
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