summaryrefslogtreecommitdiff
path: root/.html/dev/merging-structural-changes.html
diff options
context:
space:
mode:
authorOwen Jacobson <owen.jacobson@grimoire.ca>2015-07-03 22:31:49 -0400
committerOwen Jacobson <owen.jacobson@grimoire.ca>2015-07-03 22:35:09 -0400
commit76aed6ef732de38d82245b3d674f70bab30221e5 (patch)
treed50e9a296d91ef8a49bcb29c3e80096f200a3c26 /.html/dev/merging-structural-changes.html
parent92f66d3e3a0996bb1fad9dc83d7e184f92673e5d (diff)
Fuck it, serve the files directly.
Diffstat (limited to '.html/dev/merging-structural-changes.html')
-rw-r--r--.html/dev/merging-structural-changes.html156
1 files changed, 156 insertions, 0 deletions
diff --git a/.html/dev/merging-structural-changes.html b/.html/dev/merging-structural-changes.html
new file mode 100644
index 0000000..e5c8795
--- /dev/null
+++ b/.html/dev/merging-structural-changes.html
@@ -0,0 +1,156 @@
+<!DOCTYPE html>
+<html>
+<head>
+ <title>
+ The Codex »
+ Merging Structural Changes
+ </title>
+
+ <link
+ rel='stylesheet'
+ type='text/css'
+ href='http://fonts.googleapis.com/css?family=Buenard:400,700&amp;subset=latin,latin-ext'>
+ <link
+ rel="stylesheet"
+ type="text/css"
+ href="../media/css/reset.css">
+ <link
+ rel="stylesheet"
+ type="text/css"
+ href="../media/css/grimoire.css">
+</head>
+<body>
+
+<div id="shell">
+
+ <ol id="breadcrumbs">
+
+ <li class="crumb-0 not-last">
+
+ <a href="../">index</a>
+
+ </li>
+
+ <li class="crumb-1 not-last">
+
+ <a href="./">dev</a>
+
+ </li>
+
+ <li class="crumb-2 last">
+
+ merging-structural-changes
+
+ </li>
+
+ </ol>
+
+
+
+ <div id="article">
+ <h1 id="merging-structural-changes">Merging Structural Changes</h1>
+<p>In 2008, a project I was working on set out to reinvent their build process,
+migrating from a mass of poorly-written Ant scripts to Maven and reorganizing
+their source tree in the process. The development process was based on having
+a branch per client, so there was a lot of ongoing development on the original
+layout for clients that hadn't been migrated yet. We discovered that our
+version control tool, <a href="http://subversion.tigris.org/">Subversion</a>, was unable
+to merge the changes between client branches on the old structure and the
+trunk on the new structure automatically.</p>
+<p>Curiousity piqued, I cooked up a script that reproduces the problem and
+performs the merge from various directions to examine the results. Subversion,
+sadly, performed dismally: none of the merge scenarios tested retained content
+changes when merging structural changes to the same files.</p>
+<h2 id="the-preferred-outcome">The Preferred Outcome</h2>
+<p><img alt="Both changes survive the
+merge." src="/media/dev/merging-structural-changes/ideal-merge-results"></p>
+<p>The diagram above shows a very simple source tree with one directory, <code>dir-a</code>,
+containing one file with two lines in it. On one branch, the file is modified
+to have a third line; on another branch, the directory is renamed to <code>dir-b</code>.
+Then, both branches are merged, and the resulting tree contains both sets of
+changes: the file has three lines, and the directory has a new name.</p>
+<p>This is the preferred outcome, as no changes are lost or require manual
+merging.</p>
+<h2 id="subversion">Subversion</h2>
+<p><img alt="Subversion loses the content
+change." src="/media/dev/merging-structural-changes/subversion-merge-results"></p>
+<p>There are two merge scenarios in this diagram, with almost the same outcome.
+On the left, a working copy of the branch where the file's content changed is
+checked out, then the changes from the branch where the structure changed are
+merged in. On the right, a working copy of the branch where the structure
+changed is checked out, then the changes from the branch where the content
+changed are merged in. In both cases, the result of the merge has the new
+directory name, and the original file contents. In one case, the merge
+triggers a rather opaque warning about a “missing file”; in the other, the
+merge silently ignores the content changes.</p>
+<p>This is a consequence of the way Subversion implements renames and copies.
+When Subversion assembles a changeset for committing to the repository, it
+comes up with a list of primitive operations that reproduce the change. There
+is no primitive that says “this object was moved,” only primitives which say
+“this object was deleted” or “this object was added, as a copy of that
+object.” When you move a file in Subversion, those two operations are
+scheduled. Later, when Subversion goes to merge content changes to the
+original file, all it sees is that the file has been deleted; it's completely
+unaware that there is a new name for the same file.</p>
+<p>This would be fairly easy to remedy by adding a “this object was moved to that
+object” primitive to the changeset language, and <a href="http://subversion.tigris.org/issues/show_bug.cgi?id=898">a bug report for just such a
+feature</a> was filed in
+2002. However, by that time Subversion's repository and changeset formats had
+essentially frozen, as Subversion was approaching a 1.0 release and more
+important bugs <em>without</em> workarounds were a priority.</p>
+<p>There is some work going on in Subversion 1.6 to handle tree conflicts (the
+kind of conflicts that come from this kind of structural change) more
+sensibly, which will cause the two merges above to generate a Conflict result,
+which is not as good as automatically merging it but far better than silently
+ignoring changes.</p>
+<h2 id="mercurial">Mercurial</h2>
+<p><img alt="Mercurial preserves the content
+change." src="/media/dev/merging-structural-changes/mercurial-merge-results"></p>
+<p>Interestingly, there are tools which get this merge scenario right: the
+diagram above shows how <a href="http://www.selenic.com/mercurial/">Mercurial</a> handles
+the same two tests. Since its changeset language does include an “object
+moved” primitive, it's able to take a content change for <code>dir-a/file</code> and
+apply it to <code>dir-b/file</code> if appropriate.</p>
+<h2 id="git">Git</h2>
+<p>Git also gets this scenario right, <em>usually</em>. Unlike Mercurial, Git does not
+track file copies or renames in its commits at all, prefering to infer them by
+content comparison every time it performs a move-aware operation, such as a
+merge.</p>
+ </div>
+
+
+
+<div id="comments">
+<div id="disqus_thread"></div>
+<script type="text/javascript">
+ /* * * CONFIGURATION VARIABLES: EDIT BEFORE PASTING INTO YOUR WEBPAGE * * */
+ var disqus_shortname = 'grimoire'; // required: replace example with your forum shortname
+
+ /* * * DON'T EDIT BELOW THIS LINE * * */
+ (function() {
+ var dsq = document.createElement('script'); dsq.type = 'text/javascript'; dsq.async = true;
+ dsq.src = 'http://' + disqus_shortname + '.disqus.com/embed.js';
+ (document.getElementsByTagName('head')[0] || document.getElementsByTagName('body')[0]).appendChild(dsq);
+ })();
+</script>
+<noscript>Please enable JavaScript to view the <a href="http://disqus.com/?ref_noscript">comments powered by Disqus.</a></noscript>
+<a href="http://disqus.com" class="dsq-brlink">comments powered by <span class="logo-disqus">Disqus</span></a>
+</div>
+
+
+
+ <div id="footer">
+ <p>
+
+ The Codex —
+
+ Powered by <a href="http://markdoc.org/">Markdoc</a>.
+
+<a href="https://bitbucket.org/ojacobson/grimoire.ca/src/master/wiki/dev/merging-structural-changes.md">See this page on Bitbucket</a> (<a href="https://bitbucket.org/ojacobson/grimoire.ca/history-node/master/wiki/dev/merging-structural-changes.md">history</a>).
+
+ </p>
+ </div>
+
+</div>
+</body>
+</html> \ No newline at end of file