blob: 81767b8ff6f63bf96c53e1583982d51d50183098 [file] [log] [blame]
Updating/Committing with Multiple Arguments
===========================================
Currently, updates and commits work with a single directory argument,
as in
$ svn update A/D/G
or
$ svn update // implied "." argument
But users would really like to do this:
$ svn update A/D/H A/D/G iota
When there are multiple arguments, each item's revision number should
be bumped as soon as it is updated, so that updates can be interrupted
without leaving the working copy in a silly state. Also, the items
should be done in the order given, so users on slow links can put
higher priority items first.
How do we do this?
Let TL be an apr_array_header_t representing the target list, in the
order the targets appeared on the command line.
1. Convert each path in TL to an absolute path.
2. Remove redundancies from the target list: all targets that are
descendants of some other target are removed.
3. Find the longest common prefix ending with "/" for all the
targets. Strip that prefix off all targets and save it for
later.
(Kevin Pilch-Bisson has written code to help with 1, 2 and 3, see
libsvn_subr/target.c.)
4. Report the working copy state to the repository, to build a
transaction TXN that reflects the state of the working copy _as
necessary to update the requested targets_. For example:
Suppose the working copy is:
./ (at revision 3)
./iota (at revision 5)
./A/foo (at revision 3)
./A/bar (at revision 7)
./A/baz (at revision 8)
./A/B/ (at revision 10)
./A/C/ (at revision 11)
and the user runs this command in "./":
$ svn update A/bar A/C
Then TXN should look something like this:
./ (at revision 3)
./iota (at revision 3)
./A/foo (at revision 3)
./A/bar (at revision 7)
./A/baz (at revision 3)
./A/B/ (at revision 3)
./A/C/ (at revision 11)
because there's no point making TXN reflect portions of the
working copy which are not even involved in the update. Thus,
the state reporter uses TL to decide what in the working copy to
examine, as it builds TXN. Some state will be left unreported,
if the update won't affect it anyway.
5. Get an update editor, using the common prefix we saved earlier
as the path argument, and pass TL too.
/* TODO: interface change to svn_wc_get_update_editor: Take
`apr_array_header_t *targets'. */
6. Drive the update editor:
a) Call set_target_revision (edit_baton);
b) Call replace_root (), get a root_dir_baton;
c) For each target T in TL:
if (T is a directory)
{
call replace_directory()'s to get down to bottom of T;
call svn_fs_dir_delta (roots, paths, dir_baton);
/* TODO: interface change to svn_fs_dir_delta.
Instead of driving the whole edit, it should assume
that the edit has been started, and take the editor
and a directory baton as arguments. It takes over
driving until close_directory() is called on that
baton, then returns control to its caller. So it
promises never to call set_target_revision() or
replace_root(). */
}
else if (T is a file)
{
call replace_directory()'s to get down to T's parent;
somehow (involving svn_fs_file_delta?) drive the
editor to make the appropriate changes to the file;
/* TODO: interface change to svn_fs_file_delta, or
rather: current svn_fs_file_delta gets named
something new, and the old name is occupied by a
companion function to svn_fs_dir_delta: it will
take an editor and a file baton and do the
appropriate stuff. */
}
d) Call close_edit(edit_baton);
/* TODO: new interface in svn_fs.h: we need a function to
drive an editor, temporarily handing off control to
svn_fs_dir_delta() as necessary, as described in
5(c) and 5(d). */
That's it. Remember, the update editor knows the target list too, so
it uses the following rules for close_directory, close_file, and
close_edit:
close_foo (FOO) /* foo is file or directory */
{
if (FOO is a member of TL, or a descendant of a member of TL)
{
set FOO's revision number to the new revision;
}
if (FOO is a member of TL)
{
remove FOO from TL
}
/* Of course, when foo is a directory, the above code doesn't run
* when close_directory(foo_baton) is called, but rather when
* foo_baton's reference count reaches zero -- that's when
* directory closure *really* happens in the update editor.
*/
}
close_edit ()
{
foreach (member M in TL)
{
if (M is a file)
{
set to new revision;
}
else if (M is a directory)
{
recurse into M, setting everything underneath to new
the new revision, finally set M to the revision when
come out.
}
}
}
/* TODO: make the editor behave as described above. */