The commercial effect of a strong copyleft software license (e.g. the GNU General Public License [GPL] and lesser-known GNU Affero General Public License) varies drastically depending upon how the software is used.

For an end-user product, e.g. GIMP, strong copyleft is an elegant way to force companies to contribute their modifications back to the community.

But for a developer-oriented library or other building block, e.g. Neo4j Enterprise, strong copyleft effectively prevents companies from selling applications built using the product1, and is therefore typically used in dual-licensing arrangements, as a way for the copyright holder to publish the source code but still sell commercial licenses.

Strong copyleft says it’s ok for a photographer to use GIMP in the pursuit of profit, but not ok for a developer to do likewise with Neo4j2. This is not an accident: it flows from the Free Software Foundation’s definition of freedom for software end users. Developers must be guarded against restricting this freedom while photographers can be left alone.

1. Copyleft licenses do not explicitly prohibit this selling, but because the seller must make the source code available to users under the same copyleft license, most of the market value is lost. It is still possible to sell convenience and support, or to uninformed buyers.

2. This is not entirely true for all strong-copyleft developer products, since some of them provide estoppel statements in the form of linking exceptions or permissive licenses for interface points. These statements exist so that certain types of works based on the product can be sold.

I recently found this great decision tree for centering elements with CSS. It is both accurate and helpful, which you might expect since it comes from the excellent CSS-Tricks site.

It’s been about a year since I created the Mercurial nextMerge revsets alias. It has worked well, but recently the complex Enterprise Web Library repository exposed some flaws, especially the script’s obliviousness to the need for recursion when delving down the graph to determine the safe merges that must be done in preparation for a “no-op” merge. I’ve just published a new version that fixes all known flaws. It deals with the recursion issue not by actually doing recursion (which isn’t possible with revsets), but by returning multiple changesets to let the user know that nextMerge must be rerun.

Here’s the updated code, which you can drop into your Mercurial configuration file:


[revsetalias]
# The first parameter is the merge destination. The second is the set of changesets that you'd ultimately like to merge.
# If multiple changesets are returned, recursion is required. Please rerun with the head changeset as the second argument.
#
# Implementing this within Mercurial or as an extension would be better because we'd be able to automatically perform the recursion.
nextMerge($1,$2) = nextMergeForSingleHead( $1, oldest( heads( $2 – ancestors($1) ) ) )
# In the following aliases, each parameter should be a single changeset.
# only one term at a time should return results
nextMergeForSingleHead($1,$2) = headMerge($1,$2) | safeMergeCandidateAndAncestors($1,$2) | noOpMerge($1,$2)
# the unmerged head, if only a single GCA exists
headMerge($1,$2) = $2 & firstGcaChangesets($1,$2)
safeMergeCandidateAndAncestors($1,$2) = ancestors( safeMergeCandidate($1,$2) ) & unmergedAncestors($1,$2)
# the oldest unmerged parent of the oldest multi-GCA changeset
safeMergeCandidate($1,$2) = oldest( unmergedAncestors($1,$2) & parents( oldestMultiGcaChangeset($1,$2) ) ) & noOpMergeMask($1,$2)
# The oldest unmerged changeset that descends from the first GCA and one or more other GCAs. Will always be a merge changeset.
oldestMultiGcaChangeset($1,$2) = oldest( ( unmergedAncestors($1,$2) & descendants( firstGca($1,$2) ) ) – firstGcaChangesets($1,$2) )
# the changesets that descend from the first GCA and not from any others
firstGcaChangesets($1,$2) = descendants( firstGca($1,$2) ) – descendants( greatestCommonAncestors($1,$2) – firstGca($1,$2) )
noOpMergeMask($1,$2) = not descendants( ancestors( noOpMergeChangesets($1,$2) ) )
# the oldest head of the no-op merge changesets
noOpMerge($1,$2) = oldest( heads( noOpMergeChangesets($1,$2) ) )
# the GCA-child merge changesets that, when merged into the destination, will eliminate GCAs without changing anything
noOpMergeChangesets($1,$2) = gcaChildMergeChangesets($1,$2) – descendants( unmergedAncestors($1,$2) – merge() )
# the unmerged merge changesets that are children of a GCA
gcaChildMergeChangesets($1,$2) = unmergedAncestors($1,$2) & merge() & children( greatestCommonAncestors($1,$2) )
# The first GCA of the oldest unmerged child of a GCA. If the oldest unmerged child of a GCA is a merge with two GCA parents, one is chosen arbitrarily.
firstGca($1,$2) = first( parents( oldest( children( greatestCommonAncestors($1,$2) ) & unmergedAncestors($1,$2) ) ) & greatestCommonAncestors($1,$2) )
unmergedAncestors($1,$2) = ancestors($2) – ancestors($1)
oldest($1) = first( sort($1,rev) )
# Similar to the *ancestor* predicate, but not limited to one result. Each parameter should be a single changeset.
greatestCommonAncestors($1,$2) = heads( ancestors($1) & ancestors($2) )
# Rationale:
#
# Merging algorithms depend on finding the Greatest Common Ancestor (GCA) of the two changesets being merged, so they'll work most predictably when this is not
# ambiguous. This means that, in general, we should avoid these "criss-cross" merges. When we cannot avoid them, we should first perform merges into the
# destination that get us in a position to perform a criss-cross merge that consists solely of merge changesets (i.e. a "no-op" merge). We can repeat this
# process until only a single GCA remains, at which point we can safely complete the original merge.
# References:
# http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html
# http://article.gmane.org/gmane.comp.version-control.mercurial.general/29523

view raw

nextMerge

hosted with ❤ by GitHub

I’ve just created a new package in the ReSharper Gallery containing external annotations for the Humanizer library, of which I’m a big fan. If you use Humanizer and ReSharper, open up the ReSharper Extension Manager from Visual Studio, find the Humanizer.Annotations package, and install it now! It doesn’t yet cover the entire library, but pull requests are welcome!

I greatly enjoyed this talk by Erik Dörnenburg and Martin Fowler about the responsibility aspect of software development and especially software-as-a-service. We developers should never forget how privileged we are, and with this comes the responsibility of periodically checking ourselves to make sure we’re helping to take the world down a good path.

(Post title from FDR)

We just set up a new team blog over on the Enterprise Web Library site. Also, I just posted there about why we no longer support ASP.NET session state.

Last week at the Build conference, Microsoft announced the formation of the .NET Foundation and the related open-sourcing of Roslyn, now called the “.NET Compiler Platform”. I have been building web applications atop .NET for over a decade and this is the most encouraging news I have heard in a while.

Over the past few years I have watched platforms like Python, Ruby, and of course the JVM (via “new” languages such as Groovy) steal developer excitement away from C# and .NET, at least in the open-source community. Check out this Ohloh graph. While I’m not sure what the future holds for .NET as a client-side platform (I think it largely depends on Xamarin), for server-side, line-of-business web app development, .NET continues to be a terrific platform (if you use the right libraries/frameworks on top of it) and there is absolutely no rational reason for an experienced web developer to jump ship. If Microsoft, Xamarin, and the other eventual sponsors of the .NET Foundation follow through and grow the foundation to its potential, they have a good chance of choking off the somewhat-irrational exodus from the platform.

 

I’m heavily involved in the Enterprise Web Library open-source project, and the other contributors and I sometimes run into situations in which we need to merge two complex branches together. This happens mainly because we don’t always have the time to promptly review and merge each other’s changes; sometimes a contributor will write some code that won’t make it into our canonical repo for several months. But anyway, when merging, we try to always avoid “criss-cross” merges (i.e. merges in which the two branches being merged have multiple Greatest Common Ancestors) since these can result in changes being accidentally undone and other bad things.

After toiling away for the better part of a week, I’ve created a Mercurial revsets alias called nextMerge that takes in two branch heads (merge destination and merge source) and gives you the next ancestor of source that you should merge into destination. It will just return source itself if there is no criss-crossing. If there is criss-crossing, it will tell you to first merge each parent of a merge changeset that would cause a criss-cross, and then it will tell you to actually do the criss-cross merge, but at this point it will be what I call a “no-op” merge, meaning that it should not change destination in any way since all you’re doing is merging in a merge changeset whose parents (i.e. the actual code changes) have already been merged. This no-op merge reduces the number of Greatest Common Ancestors for the original desired merge, and when we get down to just a single GCA, we can safely proceed with the original merge.

Complicated explanation, I know. Drop me a line if you want to know more. But to use nextMerge, just drop the code below into your Mercurial configuration file:


[revsetalias]
# The first parameter is the merge destination. The second is the set of changesets that you'd ultimately like to merge.
# If multiple changesets are returned, recursion is required. Please rerun with the head changeset as the second argument.
#
# Implementing this within Mercurial or as an extension would be better because we'd be able to automatically perform the recursion.
nextMerge($1,$2) = nextMergeForSingleHead( $1, oldest( heads( $2 – ancestors($1) ) ) )
# In the following aliases, each parameter should be a single changeset.
# only one term at a time should return results
nextMergeForSingleHead($1,$2) = headMerge($1,$2) | safeMergeCandidateAndAncestors($1,$2) | noOpMerge($1,$2)
# the unmerged head, if only a single GCA exists
headMerge($1,$2) = $2 & firstGcaChangesets($1,$2)
safeMergeCandidateAndAncestors($1,$2) = ancestors( safeMergeCandidate($1,$2) ) & unmergedAncestors($1,$2)
# the oldest unmerged parent of the oldest multi-GCA changeset
safeMergeCandidate($1,$2) = oldest( unmergedAncestors($1,$2) & parents( oldestMultiGcaChangeset($1,$2) ) ) & noOpMergeMask($1,$2)
# The oldest unmerged changeset that descends from the first GCA and one or more other GCAs. Will always be a merge changeset.
oldestMultiGcaChangeset($1,$2) = oldest( ( unmergedAncestors($1,$2) & descendants( firstGca($1,$2) ) ) – firstGcaChangesets($1,$2) )
# the changesets that descend from the first GCA and not from any others
firstGcaChangesets($1,$2) = descendants( firstGca($1,$2) ) – descendants( greatestCommonAncestors($1,$2) – firstGca($1,$2) )
noOpMergeMask($1,$2) = not descendants( ancestors( noOpMergeChangesets($1,$2) ) )
# the oldest head of the no-op merge changesets
noOpMerge($1,$2) = oldest( heads( noOpMergeChangesets($1,$2) ) )
# the GCA-child merge changesets that, when merged into the destination, will eliminate GCAs without changing anything
noOpMergeChangesets($1,$2) = gcaChildMergeChangesets($1,$2) – descendants( unmergedAncestors($1,$2) – merge() )
# the unmerged merge changesets that are children of a GCA
gcaChildMergeChangesets($1,$2) = unmergedAncestors($1,$2) & merge() & children( greatestCommonAncestors($1,$2) )
# The first GCA of the oldest unmerged child of a GCA. If the oldest unmerged child of a GCA is a merge with two GCA parents, one is chosen arbitrarily.
firstGca($1,$2) = first( parents( oldest( children( greatestCommonAncestors($1,$2) ) & unmergedAncestors($1,$2) ) ) & greatestCommonAncestors($1,$2) )
unmergedAncestors($1,$2) = ancestors($2) – ancestors($1)
oldest($1) = first( sort($1,rev) )
# Similar to the *ancestor* predicate, but not limited to one result. Each parameter should be a single changeset.
greatestCommonAncestors($1,$2) = heads( ancestors($1) & ancestors($2) )
# Rationale:
#
# Merging algorithms depend on finding the Greatest Common Ancestor (GCA) of the two changesets being merged, so they'll work most predictably when this is not
# ambiguous. This means that, in general, we should avoid these "criss-cross" merges. When we cannot avoid them, we should first perform merges into the
# destination that get us in a position to perform a criss-cross merge that consists solely of merge changesets (i.e. a "no-op" merge). We can repeat this
# process until only a single GCA remains, at which point we can safely complete the original merge.
# References:
# http://codicesoftware.blogspot.com/2011/09/merge-recursive-strategy.html
# http://article.gmane.org/gmane.comp.version-control.mercurial.general/29523

view raw

nextMerge

hosted with ❤ by GitHub

Happy merging!

I recently published a new essay about single-page applications:

SPAs Are Just Harder, and Always Will Be

I’ve spent the past ten years building and maintaining web applications, and during this time I’ve collaborated with a few other people to organically develop a library/framework that helps with a lot of the commonality I’ve seen across these applications. Up until now, this library has been internal, but after a couple months of hard work I’ve finally been able to release it under the MIT License as a NuGet package (the library is built on top of Microsoft’s ASP.NET). There’s not much information out there yet about the library, but over the next few months I hope to improve that situation so that if you’re interested in using it to build an app you’ll at least have a place to start.