| -*- text -*- |
| |
| This file is a scratch pad for working out current and expected |
| behaviors for various tree-conflict situations. It may be considered |
| unfinished at best -- scatterbrained is more like it. |
| |
| NOTE: As Subversion does not actually have real 'move' functionality, |
| please try to express move-ish use-cases in terms of deletes and |
| adds-with-history (copies). |
| |
| |
| =================== |
| BACKGROUND THOUGHTS |
| =================== |
| |
| This whole thing is, I think, basically about ensuring that the |
| history of a file isn't allowed to fork and then suffer the |
| unceremonious death of one of the two resulting lines of history when |
| the streams are rejoined. |
| |
| When doing an update, we don't have to worry that the working copy |
| base file has deviated from the line of history that's being deleted |
| -- other processes prevent this from happening (you can't commit |
| to a deleted file; you can't delete a file that's been modified since |
| you last updated). But you do have to worry about local mods, which |
| are a "different path" in the file's line of history than the path |
| that led to its deletion. |
| |
| When doing a merge, though, stuff gets really complicated. Now you |
| have an arbitrary line of history as the source which you are trying to |
| "lay down" overtop the line of history of the stuff in your |
| repository, and the goal remains the same: don't assume that |
| deviations in those lines should be resolved by disregarding one side |
| of the fork or the other -- that's ungood. In the best case you are |
| trying to append segments of one fork in the history road to the |
| other, and hopefully you don't leave gaps or cause deviations. In the |
| worst case, though, you're dealing with a "line of history" that's |
| made up on-the-fly (due to your left- and right-side merge sources |
| being unrelated, or only related as second cousins twice removed, or |
| something). This is where the "merge = diff + patch" paradigm really |
| starts to show its weaknesses (and maybe, just maybe, should have been |
| a use-case that Subversion disallowed ... if only it weren't so darned |
| useful for vendor branches...). |
| |
| Anyway, there appear to me to be some pretty clear goals: |
| |
| * Conflicting operations should be marked as such and require user |
| interaction to resolve |
| |
| * In such a conflicting situation, users would like to be able to |
| easily do the following: |
| |
| - determine what local mods they've made |
| - apply those mods elsewhere if necessary |
| - determine what mods conflict with the ones with they |
| - overrule the conflict and make their changes anyway |
| |
| * Tree conflicts appear to be something special, and should be |
| annotated as such. |
| |
| So a tree conflict is really just a conflict on the content of a |
| directory, much like a regular conflict is on the content of a file, |
| and a prop conflict is on the properties of a file or directory. To |
| annotate a tree conflict, we recycle the 'conflict-new' conflict file |
| slot pointing to a dropped file named dir_contents.trej. It's |
| contents can note the details of the tree conflict. [### TODO: Do we |
| need to be able to mark files as tree-conflicted, too?] |
| |
| |
| =================== |
| SCENARIO PLAYGROUND |
| =================== |
| |
| 'svn update' pulls file deletion atop file with local text mods |
| |
| NOW: File is silently unversioned. |
| |
| NEW: File is marked as added-with-history (as of the revision |
| previously in the working copy), and placed into a state of |
| conflict. There are no conflict markers, but the .trej file |
| notes that our file was deleted in the repository. User can |
| see mods with 'svn diff'; revert still works; resolved+commit |
| undoes the deletion and commits the users's mods. |
| |
| 'svn update' pulls text mods onto schedule-delete file |
| |
| NOW: Text mods are silently merged into file's text-base and file |
| remains scheduled for deletion. |
| |
| NEW: File remains scheduled for deletion, text-base is up-to-date, |
| but file is marked as conflict, and .trej file notes that |
| the file was modified in the repository. |
| |
| QUESTION: Should .trej file contain the diff of those mods |
| from the repository? |
| |
| 'svn update' pulls dir deletion atop dir containing deep text mods |
| |
| NOW: Directory is removed save for modified files (left |
| unversioned). |
| |
| NEW: Modified files are marked as added-with-history (as of their |
| working copy revisions), placed into a state of conflict, with |
| .trej files noting the deletion of the parent directory. |
| Intermediate directories are marked as added (no history) and |
| in conflict, with their .trej files noting the deletion of the |
| directory. |
| |
| 'svn update' pulls file replacement atop modified file |
| |
| NOW: Behaves as a two-step action: delete over modified file (see |
| above), then file add over existing file (which is obstructed |
| unless --force is provided. |
| |
| NEW: If the 'delete' step goes as recommended above, we'll wind up |
| in a state of conflict already with a schedule-added file. |
| Once the 'add' step hits, the operation will be obstructed |
| even if --force is provided. |
| |
| QUESTION: Is this desirable? What's the final state of the |
| file after the failed update? How do the recovery options |
| play out from here? For example, we wouldn't want the file |
| scheduled for add-with-history because that commit can't |
| succeed (another file already exists in the repository at |
| that location). |
| |
| 'svn update' pulls dir replacement atop deep-modified directory |
| |
| 'svn update' pulls file-to-dir replacement atop modified file |
| |
| NOW: Behaves as a two-step action: delete over modified file (see |
| above), then directory add over existing file (which is |
| disallowed, even if --force is supplied). |
| |
| NEW: |
| |
| 'svn update' pulls dir-to-file replacement atop deep-modified directory |
| |
| NOW: Behaves as a two-step action: delete over deep-modified |
| directory (see above), then file add over existing directory |
| left over from delete step (which fails). |
| |
| NEW: |
| |
| 'svn merge' pulls file deletion |
| |
| NOW: If there's nothing at the target location to delete, the |
| target is "skipped" (?); otherwise the target is removed from |
| version control and, if not locally modified, deleted. |
| |
| NEW: All file deletions that would affect a local file and that come |
| via merge get sanity-checked, with the client pulling the |
| left-side source version of the file down and comparing its |
| contents with the to-be-deleted working file. If they are |
| equivalent and there are no local mods to the file, it is |
| removed from version control. Otherwise, we have two sets of |
| data we'd like to keep easy track of: diffs between |
| deleted-source and wc base file, and diffs between wc base and |
| wc working files. |
| NOTE: Need more thinkin' 'round these parts... |
| |
| 'svn merge' pulls file modification atop schedule-delete file |
| |
| NOW: "Skipped missing target" message. |
| |
| NEW: File remains scheduled for deletion, but working file is |
| restored (if missing), marked as conflicted, and contents |
| carry conflict info. |
| |
| 'svn merge' pulls file modification atop missing file |
| |
| NOW: "Skipped missing target" message. |
| |
| NEW: |
| |
| 'svn merge' pulls file modification atop added file |
| |
| NOW: Conflict! |
| |
| NEW: Conflict! |