VersioningRight - refining the problem by guiding implementation towards the working solution

VersioningRight details

Introductory remarks, basic terminology and conventions

    Enter version of the branch to edit: 
    Enter new contents of the branch: 

In most cases command prompts are equipped with contextual autocompletion functionality that is triggered in command-line mode by pressing tab key. Suggestions for autocompletion appear at the new line after the prompt separated by ‘\t’ character:

    Enter version, which will be used to append new support snapshot to: 
0.x.x  1.x.x

In the case of triggered autocompletion, actually supplied command prompt argument value appears on duplicate command prompt line after the colon:

    Enter version, which will be used to append new support snapshot to: 1.x.x 
"repositoryExample" => 
   x.x.x    
     |      
   ------   
  /      \  
0.x.x  1.x.x

Otherwise (command does not modify version tree) command output is either the requested value itself or no output at all.

%Repository:repositoryExample> :show
"repositoryExample" => 
   x.x.x    
     |      
   ------   
  /      \  
0.x.x  1.x.x
%Repository:repositoryExample> :showContent 
    Enter version: x.x.x
"some content"
%Repository:repositoryExample> :newSupportSnapshot 
    Enter version, which will be used to append new support snapshot to: 
0.x.x  1.x.x
    Enter version, which will be used to append new support snapshot to: 1.x.x 
"repositoryExample" => 
   x.x.x    
     |      
   ------   
  /      \  
0.x.x  1.x.x
         |  
       1.x.0

Basic commands

First simplest snippet of :show command run on empty initial repository:

%Repository:repositoryExample> :show
"repositoryExample" => 
x

As you might have noticed, :show command does not have command prompts. This is because it does not require input/arguments. Not only in this particular case, but ever. In that sense :show is a simplest command possible in VersioningRight. It just shows visualization of current state of the repository in a form of a version tree. As long as repository has initial state, version tree looks as x. This is because there is only one solution provided (the one with unique descriptor x), which is, in fact, empty:

%Repository:repositoryExample> :showContent 
    Enter version: x 
""

Let’s fix it by using :editBranch command on the only solution we currently have - x:

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x 
    Enter new contents of the branch: some content

Now :showContent outputs something different than an empty string:

%Repository:repositoryExample> :showContent 
    Enter version: x
"some content"

This is our current and latest solution - some content. If we want to change it again - :editBranch command is to the rescue. We can change it as many times as we want. But there is a pitfall - it will be our only one current and latest solution. Meaning there is no possibility to track previous solutions (history) and choose between them. Sometimes that’s all we want, especially when things are simple - latest one is the best one, no matter how many times we change our mind about what is best, right?

Tell it to your friend you share your best solution with! I mean, she will have a good point - some content is kind of bleaky and, if fact, does not solve anything. ‘The best solution is the one that works’ she says. Probably you should come up with something else. ‘No problem!’ you say and come up with another one:

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x 
    Enter new contents of the branch: some different content
%Repository:repositoryExample> :showContent 
    Enter version: x
"some different content"

This is the best solution, you think. But your friend doesn’t share your optimism. No matter how many solutions you show, she will always have a point why it is not the best one. Good friend? Of course! She cares about you to come up with the best solution possible. Actually, is there a better way to go around this? You both want the best possible solution after all… What if you were able to share several solutions simultaneously? That would probably speed things up with the decision making process about what solution works - you show your friend two solutions at once for her to tell you which one to throw away so that you could at least understand faster which one is not the best you’re looking for. In order to share two solutions simultaneously, you need to be able to have two versions of it that exist side by side. Can VersioningRight do that? Of course! But first we need to do something that would let us understand another basic principle - revisions.

Revisions

%Repository:repositoryExample> :toggleRevisions
"repositoryExample" => 
 0 
 | 
 x 

As you can see, command :toggleRevisions enables output of another version 0 that is there in the version tree of initial repository. We have nothing to do with it. Seems like it has been sitting there all along, we just didn’t have chance to see it. VersioningRight has been hiding it for the sake of simplicity. We didn’t want to know that there are two solutions. But now we need to know more.

%Repository:repositoryExample> :showContent 
    Enter version: 0
""
%Repository:repositoryExample> :showContent 
    Enter version: x
"some different content"

So, we have two solutions - one empty (which is 0), another non-empty (which is x). The empty one is what we started with, remember? Can we modify this another version 0 that is already there to share another solution with friend?

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: 0
You should enter branch version. Aborting operation

No! It says it is not a branch. What is it then? Ah, it appeared after executing command :toggleRevisions, so it must be revision. What is so special about it that it got another name? We kinda already know the answer. This is because we can’t modify it - :editBranch command didn’t work on it. So, we figured out, there are actually two things in an initial repository - version that you cannot modify (0) and another version that you can modify (x). For that particular reason 0 is called revision and x is called branch. Revisions cannot be modified while branches can. Initial repository is always started with two empty solutions - one editable, another non-editable. We have been playing with the editable one all along. It gave us possibility to come up with something new. Even though is seems useless, you have to admit that there is nothing like the revision 0 - it is an empty solution that both works and doesn’t work at the same time! It works because it kinda always does, no matter what. But you need to come up with your own. In that sense it doesn’t work.

Now that we’ve learned we cannot modify existing revisions, how can we add another one? :newRevision command is to the rescue:

%Repository:repositoryExample> :newRevision
    Enter commit message: some solution
    Enter version, which will be used to append new revision to: x 
"repositoryExample" => 
0
|
x
|
1

What do we see here? It asked us for some commit message! What is the purpose of this? Very simple. It allows us to shortly describe how our solution is better than the previous one. Everything is better than empty solution! Hence the entered value of commit message: ‘some solution’. One more question: “Why is it called commit message, not revision message?” Answer: "Because while revision represents full content of a branch at some specific point, commit represents difference between revisions". Revision 1 is a snapshot of branch x, while commit 1 is a difference between revision 0 and revision 1.

Also :newRevision asked about the ‘version to append new revision to’. It means that revisions are somehow ‘appended’. Does it have something to do with the way versions appear in the version tree? Looks like it does. Judging by the :newRevision command output and its second argument value, 1 has been appended to x. Following this logic, we can say that x has been appended to 0. Hm. Did we do this? Does not seem so. Looks like 0 and x were there as a part of the initial repository. Was it indeed there or did we do something to append x to 0? The answer is yes (0 and x were there as parts of the initial repository) and yes (we did something to append it). How could that be? Simple. Behind the scenes we did repository instantiation. Meaning of this operation is to allow the possibility of coming up with your own solution to the problem. Empty solution 0 is always there for any problem no matter what. And by instantiation of repository we append x to 0 with that allowing to modify empty solution how we please. To understand more, we need to learn about something else - experimental branches.

Experimental branches

%Repository:repositoryExample> :newExperimentalBranch 
    Enter name of the branch to be created: exp1
    Enter version, which will be used to append new experimental branch to: x 
"repositoryExample" => 
     0     
     |     
     x     
     |     
 -----     
/     \    
1     2    
      |    
   x (exp1)

As we can see, appending of the new experimental branch with the name exp1 to the existing branch x resulted in creation of revision 2 that sits between new and old branch. Hm. Weird. Not that weird actually if you look closer to see a pattern. No? Thought so. But it is there, difficult to spot right away though indeed. A hint question - what does initial repository look like?

0
|
x

Now do you see it? Of course you do - structure similar to initial repository was appended to the x branch! There are three subtle differences though. First is that there is 2 instead of 0. Second is that new experimental branch gets displayed not only as its version, but version followed by its name in parentheses. Third is that new branch has the same version as the old branch. Aren’t they supposed to be different? Let’s address those peculiarities slowly one by one.

Sequence, order and history

Remember when we talked about repository instantiation and how x ‘takes’ empty solution 0 to modify it in any way we want? Same happens with instantiation of experimental branches. The difference is that starting point is not the empty solution 0, but some non-empty solution. More specifically, state of branch x becomes a starting point for another branch. Which state? The current/latest one. And how to ‘record’ the state of the branch? By taking a snapshot or, more specifically, instantiating a revision. This is what revision 2 means - it is the snapshot of latest content of the branch x that becomes starting point for content of the branch x (exp1):

%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: 
"some different content"
%Repository:repositoryExample> :showContent
    Enter version: 2
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: exp1 
"some different content"

For the moment contents of x, 2 and x (exp1) are all the same. Can we modify it? Yes, but only contents of branches because, as you might remember, revisions are non-editable:

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x
    Enter branch name to edit content: 
    Enter new contents of the branch: yet another content
%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: 2
You should enter branch version. Aborting operation
%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x
    Enter branch name to edit content: exp1
    Enter new contents of the branch: experimental content

This means now that we have another branch, we can modify two different solutions independently as we please. This is what we wanted when discussed speeding up the process of decision making with the friend, isn’t it? Well, not quite. We didn’t want to work on two solutions in parallel just yet. We wanted to share a solution and be able to continue working on another one being sure that friend will see what we intended her to see. Imagine the opposite - us sharing a solution that keeps changing. In that case, what our friend is supposed to give feedback to?! In other words, all we wanted is to be able to share results of our work that do not change. A snapshot. That’s what revisions are, aren’t they? Let’s keep exploring them:

%Repository:repositoryExample> :newRevision 
    Enter commit message: some another solution
    Enter version, which will be used to append new revision to: x
"repositoryExample" => 
      0       
      |       
      x       
      |       
 ------------ 
/     |      \
1     2      3
      |       
   x (exp1) 

Do you remember the situation when current/latest state of branch x became a starting point for another branch? This happened with the help of a revision. When :newRevision command is run, current content of branch (editable entity) is taken/copied to form content of revision (non-editable entity). If there are several revisions instantiated on the same branch, they form an order that allows to talk about the sequence in which snapshots of the branch were taken. Thus, revisions enable history. That’s why numbers represent revisions - they establish order and direction. When revision 2 appeared, it meant that it was instantiated after the previous one, which is 1. And 3 was instantiated after the previous one, which is 2. Sequence, order and history.

Only meaningful history

Let’s look at history of revisions:

%Repository:repositoryExample> :showContent 
    Enter version: 1
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: 2
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: 3
"yet another content"

What would be the content of revision 4 if we run :newRevision command now on x branch? Let’s figure it out by running a command:

%Repository:repositoryExample> :newRevision
    Enter commit message: yet another solution
    Enter version, which will be used to append new revision to: x
"repositoryExample" => 
      0       
      |       
      x       
      |       
 ------------ 
/     |      \
1     2      3
      |       
   x (exp1)   

It would not create revision 4 for us! Why is that? Because revision 4 in a sense is already there. Where exactly? Let’s look for it:

%Repository:repositoryExample> :showContent 
    Enter version: 0
""
%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: 
"yet another content"
%Repository:repositoryExample> :showContent
    Enter version: 1
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: 2
"some different content"
%Repository:repositoryExample> :showContent
    Enter version: 3
"yet another content"
%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: exp1
"experimental content"

Do you see what is going on here? No? Ok, let’s narrow it down. Do you remember that revision takes a snapshot of latest content of source branch? There is already a revision that corresponds to the latest content of branch x and it is revision 3. Is that the reason why :newRevision command would not create another revision for us? Let’s find out:

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x
    Enter branch name to edit content: 
    Enter new contents of the branch: one another content
%Repository:repositoryExample> :newRevision
    Enter commit message: one another solution
    Enter version, which will be used to append new revision to: x
"repositoryExample" => 
        0        
        |        
        x        
        |        
 --------------- 
/     |      |  \
1     2      3  4
      |          
   x (exp1)      

Whoa! It is there now, brilliant! What is the lesson here? Simple - it makes sense to assign new numbers to the things that are somehow different from previous ones. As long as it is not possible to modify revisions directly, the way to come up with another one is through modification of parent branch.

What about revisions 1 and 2? They have absolutely identical content yet somehow revision 2 was created without any problems. But do you remember how revision 2 was created? Not with :newRevision command, but with :newExperimentalBranch command. The reason why content of revision 2 was allowed to keep the same as 1 is because it was not only about recording the state of the parent branch, but also about event of branch instantiation. So, 2 is different from 1 in a sense that it helped bringing another entity to life that was not there before. Thus, revisions help to track meaningful repository events and the order, in which they happened. Only meaningful history.

Scope

Let’s try to instantiate revision based on experimental branch:

%Repository:repositoryExample> :newRevision exp1 
    Enter commit message: experimental solution
    Enter version, which will be used to append new revision to: x
"repositoryExample" => 
        0        
        |        
        x        
        |        
 --------------- 
/     |      |  \
1     2      3  4
      |          
   x (exp1)      
      |          
      5          

Hm. Unexpected outcome. Is it? But what would you expect then instead of 5? a? Would it make sense if we say that revisions are there for recording what happens not only to specific branches, but to the whole repository? Let’s keep exploring. For example, how would the ‘unexpected turn of events’ with 5 appearing ‘out of place’ change the history of x branch if we try to add new revision to it?

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: x
    Enter branch name to edit content: 
    Enter new contents of the branch: one more content
%Repository:repositoryExample> :newRevision 
    Enter commit message: one more solution
    Enter version, which will be used to append new revision to: x
"repositoryExample" => 
         0          
         |          
         x          
         |          
 ------------------ 
/     |      |  |  \
1     2      3  4  6
      |             
   x (exp1)         
      |             
      5             

History is broken! Well, not quite. It just means that history is happenning not only at one place, but at different places. Thus, repeating what we already said before: scope of revisions is the whole repository. Meaning that whatever gets recorded in a repository, gets a revision.

What about content of branches then? Isn’t it being recorded as well? Not quite. At least not in a sense of keeping track of every single change that happens to a branch - repository has the latest content of the branches themselves. History of the branch can be figured out based on corresponding revisions appended to the branch. Given that decisions about creating revisions have been made in the past. Decisions about new revisions are decisions about storing progress. Why to store progress? To share it. Why share it? To get feedback. Why to get feedback? To get help on solution. Feedback is the simplest form of getting help or, in other words, collaborate on the solution. That’s right - you and your friend are collaborators working together on the solution. More people are involved in problem solving when there is more than one version of the solution. That brings us to the whole point of versioning - being able to involve other people in problem solving.

This understanding brings us further. What if friend wants to not only give feedback, but also to contribute? That would be another level of collaboration activity - problem solving participation.

Problem solving participation

Being able to independently contribute is, in fact, why we needed the branch in the first place. It’s good to be able to try to solve the problem in two different ways yourself, but it is even better if another person can come up with their own solution independently from us and then share it with us or, even better, make their solution part of ours, for example, by merging. In fact, number of people that can work on the same branch is not limited. In that sense branches are the entities that enable collaboration by participation.

Version is a unique descriptor of the solution in a way that it allows to tell about the type of solution and its place in repository history. Having in mind what we saw so far, we conclude that x represents branches while versions in a form of a number represent revisions. While talking about order of branches does not make a lot of sense so far because of the only branch version we had chance to see (x), numbers of revisions tell us a lot about the order, in which they have been created. Also version of branches x tell a lot about the content, which branch can have. In this specific case of x it can be any content without any restrictions. This is the essence of experimental branches - they can contain anything, thus enabling the most general way of collaboration. In that regard all branches we have seen so far are similar. But they are different at the same time at least because they have different content. And this is how branch name is important. It is used to distinct between branches of the same kind. Hence the name in parentheses after the version: x (exp1). Experimental branch with no name is a special one. It has even got its own name - mainline. This is because it is the branch that gets updated with first possible solution and ultimately ends up with containing the latest solution (ideally, when all other corresponding child branches have been merged back).

Apart from experimental branches that enable collaboration in general, there are also other kinds of collaboration, such as, errors feedback (special case of feedback) and errors fixing (special case of participation). That’s what release branches are for.

Release branches

Let’s run :newReleaseBranch command to see it in action:

%Repository:repositoryExample> :newReleaseBranch 
    Enter version, which will be used to append new release branch to: x
"repositoryExample" => 
                x.0                
                 |                 
                x.x                
                 |                 
  -------------------------------  
 /       |        |    |    |    \ 
x.1     x.2      x.3  x.4  x.6  x.7
         |                       | 
     x.x (exp1)                 0.x
         |                         
        x.5                        

Wow! This is just… perplexing, to say the least. First, what happened to all the versions? They just ‘doubled’! Is this just a temporary side-effect? May we have old versions back? Second, where is the release branch in all this mess? Did we convert existing branches into a release type or did we add a new one? Third, is this the same history or was it somehow ‘rewritten’? In either case, why? Let’s address those peculiarities slowly one by one.

Bad news first. This is not a temporary side-effect. There is no going back. Once at least one release branch gets instantiated, version tree starts looking like this. But old versions are, in some sense, still there:

%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: 
"one more content"
%Repository:repositoryExample> :showContent 
    Enter version: x
    Enter branch name to show content: exp1 
"experimental content"

The twist is that all existing versions now have a weird prefix x.:

%Repository:repositoryExample> :showContent
    Enter version: x.0
""
%Repository:repositoryExample> :showContent
    Enter version: x.1
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: x.2
"some different content"
%Repository:repositoryExample> :showContent 
    Enter version: x.3
"yet another content"
%Repository:repositoryExample> :showContent 
    Enter version: x.4
"one another content"
%Repository:repositoryExample> :showContent 
    Enter version: x.5
"experimental content"
%Repository:repositoryExample> :showContent 
    Enter version: x.6
"one more content"

Now that we’ve made sure everything that was there in the repository didn’t go away (it just has gotten a version ‘extension’), we can spot new entities:

%Repository:repositoryExample> :showContent
    Enter version: x.7
"one more content"
%Repository:repositoryExample> :showContent
    Enter version: 0.x 
"one more content"

Seems like 0.x is our new release branch. Let’s make sure. Can we change it?

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: 0.x 
    Enter new contents of the branch: cntent 4 rellease
%Repository:repositoryExample> :showContent 
    Enter version: 0.x 
"cntent 4 rellease"

We can! Good news, after all, is that 0.x is a branch indeed. Turns out we added a branch, not modified existing ones. Does that mean x.x represents experimental branch now? You bet it does!

%Repository:repositoryExample> :showContent 
    Enter version: x.x
    Enter branch name to show content: 
"one more content"
%Repository:repositoryExample> :showContent 
    Enter version: x.x
    Enter branch name to show content: exp1 
"experimental content"

Turns out experimental branches can be referenced both by old x and by new x.x. Is x actually the same as x.x? Yes it is. Oooh, hidden dimension? That’s exactly what is going on here. :newReleaseBranch revealed a hidden dimension that was ‘sitting’ there, we just didn’t see it. Just like we didn’t see revisions before running :toggleRevisions. Let’s run it once again, just for laughs:

%Repository:repositoryExample> :toggleRevisions
"repositoryExample" => 
      x.x      
       |       
     --------  
    /        \ 
x.x (exp1)  0.x

Laughs or no laughs, it looks like it gives us simplified overview that we might use to our advantage. Ok, I want to hear your theory behind brand new release branch version. You say x. is a hidden dimension and 0. is a ‘materialized representation’ of that dimension? WOW! I am amazed! I could not describe it better myself! That explains perfectly why we didn’t need that dimension before running :newReleaseBranch command - we didn’t need release branches. Now that release branch is instantiated, hidden dimension comes to light together with the release branch 0.x itself. The history was not ‘rewritten’, it just got refined a bit.

Let’s go back to the representation where revisions are there:

%Repository:repositoryExample> :toggleRevisions
"repositoryExample" => 
                x.0                
                 |                 
                x.x                
                 |                 
  -------------------------------  
 /       |        |    |    |    \ 
x.1     x.2      x.3  x.4  x.6  x.7
         |                       | 
     x.x (exp1)                 0.x
         |                         
        x.5       

Now that we are talking about ‘materialized representation’ of the dimension, we can’t help, but spot another pattern. Looks like revisions are ‘materialized representations’ of experimental branches. Not seeing it? Well, if you look at the right dimension .x instead of left one x., it looks like all the revisions are results of ‘materialized representation’ of the right .x dimension: x.0, x.1, x.2, … But the problem is that x.0 comes ‘before’ x.x in the version tree hierarchy. Meaning x.0 was there earlier than x.x. Fair point. It is worth brining up the memory of the initial repository version tree though:

0
|
x

In that configuration there was no ‘before’ or ‘after’. Two sides (non-editable and editable) of essentially the same empty solution coexisted before the bare notion of direction entered the picture. Thus, it is rather a mystery what comes first - non-extendable void or the void that can be filled. This is a choice to be made. Otherwise the structure and its laws are lean and sound - revisions are instantiated via experimental branches.

If revisions get instantiated by ‘materializing representation’ of the right dimension .x and release branches get instantiated by ‘materializing representation’ of the left dimension x., what happens when both dimensions get ‘materialized’, for example, into solution with version 0.0? That is when release snapshots appear.

Release snapshots

In the spirit of previous adventures, let’s keep exploring by running new things. Command :newReleaseSnapshot in this particular case:

%Repository:repositoryExample> :newReleaseSnapshot 
    Enter version, which will be used to append new release snapshot to: 0.x 
"repositoryExample" => 
                  x.0                   
                   |                    
                  x.x                   
                   |                    
  ---------------------------------     
 /       |        |    |    |      \    
x.1     x.2      x.3  x.4  x.6    x.7   
         |                         |    
     x.x (exp1)                   0.x   
         |                         |    
        x.5                       x.8   
                                   |    
                                Test/0.0

Well well. There is something unexpected, as always. But we have to admit there is something expected too - 0.0 is featured as advertised. What is so (un)expected about it? Every new step is a combination of expected and unexpected elements. In general, that’s how the problem is getting refined together with the progress towards the working solution. Test/ is a new aspect of both the problem and the solution. This new aspect is called maturity. The aspect that we already know about is called version number. 0.0, x.0, x.1, x.x, 0.x are all version numbers. We called them earlier as versions though, which is also correct. This is because aspect of maturity has been hidden before the introduction of release snapshots. Maturity together with version number form a version. If there is no maturity, just a version number, it is still a version.

Now that there is a deeper analysis of release snapshots going on… We have come across the concept of snapshots before, haven’t we? Remember talking about revisions and how they capture the latest state of a particular branch? We referred to that as ‘taking a snapshot’. Revisions are indeed snapshots. So are particular kinds of snapshots, such as release snapshots. We can’t modify them. Apart from that, there is another special thing about all snapshots - their version numbers end with an actual number. Unlike branches, version numbers of which end with an x. What’s so special about release snapshots specifically as opposed to snapshots in general? They get created only based on release branches. Do you remember what is the purpose of release branches? To collaborate on errors elimination. While release branches are for contributing error fixes, release snapshots are for getting errors feedback. Release snapshots hit broader audience than just contributors to solution. They are targeted at all people that help with finding errors in the solution. In order to be able to tell errors apart from expected behavior, problem has to be defined to the extent that allows to test solution against that problem definition. While having the problem definition is a sign of problem maturity, having a solution to test against that definition is a sign of solution maturity. That’s why release snapshots have maturity as part of their versions. In order to understand more about different release snapshots and their target audiences, we need to learn more about maturity levels.

Maturity levels

We already know that there is at least one maturity level - Test. That’s all we will ever want, right? After all, getting errors feedback is in fact testing. So it makes sense to have Test snapshots as a sign that they are being tested. Can we keep it simple? Let’s fix some errors to get a new release snapshot and see:

%Repository:repositoryExample> :editBranch 
    Enter version of the branch to edit: 0.x 
    Enter new contents of the branch: c0ntent 4 release
%Repository:repositoryExample> :newReleaseSnapshot 
    Enter version, which will be used to append new release snapshot to: 0.x 
"repositoryExample" => 
                       x.0                        
                        |                         
                       x.x                        
                        |                         
  --------------------------------------          
 /       |        |    |    |           \         
x.1     x.2      x.3  x.4  x.6         x.7        
         |                              |         
     x.x (exp1)                        0.x        
         |                              |         
        x.5                         ---------     
                                   /         \    
                                  x.8       x.9   
                                   |         |    
                                Test/0.0  Test/0.1

It will be simple as long as we keep it simple. No other maturity levels appeared as a result of :newReleaseSnapshot command. Are we sure that we need to learn about other maturity levels? You can stop at any point if you think you’ve learned enough to meet your needs. That, in fact, was always the case. Many stop after learning that there is a single branch they can use to work on their solution.

I want to learn more