You may have noticed that I sent out no email yesterday. The reason was that my frustration with MPS has reached a higher level than usual.
I was migrating a client project from MPS 2020.3 to 2021.1. The project depends on several libraries, one of which was not migrated to 2021.1 yet. While migrating that library I found that it depends on yet another library. That last library had a branch that was migrated to 2021.1 but it turned out that the set of features between the 2020.3 and 2021.1 versions was different. Some features were added to 2020.3 but not to the 2021.1 branch.
The underlying reason was that in this project, instead of merging the older branches into the newer ones, the changes were cherry-picked instead. But some of the “cherries” were not “picked”. So I was two dependency levels away from my original project and now I had to do some Git archeology to figure out which changes are missing from the 2021.1 version of that other project.
You may think, what does all of this have to do with MPS? Isn’t it just a branching problem in a particular project? Yes, but certain propertis of MPS lead one to this.
MPS does not have a powerful, well-defined, well-documented API. The official MPS Open API is well defined but limited in power. You can use Open API to write code that manipulates models, nodes, and controls editors. But more advanced functionality requires various “hacks” that access MPS internal classes, either directly or via reflection when necessary. MPS-extensions is a collection of such hacks that becomes difficult to maintain.
Merging branches with different language versions is problematic. The best practice calls for migrating all branches to the same version before merging them together. Merging a branch with different language versions may in the worst case lead to completely lost work and having to reimplement the changes again with the new language version (this is my personal experience).
Large projects are slow to migrate to newer MPS versions. Before a project can migrate to a newer MPS versions, all of its dependencies have to be migrated and all the feature branches must be merged in. For a large project with many open branches and dependencies it takes time, there is a risk of breakage due to changes in MPS internals and problems with migrations, and no clear tangible benefit. After all, you do all that work to keep things working just the same as before. It is easy to see why other tasks take priority over migrations.
Widely used library projects have to maintain active branches for multiple MPS versions. Since projects are slow to migrate to newer versions, yet they need new features and bug fixes, it becomes necessary for widely used libraries, such as MPS-extensions or the mbeddr platform, to maintain branches for multiple MPS versions in parallel.
Now, I still don’t know exactly why that particular project has chosen to cherry-pick, rather than merge, changes between the branches. I strongly advocate for merging in such cases.
One argument for cherry-picking is that you might not want to merge a particular fix from the maintenance branch to the master branch. Perhaps the code diverged in the mean time, or you had to fix it in a quick-n-dirty way on the branch while implementing a proper fix on master. In any case, the merge would not apply cleanly and would cause unnecessary conflicts.
I would still strongly recommend to do a merge. You can even tell Git to “pretend-merge” a branch by using git merge --strategy ours
. This will make Git record the merge in the commit log but keep the files on the master branch
untouched. The next time you want to merge the branch, Git will only consider changes done after the merge point.
(Special thanks to Markus Völter for reviewing an early draft of this post.)