| Commit from Multiple Working Copies |
| |
| 1) Expected behavior |
| |
| When committing files, listing their paths, no matter whether they belong to |
| the same work copy or not, if they all live in the same repository, they |
| should be committed successfully. |
| |
| 2) Actual behavior |
| |
| Currently, the code "condenses" all target paths, which means it |
| tries to find a common root for them, and then it tries to lock that |
| common root. If the common root is not a working copy, locking the |
| common root fails and the commit is simply aborted ("svn: <common root> |
| is not a working copy"). Multiple commit targets will only be committed |
| successfully if all they all belong to the same working copy. |
| |
| 3) Suggested behaviour |
| |
| There are three possible solutions, listed in order of difficulty. |
| |
| A Make svn_client_commit4() do multiple commits, one for each |
| target working copy. Essentially: |
| svn commit wc1; svn commit wc2; ...; svn commit wcN; |
| This solution has many drawbacks, but is the easiest one to implement. |
| Most people won't like it and it will probably never end up |
| in a Subversion release. The solution will be discarded |
| anyway later when commit functionality is rebased on WC-NG. |
| |
| B Try to change the current commit code into passing around a |
| list of access batons, and make it create truly atomic commits |
| from multiple working copies as people would expect. This solution |
| is much more involved than A. |
| People might like it, and it could even end up in a release if |
| WC-NG continues to progress at the current slow speed. |
| The solution will be discarded or amended later when commit |
| functionality is rebased on WC-NG. |
| |
| C Start working on WC-NG instead of doing this in wc-1. A lot of |
| things are still left to do before svn commit will even start using |
| WC-NG code. So the goal will change from "allow commit from multiple |
| working copies" to "help get WC-NG ready so that, one day, we can have |
| truly atomic commits from multiple working copies". |
| |
| Since this problem is being worked on as part of Summer of Code, |
| and the student has no prior experience with the Subversion code |
| base, it is reasonable to solve the problem in multiple steps, |
| implementing solution A first, and then extend that to solution B, |
| or jump straight to solution C. |
| |
| Below, we describe how solution A will be implemented. |
| |
| Because solution A will make more than one commit, we'll have to make |
| svn_client_commit4() return an array of svn_commit_info_t |
| objects, one for each commit made, instead of just a single |
| svn_commit_info_t object. Because this changes the public API, |
| we need to bump the function's revision to 5: svn_client_commit5(). |
| This API change will be reverted in case solution B or solution C |
| gets implemented, or in case solution A gets discarded before release. |
| |
| 1. We receive a list of targets to commit. |
| |
| In subversion\svn\commit-cmd.c the function |
| svn_error_t * |
| svn_cl__commit(apr_getopt_t *os, |
| void *baton, |
| apr_pool_t *pool) |
| receives the targest list first. |
| |
| Then, in E:\subversion\subversion\libsvn_client\commit.c the function |
| svn_error_t * |
| svn_client_commit4(svn_commit_info_t **commit_info_p, |
| const apr_array_header_t *targets, |
| svn_depth_t depth, |
| svn_boolean_t keep_locks, |
| svn_boolean_t keep_changelists, |
| const apr_array_header_t *changelists, |
| const apr_hash_t *revprop_table, |
| svn_client_ctx_t *ctx, |
| apr_pool_t *pool) |
| uses the targest list to do a commit. |
| |
| 2. If targets are not from the same working copy, we do the following |
| instead of aborting the commit: |
| |
| If locking the common root failed: |
| For each target path we got: |
| For each working copy root we already know (initially we don't |
| know any working copy roots): |
| Check if the current target path is below the working copy root. |
| If it is, put it into this root's group ("commit packet") |
| and continue with the next target path. |
| |
| If we end up here, no suitable working copy root was found |
| for the current target path. |
| Walk the current target path downwards, starting from the common |
| root (the root which we could not lock, in the code this is often |
| called the "base_dir"). |
| Try to lock the current directory at each step. |
| If locking succeeds, we have found a new WC root! |
| Store its access baton in the set of known working copy roots. |
| Put the current target path into the group of the root we just found. |
| |
| Here we use a struct to store each "commit packet": |
| typedef struct |
| { |
| /* Working copy root of a wc */ |
| const char *base_dir; |
| |
| /* Targets under base_dir */ |
| apr_array_header_t *targets; |
| |
| svn_wc_adm_access_t *base_dir_access; |
| |
| } commit_packet_t; |
| It would be a local variable of the function svn_client_commit5(), |
| because it is used only in the function. And it is allocated from pool. |
| |
| Now run a commit for each working copy root we found. |
| This is done just like before when the code only knew about |
| a single root, but it's done for each root in turn. |