How the 2d version tree works
This article explains how the 2d version tree works and what it exactly renders.
We realized 2d-version-tree is one of the less understood features in Plastic, so a detailed explanation is definitely worth.
Item-level trees vs changeset trees
Plastic displays the evolution of a repository rendering the Branch Explorer. It is an overall view of what happened at a global level instead of going file per file.
While this is very useful in almost all cases, there are users who miss the old days of individual version trees. Maybe they have a pre-Plastic 4.0 background, maybe they come from good-ol ClearCase or maybe they just find it more natural.
Plastic SCM works on a changeset basis: changesets are the core of the system and not individual file histories. The reason is merge tracking: merges are tracked at the changeset level and not the individual file revision level.
We actually change this when we moved to version 4 a few years ago. Before that merges were tracked at the individual item (directory/revision) level.
What does it mean? Well, when you had to merge, let’s say 1200 files (or directories) Plastic 3.0 had to walk 1200 different history trees finding merge links and ancestors. In 4.0 and beyond it only walks one single tree. There’s no way for 3.0 to outperform 4.x (and later) in merge speed because the old version simply had to do tons of work. I won’t cover all the details, but this radical change didn’t only benefit merge performance but also the overall system speed and distributed features.
A simple 2d tree scenario
Let’s go through a very simple branch/merge scenario and let’s follow the history of a single file inside our repository. The following figure shows the Branch Explorer of our repo and where the file “foo.c” was modified.
As you can see the file was added in changeset 1, later branched and changed on changeset 5, and this change was merged back to “main” in changeset 7.
How does the version tree of the “foo.c” file looks like?
Look at the following figure: you probably expect something like the graphic on the right, but this is not how Plastic works. Plastic actually created only 2 revisions of “foo.c” so far: one created on changeset 1 and the second one created on changeset 5.
You may wonder what happened during the merge: well, changeset 7 simply includes the revision loaded by changeset 5 because there is no merge conflict and hence no need to create an extra revision for the file. This is what we call a “revision replacement” because changeset 7, which is a children of 4, simply replaces the loaded revision of “foo.c” as the result of the merge.
You probably expected something like the graphic on the right of the figure above and if fact this is how things worked on Plastic 3 and before, but the underlying merge tracking mechanism changed in 4.0 and beyond. There’s no need to create extra revisions of the file for trivial merges which greatly reduces the amount of operations to be performed.
Think about it: suppose you added 10k files on a branch and later merged them back to main: 3.0 was actually creating another 10k revisions of the files on main, while 4.x and beyond simply say “hey, load them on the main branch, that’s all” saving precious time.
So, how does the 2d-version-tree renders the previous case? Check the following figure:
As you can see the 2d-version-tree decorates the “real” version tree of the file with information from the Branch Explorer (changeset history) so you can better understand what is going on with the file.
The changesets marked as “U” mean the file was unchanged on this changeset, but it is still rendered so we can understand how the file evolved through the repo history. Looking at this diagram you can understand that the revision changed on branch1 was the one finally labelled as BL001. Looking at the “raw” tree (or the history of the file) you wouldn’t have enough info to understand it.
A slightly more complex 2d-tree scenario
Look now at the following Branch Explorer:
It is slightly more complex than the previous since it involves 3 branches and a couple of merges. Our file “foo.c” was simply added on changeset “1” and changed on “9”. Look how the “real” version tree looks like:
Looking at this tree you’d never understand what actually happened to the branch! How did it end up in branch2? Was it ever merged? You can’t tell.
Now, let’s look at the 2d-version-tree:
Still it explains there are only two revisions of the file, but by rendering the “unchanged changesets” where it was rendered you can now understand how the file evolved and how it end up being labelled in BL001.
A 2d-tree with a file concurrently changed
The cases so far didn’t run into merge conflicts: foo.c wasn’t modified in parallel and involved in a real merge.
The following Branch Explorer renders a third scenario where foo.c is finally modified in parallel and merged:
Now foo.c is added in 1 as before but changed both on 4 and 9.
This is how the raw version tree looks like:
Whenever we have a *real* merge we’ll be able to render a merge link between two revisions, which greatly helps understanding the scenario, but still, the graphic above falls short to explain what actually happens to the file, doesn’t it?
You didn’t do a merge from “branch2” to “main” so, why do you have such a merge link?
That’s why the 2d-version-tree solves the scenario as follows:
I hope that reading through the previous cases helps understanding how the 2d-version-tree works and getting a better idea of why it explain the history the way it does.
Don’t hesitate to reach us if you have any questions.