| |
| *** Branches and Tags -- Brainstorm *** |
| or |
| M7: The Final Frontier |
| |
| |
| First, let's define what it means to create branches/tags in a |
| repository. Simply put, the repository creates a new dirent which is |
| just an "fs_link" to an existent directory node-rev-id. It's the same |
| process that happens when we build transactions. |
| |
| That reminds me of an important point; sometimes the user wants to |
| make a "true" copy of something (svn_fs_copy), and sometimes the user |
| just wants to make a "cheap" copy of something (svn_fs_link). |
| |
| [svn_fs_copy] |
| |
| * Use case: the user has a file or dir that she wants to duplicate. |
| |
| Instead, the user would type 'svn cp foo bar', and bar would |
| automatically be scheduled for addition (and have other ancestry |
| info recorded). |
| |
| * do a system copy of foo to bar, including props and text-base |
| * schedule 'foo' for deletion in its original parent |
| * schedule 'bar' for addition -- with ancestry -- in a new parent |
| |
| The commit would supply "copyfrom" args to the editor, which would |
| then run 'svn_fs_copy'. The new node-rev-id for bar would then |
| contain special copy-info in its skel. |
| |
| This works as of M6, for files at least. |
| |
| |
| [svn_fs_rename] |
| |
| * Use case: the user wants to rename/move a file or dir. |
| |
| In M6, this is 'svn mv src dst' is just a wrapper (literally!) |
| around 'svn cp src dst; svn rm src'. |
| |
| Here's the problem: why on earth wouldn't the commit-process just |
| run 'svn_fs_copy' when it sees the editor's "copyfrom" args on bar? |
| We want 'svn_fs_rename' to run instead, which means that the editor |
| somehow needs to realize that the deletion and addition are |
| connected. |
| |
| Cmpilato sez: svn_fs_rename exists only to "fill out" the |
| filesystem API. But we never expect an svn client to call it; our |
| editor model has already defined that a 'move' is an 'add with |
| history and a delete." So we're fine. |
| |
| |
| [svn_fs_link] |
| |
| * Use case: the user wants to create a tag or branch. |
| |
| Because the user may not have a large-enough tree checked out, I |
| think it's best that the 'svn branch' and 'svn tag' subcommands |
| take two URLs as "source" and "dest" arguments. This even allows |
| someone to make tags/branches without a working copy. |
| |
| (Eventually, I suppose, we could loosen the restriction and allow |
| people to refer to paths in their working copy. As with 'svn log', |
| the wc paths are just converted to repository URLs anyway.) |
| |
| So a user might type |
| |
| % svn tag http://svn.collab.net/repos/svn/trunk \ |
| http://svn.collab.net/repos/svn/branches/one |
| |
| And this command would cause a simple 'svn_fs_link' in the |
| repository. (### RA mechanism for this? ) |
| |
| To remove the tag, a user could check out the repository's root and |
| just delete the tags/m5 directory and commit. Or maybe a different |
| subcommand could make this an easier task -- one which wouldn't |
| require a big working copy. |
| |
| [ Nothing could be easier: |
| |
| svn remove http://svn.collab.net/repos/svn/branches/one |
| |
| --xbc ] |
| |
| |
| [dir_delta updates] |
| |
| * Use case: the user wants to move their existing working copy onto a |
| tag or branch. |
| |
| I guess this is just a plain old update, except that dir_delta |
| isn't going to be called with identical path arguments (like it |
| usually is.) |
| |
| dir_delta notices a relationship between the two paths, and |
| therefore won't do anything stupid like delete and re-checkout your |
| whole working copy. Instead, it sends patches as needed, based on |
| relationships between node-rev-ids. (At least, according to |
| cmpilato, dir_deltas already *should* behave this way.) |
| |
| As usual with updates, local mods will be preserved, merged, or |
| come into conflict. If the user really wanted a perfectly clean |
| branch-move, they shouldn't have local mods lying around... or they |
| should just checkout the branch directly! |
| |
| THE REAL WORK: instead of running log_do_committed during the update, |
| the editor should be running a *new* log command that tweaks the |
| 'ancestry' URLs in the entries files. The entries files need to |
| point to the new branch URL. [Ben sez: this same URL-tweaking |
| functionality is needed when running 'svn cp dir1 dir2'.] |
| |
| |
| [MERGING] |
| |
| Assume that our working copy corresponds to /branches/one in the |
| repository. /branches/one started out as a cheap copy of /trunk |
| some weeks ago, but both directories have received commits since |
| then. |
| |
| Now we want to absorb any new changes from /trunk into our working |
| copy branch. |
| |
| Here's what needs to happen: |
| |
| * somebody calls dir_delta with two special arguments. The first |
| argument is the common ancestor of /branches/one and /trunk; in |
| other words: "/branches/one in the revision where it first came |
| into existence". The second argument is "/trunk in the head |
| revision." |
| |
| * Now the changes start coming down to the working copy. |
| Unfortunately, the working copy can't just use its vanilla |
| update editor to apply them. Why not? Because dir_deltas |
| thinks that the working copy is different than it really is -- |
| it thinks the working copy looks like the very origin of |
| /branches/one! |
| |
| Therefore, one of two things need to happen: |
| |
| * dir_delta is told to send fulltext (because any svndiff |
| patches *wouldn't* correctly apply to pristine files!) The |
| client then does a diff between the fulltext and the |
| pristine file, and applies that patch to the working file. |
| The fulltext is then deleted, and the pristine copy is NOT |
| touched! (Otherwise our next commit would be screwy!) |
| |
| * dir_delta sends a flexible, *contextual* diffs against the |
| head of /trunk. We then apply these diffs directly to the |
| working files (again, leaving the pristine files |
| untouched.) |
| |
| |
| [GENETIC MERGING] |
| |
| In theory, every time we do a merge, we should *store* the second |
| argument to dir_delta in the working copy somewhere. In other |
| words, we remember exactly the "tip" of trunk from which changes |
| were merged. |
| |
| Now, the next time you merge, this "tip" becomes the *first* |
| argument (base) to dir_delta. This prevents you from re-merging |
| patches you already have, and solves a major CVS annoyance. |
| |
| Really, I'm not sure we ever need to track each merged changeset |
| one by one. That might be unnecessary here. |
| |