Care And Feeding Of Revisions
This document is intended to describe the various ways Revisions can be moved around. To a large extent, this ends up being about push, pull, and merge.
Throughout, we talk about copying Revisions from one Branch to another. In reality, the Revisions are always stored in a Repository, but we don't talk to them directly; instead the Branch in question knows where its Repository is and takes care that things are stored correctly. So from a user interface point of view, all the interaction here is with Branches.
The setups we create here for describing things will be very simple and ignore how you'd set things up in a real filesystem. Also, we describe everything on one machine for simplicity. That doesn't change the essentials of the analysis though; you can imagine the various Branches we're working with being on different systems, and things will work the same.
We start out with a single Branch at /a, containing some history; let's say, 3 revisions.
3 | 2 | 1
We want a second Branch so we have something to talk about, so we make it at /b.
% bzr branch /a /b
Both branches at this time have identical history; revisions 1-3.
So now imagine we add a revision to branch /a, using bzr commit. Now /b still has our original 3 revisions as above, but /a has added a fourth onto the end:
4 | 3 | 2 | 1
Now our branches are no longer in sync. Colloquially, we could say that /b is "behind" /a. We can use the bzr missing command to see what the differences are.
We want to bring these branches back into sync, however. To do that, we need to conceptually do two steps:
Copy revision 4 into branch /b.
Update branch /b so that it considers 4 (rather than the previous 3) to be the current branch head.
The two commands used for this are push and pull. The difference between the two is mostly whether you're in the destination pointing at the source (pull), or in the source pointing at the destination (push):
# One way % cd /a ; bzr push /b # or another % cd /b ; bzr pull /a
Tip: both push and pull remember the location used, so if you're always push/pull'ing the same place, you won't need to specify the location most of the time.
The result of these commands, then, is to update one branch so that it's the same as another. It's used when one branch has moved forward and you want the other one to catch up.
When you run pull and have a local Working Tree, bzr will update that to the new head, trying to merge in your local changes if possible. push will do the same, if the destination is local. However, push is often used to push across the network, and across the network the Working Tree will not be updated.
So now our branches are in sync with each other again. They both have 4 revisions:
4 | 3 | 2 | 1
We often talk of our Revisions by their number. But the real identifier is a long string called the Revision ID. Numbers are assigned to the revisions per-branch, so the number can actually be different for the same revision in different branches. The RevID is always the same, though. Details on this topic is covered in RevNumbering.
Let's rename those revisions we have to letters instead from here on, since we're about to start talking about branches diverging:
D | C | B | A
Now let's consider another case, where both branches add new revisions. We start where we left off, both branches being in sync having the 4 revisions A, B, C, and D.
We commit a new rev onto branch /a which we call E, and one onto /b which we'll call F:
/a /b --- --- E F | | D D | | C C | | B B | | A A
Push or Pull?
Now, however, we can't use either push or pull. Since each branch has a revision the other doesn't (and we presumably want to keep both), neither command will do what we want. In fact, both will refuse to work by default, because they can see that the branches have diverged. You would have to use the --overwrite argument to make them run, and the result of that would be to throw away the extra rev on the destination side:
% cd /b ; bzr pull --overwrite /a # Now both have A..E, and F is lost
More often, though, both E and F are important, and we want to keep both. Somehow, we want to merge them together. Luckily, we have a command for that, called merge.
We'll pretend that we're /b, and we want to merge in /a's changed. Note that merge does not create a new revision. It only sets up the merge, and leaves the working tree for you to examine. You'll have to commit after checking things out to create that new rev.
% cd /b % bzr merge /a <output from bzr describing what it's doing> # Check out the files to make sure everything looks right, resolve any # conflicts, etc. % bzr commit
Now those changes are merged into /b, via the creation of a new merge revision. Unlike the other revisions we've seen, this rev has two parents, rather than just one. We end up with a revision graph that looks like
G |\ F E |/ D | C | B | A
The new merge revision G has two parents; the revision F which was our previous head, and the revision E which was the revision we merged in from the /a branch. Both of them are children of the revision D, and neither is a child or parent of the other.
Now, we're back in a more familiar situation. Branch /b now has everything branch /a has, as well as some extra stuff. So now we could use push or pull to bring them back into sync.
% cd /a ; bzr pull /b # Or % cd /b ; bzr push /a
And now both branches will look like /b did.
The Other Way Around
What if we'd gone the other way around, and instead of merging /a into /b, we'd merged /b into /a?
% cd /a % bzr merge /b % bzr commit
Now /a would have a revision graph that looks like
H |\ E F |/ D | C | B | A
Note that this is different from what we came up with above. We have a revision H instead of G, and E and F are swapped around. Even though the files you end up with in H are probably the same, to bzr, these histories are different. And they're not interchangeable.
If you'd done both of those merges before doing a push or pull, you'd be back where you started. Each branch would have something the other didn't; one would have H, the other G. Choosing between these histories involves choosing your mainline, which is a topic for another document.
Odds & Ends
That pretty much covers what we want to talk about here.
An important thing to realize here is that the only thing that really matters is the Revisions that are present; what branch "came from" (e.g., bzr branch) what others is unimportant. You can do a long chain, like
% bzr branch /a /b % bzr branch /b /c % bzr branch /c /d
and interact between /d and /a, or any other pair, with no problems. The question always is "what revisions are where, and what am I trying to move revisions from/to?".