Branching Zen

To master distributed version control, you have to master branching. In Bazaar, branching is easy, inexpensive, and automatic. In this part of the tutorial, I'll explain what branching is, how it works, and why you're going to love it.


In Bazaar from the Ground Up, I mentioned that a version control system should track all your work and your project history, but I also said it should help you collaborate. I've already shown you how to track every version of your project and how to get old versions when necessary, but I didn't show you how Bazaar makes collaboration easy. Branching is how we work with other people with Bazaar.

Branch Oriented
All work in Bazaar happens in the context of a branch.

You could say that Bazaar is branch oriented. When we call bzr init to start a tracking a project, we create a new branch of the project. All of your work happens in the context of a branch.

A branch is simply a line of development— the history of adding subsequent revisions. We always start with a single branch, but most projects will fork off many branches. Some of these branches will be experiments that simply stop and are discarded, while others will merge together with other branches to flesh out a project.

Let's start out with a very simple project— we're going to make a cake! No really, stay with me here. We're going to write a recipe for making chocolate cake. To get started, let's make our initial branch of the project:

fmccann
~ fmccann$ bzr init recipe
Created a standalone tree
~ fmccann$ cd recipe/
~ fmccann$ edit cake.txt

I'm using a generic "edit" command to start the recipe. You can use any editor you like to edit your files. (Though, for all the vi people, you should definitely use emacs. Oh, and by the way, emacs people, get with it and use vi.)

cake.txt
My Awesome Chocolate Cake Recipe

Not very impressive as far as recipes go, but it's a start. Let's commit this as the first revision in our branch:

recipe
recipe fmccann$ bzr add
adding cake.txt
recipe fmccann$ bzr commit -m"Started cake project"
Committing to: /home/fmccann/recipe/
added cake.txt
Committed revision 1.
Forking Branches
Whenever we want to work independently of an existing branch, we create a copy as a new branch.

Great! We've started the project. There's just one thing— I don't really know how to make a cake. I'm going to have to experiment a bit. Rather than muck around in the current branch, I'm going to make another branch of the project to play around. We make a branch whenever we want to work independently of an existing line of development.

Forks a new branch from an existing branch. The new branch has the complete history of the original branch.
recipe
recipe fmccann$ cd ..
~ fmccann$ bzr branch recipe experiment
Branched 1 revision.
~ fmccann$ ls -l
experiment
recipe

We've just create a new branch of the project. Bazaar made a new directory that contains a complete copy of our project, including the complete history. Technically, we now have two repositories with two different branches. Both branches contain the same revision— revision 1, and each branch has a working tree that is up-to-date with revision 1.

Let's take a look at the files and the history in the new branch:

fmccann
~ fmccann$ cd experiment/
experiment fmccann$ ls -la
.bzr
cake.txt
experiment fmccann$ cat cake.txt
My Awesome Chocolate Cake Recipe
experiment fmccann$ bzr log
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project

Everything checks out! Let's take our first crack at the recipe:

cake.txt
My Awesome Chocolate Cake Recipe
cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2 cups all-purpose flour
  2 cups salt
  1 teaspoon sugar
  2 cup vegetable oil
  6 eggs
  9 teaspoons vanilla extract
  6 cup boiling water
                

That looks good right? I mean, you can't make a cake without boiling 6 cups of water, as I always say. And you don't want to skimp on the salt either. Let's commit this to our experimental branch:

experiment
experiment fmccann$ bzr commit -m"My first cake recipe"
Committing to: /home/fmccann/experiment/
modified cake.txt
Committed revision 2.

It turns out that the first recipe wasn't as good as we'd hoped. I bet the problem is we need more salt. Most problems in computer science can be solved by adding a layer of indirection, and all problems in cooking are solved with more salt:

cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2 cups all-purpose flour
  2 cups salt
  1 teaspoon sugar
  2 cup vegetable oil
  6 eggs
  9 teaspoons vanilla extract
  6 cup boiling water
              
cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2 cups all-purpose flour
  4.5 cups salt
  1 teaspoon sugar
  2 cup vegetable oil
  6 eggs
  9 teaspoons vanilla extract
  6 cup boiling water
              
experiment
experiment fmccann$ bzr commit -m"Let's try more salt!"
Committing to: /home/fmccann/experiment/
modified cake.txt
Committed revision 3.

I'm going to level with you. This whole cake plan is not working out well at all. I think it's time to pull the plug on this failed experiment. How do we get rid of a branch that we don't want anymore?

Removing Branches
To remove a branch, delete the directory containing the branch.
experiment
experiment fmccann$ cd ..
~ fmccann$ rm -rf experiment/

And that's the end of the experiment branch. It's life was brutal and short, just like my career as a baker. Let's see how the experiment branch affected our recipe branch:

fmccann
~ fmccann$ cd recipe/
recipe fmccann$ bzr log
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project

The answer is not at all. We added two revisions in the experiment branch, but they only existed in that branch. When we abandoned it, all of the revisions went with it.

Let's start over with a new branch, and this time, let's consult a cookbook.

recipe
recipe fmccann$ cd ..
~ fmccann$ bzr branch recipe cookbook
Branched 1 revision.
~ fmccann$ cd cookbook/
cookbook fmccann$ edit cake.txt
cake.txt
My Awesome Chocolate Cake Recipe
cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  .75 cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water

One teaspoon of salt. Go figure.

cookbook
cookbook fmccann$ bzr commit -m"First version of cake recipe 'borrowed' from a cookbook"
Committing to: /home/fmccann/cookbook/
modified cake.txt
Committed revision 2.

This version worked out much better. You know what, let's make one more change:

cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  .75 cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  

Can't have too much cocoa. Let's commit this change.

cookbook
cookbook fmccann$ bzr commit -m"Needs more cocoa"
Committing to: /home/fmccann/cookbook/
modified cake.txt
Committed revision 3.
Merging Branches
To combine branches, we merge them.

Now that we have a good start for our recipe, let's get our work back into our original recipe branch. We branched our cookbook branch from the original recipe branch, and now we're going to merge our work back in.

Combines changes from another branch into the current branch.
cookbook
cookbook fmccann$ cd ../recipe/
recipe fmccann$ bzr merge ../cookbook/
 M  cake.txt
All changes applied successfully.

We copied all of our new revisions into the recipe branch, and the working tree of recipe has been updated to reflect this. If we take a look at the contents of the cake.txt file, we'll see all of our new work is present.

cookbook
recipe fmccann$ cat cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water

While all of our changes have been copied and all the files updated, the merge will not be complete until we commit the changes. We can see this if we call bzr status.

recipe
recipe fmccann$ bzr status
modified:
  cake.txt
pending merge tips: (use -v to see all merge revisions)
  Fred McCann 2015-12-19 Needs more cocoa
recipe fmccann$ bzr status -v
modified:
  cake.txt
pending merges:
  Fred McCann 2015-12-19 Needs more cocoa
    Fred McCann 2015-12-19 First version of cake recipe 'borrowed' from a cookbook

Canceling a Merge
To cancel a merge, call bzr revert instead of committing the merge.

As you can see from bzr status, we're merging in two revisions from the cookbook branch, but the merge is "pending". A merge isn't complete until you commit the merge, just like any other change to the branch. If we change our minds, we can always call bzr revert and the merge is canceled. We want these changes, so we're going to commit the merge:

Committing a Merge
A merge isn't complete until it is committed.
recipe
recipe fmccann$ bzr commit -m"Initial recipe based on a cookbook method"
Committing to: /home/fmccann/recipe/
modified cake.txt
Committed revision 2.

Did you catch that? We committed revision number 2. We had three revisions in our cookbook branch, but our recipe branch only has two revisions? Let's check the log:

recipe
recipe fmccann$ bzr log
------------------------------------------------------------
revno: 2 [merge]
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 19:50:47 -0500
message:
  Initial recipe based on a cookbook method
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project
------------------------------------------------------------
Use --include-merged or -n0 to see merged revisions.

Where did our new changes go? All we see is our first revision and the merge we just committed. Also, what's the deal with the note at the bottom of the log about showing merged revisions?

Merge Revision
A merge revision shows where two branches join back together. It is descended from both the previous revision of the current branch and the last revision of the merged branch.

Revision 2 is marked as a merge revision. Normal revisions have a single parent; one revision follows the previous revision. A merge revision has more than one parent— it follows both the last revision of the recipe branch and the cookbook branch. A merge revision is where two branches join back together.

The -n option to the bzr log command sets the level of nesting to display. By default, bzr log only shows the top most level, displaying all the revisions on the current branch. You can set any level of nesting; setting a level of zero will display all nested revisions.

Hierarchical Logs
Bazaar logs are not flat, because branch history is not flat. They're hierarchical, and you can control how many levels of nested revisions you show with the -n option to bzr log.
recipe
recipe fmccann$ bzr log -n0
------------------------------------------------------------
revno: 2 [merge]
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 19:50:47 -0500
message:
  Initial recipe based on a cookbook method
    ------------------------------------------------------------
    revno: 1.1.2
    committer: Fred McCann <fred@bzrinit.com>
    branch nick: cookbook
    timestamp: Thu 2015-12-19 19:28:08 -0500
    message:
      Needs more cocoa
    ------------------------------------------------------------
    revno: 1.1.1
    committer: Fred McCann <fred@bzrinit.com>
    branch nick: cookbook
    timestamp: Thu 2015-12-19 16:08:52 -0500
    message:
      First version of cake recipe 'borrowed' from a cookbook
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project

We found our missing revisions! Bazaar's logs are nested because our history is nested:

Revision 2 is the point at which the cookbook branch rejoined the recipe branch. You'll notice the revision numbers look odd too. Revisions 1 and 2 are simple integers, increasing with each revision. The revision numbers on the merged work are composed of three integers. What gives? Let's explain revision number 1.1.2:

History is Relative
Each branch has it's own relative view of history. Revision numbers reflect the branch's perspective.

In the recipe branch's view of history, the cookbook branch was forked from revision 1. It is the first branch based on revision 1, and revision 1.1.2 is the second revision in that branch.

Einstein would approve of Bazaar, because in Bazaar, everything is relative. Every branch has it's own relative view of history. Let's go back to the cookbook branch and see it's view of history:

recipe
recipe fmccann$ cd ../cookbook/
cookbook fmccann$ bzr log
------------------------------------------------------------
revno: 3
committer: Fred McCann <fred@bzrinit.com>
branch nick: cookbook
timestamp: Thu 2015-12-19 19:28:08 -0500
message:
  Needs more cocoa
------------------------------------------------------------
revno: 2
committer: Fred McCann <fred@bzrinit.com>
branch nick: cookbook
timestamp: Thu 2015-12-19 16:08:52 -0500
message:
  First version of cake recipe 'borrowed' from a cookbook
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project

The cookbook branch only has three revisions, and they are numbered {1, 2, 3}.

Whenever you check your log, you'll see the branch's history, and history will be displayed according to that branch's perspective. Relative revision numbers and hierarchical logs makes it easy to understand a branch's history.

You may be wondering how we can identify the same revision in multiple branches. For example, cookbook's revision 3 is recipe's revision 1.1.2. Relative numbers are great when looking at a single branch, but how do we make sense of things across all branches?

cookbook
cookbook fmccann$ bzr log -r3 --show-id
------------------------------------------------------------
revno: 3
revision-id: fred@bzrinit.com-20131220002808-0naame97e5x4johv
parent: fred@bzrinit.com-20131219210852-mzi3hp8zf4d5idbf
committer: Fred McCann <fred@bzrinit.com>
branch nick: cookbook
timestamp: Thu 2015-12-19 19:28:08 -0500
message:
  Needs more cocoa
Unique Revision IDs
Each revision has a global, unique revision identifier. This ID can be used to identify the same revision in multiple branches.

Every revision has a unique revision id. We're using the --show-id option of bzr log to show the unique revision id of revision number 3:

fred@bzrinit.com-20131220002808-0naame97e5x4johv

This identifier is the same in every branch that contains this revision. Let's examine revision number 1.1.2 in the recipe branch:

cookbook
cookbook fmccann$ cd ../recipe/
recipe fmccann$ bzr log -r1.1.2 --show-id
------------------------------------------------------------
revno: 1.1.2
revision-id: fred@bzrinit.com-20131220002808-0naame97e5x4johv
parent: fred@bzrinit.com-20131219210852-mzi3hp8zf4d5idbf
committer: Fred McCann <fred@bzrinit.com>
branch nick: cookbook
timestamp: Thu 2015-12-19 19:28:08 -0500
message:
  Needs more cocoa

The revision in the recipe branch has the same revision id:

fred@bzrinit.com-20131220002808-0naame97e5x4johv

Using revision numbers is much easier and much more common than using revision identifiers. For those cases when you need to specify a revision regardless of what branch it's in, you can always use the revision id. Any command that can take a revision number, such as bzr revert, bzr update, and bzr log, can use a revision identifier:

recipe
recipe fmccann$ bzr log -r fred@bzrinit.com-20131220002808-0naame97e5x4johv
------------------------------------------------------------
revno: 1.1.2
revision-id: fred@bzrinit.com-20131220002808-0naame97e5x4johv
parent: fred@bzrinit.com-20131219210852-mzi3hp8zf4d5idbf
committer: Fred McCann <fred@bzrinit.com>
branch nick: cookbook
timestamp: Thu 2015-12-19 19:28:08 -0500
message:
  Needs more cocoa

My friend Charlie heard about my brilliant cake plan, and he'd like to take a look at what I have so far. I could just start emailing him copies of the recipe as I work, but it's easier to use Bazaar to collaborate on the recipe.

Right now, the project only exists on my laptop. That's not great for sharing work as my laptop is behind a firewall and isn't directly accessible. To share my work, I'm going to have to make a copy of it somewhere Charlie can access it. Bazaar is very flexible when it comes to sharing. You can get started with a simple sftp or ftp server to share work. We've got access to a server that has a ssh and Bazaar installed, so I'm going to create a mirror of my recipe branch on the remote server.

Pushes revisions to another branch, updating it to be a mirror of the current branch.
recipe
recipe fmccann$ bzr push bzr+ssh://dev.example.com/srv/recipe
Created new branch.

The bzr push sends all my revisions another branch, making it a mirror of my branch. In this case, that other branch just happens to be on a remote server. Since we didn't already have an existing branch on the server, Bazaar created one for us (in the /srv/recipe directory).

Charlie also has access to the remove server, so now he can access our work. The way Charlie does that is he creates his own branch of the project:

Remote Branches
Bazaar commands that operate on branches such as bzr branch, bzr push, and bzr merge work on both local and remote branches. Bazaar makes it seamless to work with either remote or local branches and provides a number of different transport options for working with remote branches.
charvey
~ charvey$ bzr branch bzr+ssh://dev.example.com/srv/recipe charlie
Branched 2 revisions.
~ charvey$ cd charlie/
charlie charvey$ bzr log
------------------------------------------------------------
revno: 2 [merge]
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 19:50:47 -0500
message:
  Initial recipe based on a cookbook method
------------------------------------------------------------
revno: 1
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Thu 2015-12-19 15:06:17 -0500
message:
  Started cake project
------------------------------------------------------------
Use --include-merged or -n0 to see merged revisions.

Charlie now has a complete copy of our work and our history on his own laptop— all he needed was access to the remote server. He used the same bzr branch command to make his own branch.

Charlie takes a look at what we've got, and he has some ideas. The cake recipe looks good, but it could use some frosting. Being a helpful guy, Charlie opens the file and starts adding more ingredients:

cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2 cups all-purpose flour
  2 cups salt
  1 teaspoon sugar
  2 cup vegetable oil
  6 eggs
  9 teaspoons vanilla extract
  6 cup boiling water
cake.txt
My Awesome Chocolate Cake Recipe 

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  

  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder

Charlie, who's a better baker, tested his frosting recipe, then committed the change to his branch:

charlie
charlie charvey$ bzr commit -m"added ingredients for frosting."
Committing to: /home/charvey/charlie/
modified cake.txt
Committed revision 3.

After adding the frosting recipe, Charlie took a look at the cake ingredients and decided there were a few tweaks needed.

cake.txt
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water


  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder
cake.txt
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  1   teaspoon espresso powder


  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder
charlie
charlie charvey$ bzr commit -m"added espresso to the cake ingredients"
Committing to: /home/charvey/charlie/
modified cake.txt
Committed revision 4.

While Charlie was working, we kept working in our branch too. We decided that we also wanted to tweak the cake ingredients:

cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
cake.txt
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  2 teaspoons vanilla extract

You'll notice that we don't have any of Charlie's changes— we're working off our last version of the recipe. We like the added vanilla, so we're going to commit our changes:

recipe
recipe fmccann$ bzr commit -m"added vanilla to ingredients"
Committing to: /home/fmccann/recipe/
modified cake.txt
Committed revision 3.

Now things are starting to get out of hand. We have a completely different cake recipe than Charlie.

Charlie is happy with his version of things and can't wait to share it. He's going to send his changes back to our shared branch on the server. Before he does, he's going to make sure he isn't missing any revisions from the server's branch:

Pulls revisions from another branch, updating the current branch to be a mirror of the other.
charlie
charlie charvey$ bzr pull
Using saved parent location: bzr+ssh://dev.example.com/srv/recipe/
No revisions or tags to pull.

The bzr pull command works just like the bzr push command, except revisions flow in the other direction. Notice that Charlie didn't tell Bazaar where the other branch was located. Bazaar remembered which branch he forked from, and assumed he wanted to pull changes from there. There haven't been any new revisions pushed to the server since Charlie made his changes, so there are no new revisions to pull.

Before sending his work to the server, Charlie wants to review exactly what will be sent:

Lists revisions in the current branch that are not in the target branch and vice versa.
charlie
charlie charvey$ bzr missing
Using saved parent location: bzr+ssh://dev.example.com/srv/recipe/
You have 2 extra revisions:
------------------------------------------------------------
revno: 4
committer: Charlie Harvey <charlie@bzrinit.com>
branch nick: charlie
timestamp: Fri 2015-12-20 20:20:24 -0500
message:
  added espresso to the cake ingredients
------------------------------------------------------------
revno: 3
committer: Charlie Harvey <charlie@bzrinit.com>
branch nick: charlie
timestamp: Fri 2015-12-20 12:57:20 -0500
message:
  added ingredients for frosting.

The bzr missing command shows revisions Charlie has in his branch that aren't in the server branch. Again, Bazaar assumed he wanted to compare against the branch he forked off from, though he could have compared his branch to any other branch of the project.

Everything looks good, so Charlie pushes his changes to the server.

charlie
charlie charvey$ bzr push bzr+ssh://dev.example.com/srv/recipe
Pushed up to revision 4.

Charlie lets us know that he made a few tweaks. To get his changes into our branch, we need to pull them from the server.

recipe
recipe fmccann$ bzr pull bzr+ssh://dev.example.com/srv/recipe
bzr: ERROR: These branches have diverged. Use the missing command to see how.
Use the merge command to reconcile them.
Divergent Branches
Two branches that share a common starting point but now contain different revisions.

Error? That can't be good. Both bzr pull and bzr push are used to create mirrors of branches. That means, when we push to a branch, we intend to make it look like our branch. Similarly when we pull from a branch, we're trying to make our branch look like the other branch. We can't mirror the server's branch because our branches have diverged. That's a fancy word for saying our branches started at a common point, but now contain different revisions.

Let's use bzr missing to see how we've diverged:

recipe
recipe fmccann$ bzr missing bzr+ssh://dev.example.com/srv/recipe
You have 1 extra revision:
------------------------------------------------------------
revno: 3
committer: Fred McCann <fred@bzrinit.com>
branch nick: recipe
timestamp: Fri 2015-12-20 20:29:36 -0500
message:
  added vanilla to ingredients



You are missing 2 revisions:
------------------------------------------------------------
revno: 4
committer: Charlie Harvey <charlie@bzrinit.com>
branch nick: charlie
timestamp: Fri 2015-12-20 20:20:24 -0500
message:
  added espresso to the cake ingredients
------------------------------------------------------------
revno: 3
committer: Charlie Harvey <charlie@bzrinit.com>
branch nick: charlie
timestamp: Fri 2015-12-20 12:57:20 -0500
message:
  added ingredients for frosting.

So, we have one revision we haven't sent to the server, and Charlie has added two that we haven't seen yet. What are we supposed to do about this? The same thing we'd do any time we wanted to combine the work of two branches— merge!

recipe
recipe fmccann$ bzr merge bzr+ssh://dev.example.com/srv/recipe
 M  cake.txt
Text conflict in cake.txt
1 conflicts encountered.

Wait a minute— Bazaar told us that cake.txt is in "conflict". What does that mean?

Conflict
A conflict occurs in a merge when a file has been modified both in the current branch and the branch we want to merge. Conflicts must be resolved before the merge can be committed.

We get a conflict when we've made changes in a file and the same file has been modified in the branch we're trying to merge. Bazaar can handle multiple changes in the same file and still merge without any help from us, but when multiple people start changing the same parts of the same file, Bazaar won't let us commit the merge until we sort things out by hand.

If we want to get a list of files that are in conflict, we can call the bzr conflicts command.

Lists all conflicted files.
recipe
recipe fmccann$ bzr conflicts
Text conflict in cake.txt

Now that we know what's in conflict, how do we fix it? When a file is conflicted, Bazaar creates three additional copies of the file:

recipe
recipe fmccann$ ls -l
cake.txt
cake.txt.BASE
cake.txt.OTHER
cake.txt.THIS

The cake.txt.BASE file shows what the recipe looked like at the last common point between our version and the other version:

cake.txt.BASE
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water

Notice that this file doesn't contain any of our recent changes or Charlie's recent changes. This is the last common version of the file before either of us changed it. The cake.txt.OTHER shows what the server's branch looks like:

cake.txt.OTHER
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  1   teaspoon espresso powder

  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder

The cake.txt.THIS file shows what our version of the file looks like:

cake.txt.THIS
My Awesome Chocolate Cake Recipe

  Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  2   teaspoons vanilla extract

Bazaar creates these three files a reference to help us sort out the conflict. We don't have to look at these files or modify them; they're merely there in case we want to look at the different versions of the file before the conflict.

Those are the "clean" versions of cake.txt. The actual cake.txt file contains all the changes:

cake.txt
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
<<<<<<< TREE
  2   teaspoons vanilla extract=======
  1   teaspoon espresso powder

  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder>>>>>>> MERGE-SOURCE
Conflict Marker
A text string that bounds two changes at the same location in a conflicted file. Conflict markers show conflicting changes that Bazaar could not automatically resolve. These markers must be removed before resolving a file.

Bazaar inserts conflict markers into the text of our file every time it found overlapping changes that it couldn't cleanly merge. Our job is to manually merge everything inside a conflict marker and let Bazaar know we fixed the problem.

A conflict marker starts with the string "<<<<<<< TREE" and end with ">>>>>>> MERGE-SOURCE". Everything inside these characters is the problem to be fixed. A file can have multiple conflict markers.

You'll notice that the first part of the conflicted text is marked "TREE". These are the changes in our working tree, so this is the change that we added. The "=======" characters mark the end of the tree section and the beginning of the section marked "MERGE-SOURCE". This part of the conflict comes from the branch we're merging in. Both halves of the conflict want to occupy the same location in the file.

Sometimes we'll keep the tree version of the conflicted text, and sometimes we'll keep the merge-source version. In these cases, just delete the change you don't want to keep. Often we'll want both versions or some combination of both versions of the conflicted changes. For example, if the conflict was a function that two people had added new functionality to, the correct resolution might be a new version of the function that has the new behavior from both branches.

Charlie changed "Ingredients" to "Cake Ingredients". Since we hadn't changed that line, Bazaar had no problems merging it. However, we both added different text to the end of the file, so Bazaar wasn't sure what to do. For us, puny humans though we may be, it's clear that we want to keep both our change and all of Charlie's work. So, we're going to edit the file to reflect this and remove all the conflict markers:

cake.txt
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  <<<<<<< TREE
  2   teaspoons vanilla extract=======
  1   teaspoon espresso powder

  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder>>>>>>> MERGE-SOURCE
cake.txt
My Awesome Chocolate Cake Recipe

  Cake Ingredients

  2   cups all-purpose flour
  2   cups sugar
  1   cup cocoa
  2   teaspoons baking powder
  1.5 teaspoons baking soda
  1   teaspoon salt
  .5  cup canola oil
  1   cup milk
  2   eggs
  1   cup boiling water
  2   teaspoons vanilla extract
  1   teaspoon espresso powder

  Frosting Ingredients

  1   cup cocoa
  5   cups confectioner’s sugar
  .5  cup milk
  1.5 cups butter
  2   teaspoons vanilla extract
  .5  teaspoon espresso powder

Before we can continue, we need to tell Bazaar that we've resolved this file:

Marks conflicted files as resolved so a merge can continue.
recipe
recipe fmccann$ bzr resolve cake.txt
1 conflict resolved, 0 remaining
recipe fmccann$ ls -l
cake.txt

We use the bzr resolve command to tell Bazaar that we've sorted out all of the conflicts in a file. When we mark the cake.txt file as resolved, Bazaar removes all the other versions of the file it created for us. We can see that there are no more conflicted files, but if there were, we could always use bzr conflicts to get a list.

Once we've resolved all the conflicts and we're happy with the state of the files in the working tree, we can commit the merge to add it to our branch.

recipe
recipe fmccann$ bzr commit -m"merged in Charlie's work"
Committing to: /home/fmccann/recipe/
modified cake.txt
Committed revision 4.

And to make sure Charlie can see our latest changes, let's push our work to the server's branch:

recipe
recipe fmccann$ bzr push
Using saved push location: bzr+ssh://dev.example.com/srv/recipe/
Pushed up to revision 4.

Now that we've incorporated all the revisions in the server's branch and resolved all conflicts, we have no problems making it a mirror of our branch.

Review

Branching is what makes it easy to experiment, share your code, and collaborate with others. While there's still more to cover, you now know how to:

Back
Next: Bazaar for Agile Teams
Next