Bazaar

Bazaar

 




Wiki Tools

  • Find Page
  • Recent Changes
  • Page History
  • Attachments

About The Mainline And Revision Numbering

This is a cleaned up IRC log of a discussion about what the 'mainline' of a branch is, and how it affects revision numbering. Its utility and the implications of changing it are mentioned in passing.

This should be cleaned up into an actual document some time, but for now, it's here for reference.

Log

USER
What causes point versions? Like 105.1.1
fullermd
That designates a revision that was brought into the branch via a merge.
USER
oh
USER
so will it ever go away and go back to an integer?
fullermd
Possibly, if it ever ends up in the lefthand ancestry line of a future head rev.
USER
?
USER
you lost me at lefthand
fullermd

It's the opposite of righthand

USER
you lost me at "lefthand ancestry" :P
fullermd
How deep do you want to grok it? It's hard to answer that question without understanding exactly how the numbering comes about.
USER
.... I'm really really tired
USER

I'm up for knowledge though

fullermd
Theoretically, every time the head rev of the branch moves, all the numbers could change.
fullermd
In practice, that happens some time between "almost always" and "never", depending on exactly how your workflow... flows. Or whatever it is workflows do.
fullermd
OK. So, in a purely linear system (like CVS or SVN), numbering is easy, since every rev "comes from" the one before.
fullermd
I make a commit, that's 1. You make one, that's 2. I make one, that's 3. Etc.
fullermd
In a nonlinear system (like most DVCS's, though there's nothing _stopping_ a central system from being so), that's no longer the case.
fullermd
If we're both working based on a branch at say rev 10, and we both make a commit, now we both have 11.
fullermd
But our 11's have nothing to do with each other.
fullermd
So far no problem though, since if you're looking at my branch, we don't see, care, or even know about your 11 (and vice versa)
fullermd
But when we try and merge the branches together, what can we do? We can't have TWO rev 11's.
fullermd
And it doesn't make sense (in bzr's opinion; hg does sorta this) to call one 12, since that implies that it's based on 11 (which it's not; neither of our revs is based on the others', they're both based on 10)
fullermd
So, we could have NO 12 at all, and call both something-implying-descent-from-10, and then have a 13 later.
fullermd
And various other even more confusing options. But they're confusing.
fullermd
So, if I merge your branch, your 11 is named something "off to the side". And that fits how I'll think of it; I didn't make it, I brought it in from somewhere else.
fullermd
Of course, the numbering doesn't actually come from that sort of question inside bzr; that's just how it appears.
fullermd
What actually happens is, revs don't inherently have numbers; if you run 'log --show-ids', you'll see the revision id's, which are long opaque strings.
fullermd
Those are associated with the rev forever; wherever you are, that string means that rev.
fullermd
Numbers are local to a given branch; a given rev can have many different numbers, depending on the structure of the branch its in.
fullermd
The way those are assigned depends on the shape of the ancestry of that branch, which always works backward from the head rev.
fullermd
A branch works by saying "THAT rev right there is my head", and that rev's parents, and its parents' parents, and their parents, and so on back to null, are part of the branch.
fullermd
If every rev had a single parent (like in the linear case above), that would be easy; you figure how many there are, and number backward from that.
fullermd
But when you merge, you create a rev with 2 parents; your last revision, and the [head] revision you merged in.
fullermd
(Technically, you can have any number of parents, because you could do multiple merges at once, but you practically never do, it's a bit more confusing, and it doesn't change the essentials of this analysis anyway)
USER
so far this is really really easy to follow
fullermd
In theory, merges are symmetric ("merge these two things together"), but socially and mentally we generally consider them asymmetric ("merge that into this")
fullermd
So you don't, in mental modelling, "merge two branches together", you "merge that branch into this branch"; often in the sense of "merge Joe's branch into my branch".
fullermd
Now, merging has to create a new rev, since you're making a new state. And that rev has two parents; your branch's previous head, and the rev you merged.
fullermd
(in merging someone else's branch, you probably actually merge many revs, but you do that by merging its head, and thus implicitly pulling the ancestors of that head you don't already have. So you only have that previous head as a parent; the rest show up in the ancestry just like your previous history does, transitively through that head)
fullermd
So now, if we try and look back from the head to number, we have a branching; there are two paths backward from that. So which way do we follow to number?
fullermd
We could pick one way at random, but that would be stupid. We can try and create some sort of linear projection, but then we end up with the problems described above; you end up with a '12' that isn't based on '11', etc.
fullermd
What bzr does is preserve and exploit the asymmetry described above.
fullermd
We preserve an ordering of the parents of a rev; the 'first' or 'left' (depending on how you look at things) parent of the merge rev you created is YOUR previous state, and the 2nd (and 3rd, 4th, if necessary) or right-side parents are those you merged.
fullermd
So at every branching in the history (we're looking backward remember, so in this sense that means "every rev with more than one parent"), we take the left-hand or first-parent path, when figuring stuff for numbering.
fullermd
And so the number of steps on THAT path, ignoring the right-side path, determines how many numbers we have. And the revs on that path are the ones that get the numbers.
fullermd
The upshot of that scheme is that the revs that get numbers, and are considered a branch's "main line" in bzr terms, are those that were "made on this branch"; e.g., those you made by 'commit', rather than those you acquired from elsewhere via 'merge'.
fullermd
(that definition is necessarily fuzzily and somewhat incomplete (and in some ways flat wrong), but it gives sort of a flavor of what's happening)
fullermd
Internally of course, all revs are equal, but UI-wise, it can be valuable to consider that sort of dichotomy for various reasons. So bzr chooses to present based on that.
fullermd
Now, we've used the monotonically-increasing integers for those revs on the left-hand path. What can we do for numbering those remaining revs?
fullermd
Well, we could just not number them, and use the revid string if we need to refer to them.
fullermd
We used to just do that (pre-0.13 I think?). Little ugly though; revids are long.
fullermd
Or we could give them numbers that aren't integers. That's where dotted revnos come from.
fullermd
At the moment, those numbers are generated by basing them from the 'mainline', integer revision they descend from.
fullermd
So if we have a branch with 10 revs, we both branch from it and create an 11, then I merge you.
fullermd
My history looks like:
fullermd
1..10: The original 10 revs.
fullermd
11: My 11.
fullermd
12: Merge of your branch.
fullermd
10.1.1: Your 11.
fullermd
If, at the same time I do that (before my 12 exists), you merge my branch, you end up with a similar numbering.
fullermd
Except your 11 is still 11, and my 11 is 10.1.1 in your branch.
fullermd
(it's not necessarily the _only_ way to assign those dotted numbers, but it's how we currently do it. Remember these aren't _stored_ or part of the rev, they're just derived on the fly, so we could change it easily)
fullermd
So, now we both have branches with 13 revs in the history (though on both cases, only 12 are on the mainline; the 13th is off to the side and gets a dotted revno)
fullermd
12 of those 13 are common; it's only that last, number 12, that's different.
fullermd
And our rev 12's, while different revs, are probably much the same; we probably didn't have merge conflicts. So the files all look the same.
fullermd
Looking at the files, I probably couldn't tell whether I was in my branch or yours.
fullermd
So socially (not technically necessarily), we could choose to use my 12 or your 12 going forward with equal ease. But depending on which it was, a different rev would get the dotted number.
fullermd
Now, let's suppose you make a new 13 rev, and then a 14 merging my branch (which makes no file changes, but brings the history graphs together)
fullermd
My 12 (my version of the merge) ends up with a dotted number (10.1.2 I think, in the current scheme).
fullermd
(after all, it's "off to the side" in your perspective there)
fullermd
Now, I want to sync up with you. I could merge again, and I'd end up with a '13', and your 12, 13, and 14 'off to the side' with dotted numbers.
fullermd
But, since your history graph is a strict superset of mine, I could 'pull' instead, which basically turns my graph into a copy of yours.
fullermd
So now my head is your 14.
fullermd
The result, then, is since we have the same heads, we have all the same numbers.
fullermd
And my 11, which previously had an integer revno, now has a dotted.
fullermd
Your 11, which previous had a dotted (10.1.1), is now on my mainline, so it's no longer dotted.
fullermd
My head rev changed in a way that was strictly forward (it includes my previous head in its ancestry, and thus everything prior to that too), but the new head implies a different 'mainline' through its left path, so [some of] the revs get renumbered.
fullermd
If I move my head forward solely by 'commit', that will never happen; that's the commonish case in the bzr world, socially.
fullermd
Put you to sleep yet? :p
USER
I've been enjoying it - but I am also tired :P
USER
I've understood every bit of this though
fullermd
So, you have the tools now to answer your question; if your branch's head rev moves in such a way that the left-hand path through the ancestry changes, revnos (integer and dotted) can change.
fullermd
In rough colloquial terms, that happens when your head moved to a rev somebody 'commit'd elsewhere, rather than one you 'commit' locally.
fullermd
Via you 'pull'ing some other branch, or another branch 'push'ing onto you.
fullermd
Merge will never do it (since merge doesn't create a new head, just changes in your working tree waiting for you to commit them)
USER

makes sense

fullermd
Commit will never do it, since the first/left parent of the new rev you commit is your previous head, so all that ancestry is unmoved.
fullermd
And while there are probably other ways to move your head than the above, that's pretty much it through the UI leaving extraordinary circumstances out.
fullermd
Of course, it's possible that that 'outside' head could _not_ change the numbering, if it doesn't change the left path.
fullermd
e.g., I have 10 revs, you branch from that and create an 11, and (without making any local changes), I 'pull' you.
fullermd
I have a new rev tacked on the end now, but everything that was previously on the mainline still is, so existing numbers weren't changed.
fullermd
There's a branch.conf setting you can make that will refuse changes that would change the left path.
fullermd
append_revisions_only, I think.
fullermd
You can set that on a branch, and then you can commit and pull into and push-onto all you want, but it will error out if the action would change the left path.
fullermd
That can be useful on branches like 'trunk', where you want the numbering (and the view of history) to remain stable.
fullermd
But again of course, this is all social; technically all that matters is questions like "is rev X in my ancestry".
fullermd
Other systems, like git or mtn, don't assign any meaning to left vs right or first vs later parents.
fullermd
And they don't have revnos, either. The two kinda tie together; revnos would be less useful if they changed all the time.
fullermd
[other theoretical discussions elided in favor of sleep]

MatthewFuller