| -*- Text -*- |
| |
| Externals handling in wc-ng |
| =========================== |
| |
| A long time ago, we introduced svn:externals in Subversion as a solution to |
| using multiple independent components. |
| |
| When updating we process svn:externals changes, by walking over all directories |
| that have (or had) svn:externals properties defined on them. |
| |
| Then we take all the old and the new definitions and put them in a list. And |
| then we start comparing them, following this schema |
| |
| * For all directories that have and/or had svn:externals |
| + For all definitions that were and/or are defined |
| - If it does not exist locally |
| o If the definition is not removed: Checkout |
| - If it is unchanged: update it |
| - If the definition is just removed, remove the external |
| - If the definition was modified |
| o If it is switchable within a repository: switch |
| o Else: remove and then reinstall the new definition |
| - If the definition is new: checkout |
| |
| (There are some small variations around relative externals, unmatched externals |
| and revision locked externals, but this is the primary idea) |
| |
| This schema allows a read-only working copy to fully follow svn:externals |
| changes without any user interaction as long as any intermediate update is |
| followed. |
| |
| For an incoming update on 'svn update' this is easy, as we have the old and the |
| new versions of the property and we can just run the external update schema. |
| |
| |
| Ok, pretty straightforward, but things get much harder once you start looking |
| at how to change svn:externals definitions locally. |
| |
| |
| Handling changes to svn:externals |
| ================================= |
| |
| The easiest way to handle this, is how we do it today (r1102626). We only apply |
| svn:externals changes when they are applied to the BASE layer. In this case we |
| can follow the original external update schema. |
| |
| But this is not what any normal subversion user likes, because you have to |
| commit a potentially invalid external description just to test it. To avoid |
| this hassle you would like to prefer changes before committing, but then the |
| external update schema doesn't work any more. |
| |
| The external update schema needs the difference between an old and a new |
| definition to update your working copy, but with local property changes you |
| only have access to the last version and probably some older version, but not |
| to the previous version. |
| |
| [[ |
| > If you do |
| > Assuming you committed |
| > $ svn propset svn:externals A . |
| > $ svn up . |
| > (updates externals to match definition nothing->A) |
| > |
| > $ svn ci -m"" |
| > $ svn propset svn:externals B . |
| > $ svn update . |
| > |
| > Then currently this updates the directory to the definition A (just to be |
| > safe), while you (and many others) would like it to go to B. |
| > |
| > So assume the preferred scenario this would do the update A->B |
| > |
| > But then you have the even likely case |
| > $ svn propset svn:externals C |
| > $ svn update . |
| > |
| > This would then apply A->C, so this could potentially break your working |
| > copy, by leaving traces of B. |
| > |
| > We really need the step B->C here, but there is no way to access B, because |
| > it wasn't committed. It just lived in a local property change. |
| ]] |
| |
| Before Subversion 1.6, it wasn't such a big problem when the external update |
| schema broke down on the working copy where svn:externals were edited. You just |
| deleted the subdirectory and ran svn update again. But then we introduced file |
| externals.... |
| |
| "We really need some store with old (applied) svn:externals definitions, so we |
| would only have to update externals from that to the latest definition" |
| (Issue #2267, #3351, #3823) |
| |
| |
| Wc-1.0 File Externals |
| ===================== |
| |
| Just before we released Subversion 1.6 somebody noticed that you could switch |
| an added file (really a bug) and that you could use that to do svn:external |
| like things with that. Within a few days some support to manage these externals |
| was added and the file externals as we know them were born. |
| |
| These file externals were added as part of the working copy where they are |
| located with a special registration in their svn_wc_entry_t. (Note: this didn't |
| have to be the same working copy as where the definition lived). |
| One noticable limitation was that they had to be from the same repository as |
| the directory they are placed in. |
| |
| The existing externals update schema handled did this really well and file |
| externals were released as part of Subversion 1.6. |
| |
| |
| But when you mix the feature of editing svn:externals and file externals, |
| things get ugly. If you add a file external to svn:external; then perform an |
| update and then revert the definition of the file external before committing, |
| you will find that this file external will still be in your working copy. |
| (And there is no real way to fix this). |
| |
| |
| The 'file externals' problem |
| ============================ |
| |
| When we introduced file externals, we expected this to be easy to maintain, |
| as essentially we already supported switched files. |
| |
| But then somebody noticed that you could delete file externals via 'svn rm', |
| and then the external was removed... but not where it was placed, but at |
| the place where it was added from. So we added some tests to detect that |
| specific case. We never had that problem with directory externals. |
| |
| (Same problem with moving files) |
| |
| Then somebody noticed that merge was recording more merge information when |
| you had file externals. So we added some tests to detect and work around that. |
| |
| (And I think about 20 similar cases in different places of our code). |
| |
| We never expected any of this when we introduced file externals as an |
| easy feature. |
| |
| [[ |
| <@cmpilato> A file external -- like any other external -- shouldn't be an add at all in a copy situation. |
| <@Bert> cmpilato: But a file external is not like 'any other external'... It is like 'any other switch' |
| <@cmpilato> That mindset is 90% of the problem with file externals. |
| <@cmpilato> They were never intended to behave as switches. |
| <@cmpilato> Switch was simply the low-hanging mechanism that was used to shoehorn them in. |
| <@cmpilato> They were always intended to behave like dir externals. |
| <@cmpilato> hrm. we don't appear to be honoring the --ignore-externals option to 'svn cp WC WC' either... |
| ]] |
| |
| "Why can't file externals be more like 'normal' externals?" |
| (Issue #3589, #3518, #3351, #3665, #3816, #3843) |
| |
| |
| The WC-NG Externals store |
| ========================= |
| |
| After holding back several changes to more file externals corner cases on |
| "we should really design file externals before adding more features" these |
| three questions got me thinking: |
| |
| "We really need some store with old (applied) svn:externals definitions, so |
| we would only have to update externals from that to the latest definition" |
| |
| "Why can't file externals be more like 'normal' externals?" |
| |
| And later (via private mail): |
| "Will this allow to exclude externals from WC? (Use-case: watching repo |
| of public project with number of externals)" |
| |
| So we have three separate requests to store some information on externals, |
| which we couldn't store before. |
| |
| - Handling externals changes - |
| |
| For every applied external we would like to have: |
| * Where it was defined |
| * What is its definition |
| * If it has a fixed revision or not. |
| This allows applying svn:externals changes from any previous state to the last |
| state by just comparing the actual propery values against what is stored. |
| |
| - Moving file externals into their own storage - |
| |
| If we want to make file externals like 'normal externals, we should remove |
| their presence from their parent working copy and just handle them as |
| independent filesystem objects. |
| |
| For that we need some storage location of 'all the relevant information it |
| would have in the past'. |
| * Repository, repos_relpath, revision. |
| * Presence (always status normal. Can't be deleted, moved, etc.) |
| * Kind (always file or symlink) |
| * Properties |
| * Checksum |
| * Changed date, author, revision |
| * Recorded size and mod time |
| |
| - Excluding externals - |
| |
| To allow excluding externals we need some kind of presence flag containing |
| 'normal' and 'excluded' per external. |
| |
| |
| ### Combine these three ideas and you have the EXTERNALS table for format 29 |