This is not part of the official documentation of Subversion. It is aimed at the developers of Subversion, and may not reflect reality or the project community's plans. For official documentation, see http://subversion.apache.org/docs/ .
The aim of this page is to hold initial design notes and ideas on all of the topics relevant to the theory of merge tracking, in the context of enhancing merge support in Subversion after 1.8. It does not aim to comprehensively cover each topic.
Per Node or Per Tree Semantics
A 'branch' consists of a set of nodes at any given time, but the set of nodes is not fixed, it varies over time. Nodes can be added and deleted. (Can a node be moved into or out of a branch? What does it mean?)
Therefore, asking 'what changes have been made on branch A?' involves more than querying merge history for each node that is *currently* on branch A. Or does it? If every tree change (especially add, delete) is modelled as a change of the parent directory, then perhaps all the information is necessarily present just by querying the history of the current set of nodes on the target branch.
Relationships Between Branches
- "As soon as the relationship between branches ceases to be a tree, it becomes impossible for users to understand what's going on. [...] I'm in favor of the version control system not allowing you to do goofy things, and keep track of the coherent relationships between branches"
- -- Bram Cohen.
- -- "Version Control Recommended Practices" (point 9), Bram Cohen
See Perforce's "Merge Down, Copy Up".
See Perforce's "Streams". Stream types: mainline, development, release. Stream policies: "merge down, copy up".
Does Mercurial have something towards this?
Fast-forward merge should:
- Ensure minimal changes are sent to/from a WC.
- Leave merge history that clearly shows no changes were committed
- but the target was simply rebased.
Theory of committing an unrelated change along with a merge.
I think, to make sense of arbitrary merging across multiple branches, unrelated changes should not be supported. (They should be seen as part of conflict resolution.) Then, changes can be merged physically from any convenient branch in the graph. If an unrelated change is required to be "tracked" (treated as a logical change that must be merged to the target branch), then all required changes in a merge have to come physically from the specified source branch.
Doug Robinson said, for criss-cross, we must assume user may have committed an unrelated change each time, and therefore the two branches cannot be considered equivalent until either a complete merge is done one way or the user explicitly tells the VCS that the branches are equivalent.
Theory of tracking cherry-pick merges as well as full merges.
From [NewMerge]: "If you want to work on a subtree, you can make a new branch [...] that contains the subtree. We can track its relationship and merge it back to the complete tree."
Merge Each Rev-Range or Merge Each File?
Presently, Svn breaks a multi-rev-range merge into rev ranges, and the outer loop is over those ranges. An alternative is for the outer loop to be over the nodes. Advantages: splitting the merge of a single node into sub-ranges can produce spurious conflicts; merging each file in as few sub-ranges as possible avoids this.
Discussed in (one of my Wiki pages on Merge, I think) and (an old dev@ email, from Daniel Rall?).
An additional option is to merge each file according to its own merge graph, by which I really mean choosing a merge base in the node's own graph, which, if the node has had its own subtree merging, might not be where the branch root's base is. Is such a merge semantically the same as if it were done according to the branch root's merge graph? I'm thinking of the semantic subtleties such as whether unrelated changes are propagated as expected, and how reverse-merges and blocked merges are handled.
If we merge each tree according to its own graph (and if that has different semantics from merging them all using the branch root's graph), then that means we are treating each node the same as each other node, with no special treatment of a branch root. That may or may not be a Good Thing. That is a characteristic of file-based VCS's.
Reverse Merge, Blocked Merge
Idea of differentiating between "un-merging" and "blocking" changes. They have different semantics:
- If an original change 'C' was previously merged into branch 'B', then un-merging C from B means temporarily removing C from B. (Physically, this could be done by reverse-mergeing either the original change C or, if it was merged in to B on its own, by revers-merging the commit on B that merged it in.) The change C will be eligible for merging into B again. When merging from B to another branch, the combination of merging and un-merging change C will be considered as if C had never been merged into B.
- If an original change 'C' is *not* currently on branch B, then blocking C from B means marking B such that C will never be automatically merged into B. In that sense, it is like a record-only merge, but it should be possible to mark it in the history as a "blocking" merge. The semantics when merging from B to another branch should probably be: you're not going to get change C from me, but I'm not going to tell you anything about C; in that sense, it it *unlike* a record-only merge.
- Change C originating in trunk, is blocked from ever being merged to branch B (or if it was already merged to B, we now un-merge it and mark it at the same time). When merge from B to other branch, also "block" or remove C in same way. When we merge B to trunk (where C came from), we now un-merge C from trunk.
Continue after Conflicts?
Can Svn do more to enable merging to continue after hitting conflicts? Presently it stops at the end of whichever revision range it is processing.
Criss-Cross & Recursive Merge
Recursive merge strategy can resolve a criss-cross -- see the explanation linked below.
How could we implement the "recursive merge" strategy in Subversion?
Ways to match nodes in source branch (A) & target (B):
- explicit (node-id)
- last merge
- last *complete* merge
- previous merges
- natural history, if any
Different possible ways to model mergeinfo that connects to previously unconnected nodes:
- this node is "equivalent to" or "a branch of" that node (explicit)
- we have merged rX, rY, rZ from that node (or those nodes) into here (implicit link). Open to interpretation.
- we have merged "all up to rX" from that node into here (more explicit that a list of rev-ranges)
How to link two previously unrelated branches? Various vendor-branch scenarios have this requirement initially, if the local copy of the vendor's branch was not created by a first-class copy operation from the original.
This should be supported by a degenerate form of merging: an operation that sets the mergeinfo as if "all merges on A up to r100" had been merged into B. The operation should match nodes by the fall-back methods such as child-name matching and potentially content matching, and allow the user to edit the result. For example, Subversion could say "all nodes in A and B have been matched by name, except for these in A (...) and these in B (...)", and offer something akin to tree conflict resolution to allow the user to choose which of the remaining ones are adds, deletes, renames, etc.
Can merge history store enough info about a foreign branch so that we can calculate & use the latest available (currently accessible) common acncestor? So, if branch X is in a "foreign" repo and branches A, B are in a "local" repo, and we have merged A <-> X <-> B, can we than merge A <-> B if we are unable to contact X?
- Subversion Wiki, Julian Foad,
- How recursive merge resolves a criss-cross -- simple explanation, PlasticSCM blog,
- "version control tidbits", Bram Cohen,
- "Git Can't Be Made Consistent", Bram Cohen,
- "Rebasing", Bram Cohen,
- "Version Control Recommended Practices" (point 9), Bram Cohen,
- "Stream Types", Perforce,
- "CM Research Papers and Experience Reports", Brad Appleton,
- "Using git fast-forward merging to keep branches in sync",
This page was written initially by JulianFoad and may be edited by others.=~