# Document Title
Skip to content
Duck Rowing
Duck Rowing
by Fred McCann
Home
About
Microblog
Mastodon
Search
RSS
Bzr Init: A Bazaar Tutorial
Posted On December 26, 2013
Around seven years ago I made the leap from Subversion to distributed version control. The problem I was trying to solve was simple- I used to commute which meant 2-3 hours of my days were spent on a train. To pass the time, I would learn new programming languages or frameworks. Eventually I became very productive in spite of the fact that a train is a hard place to work and battery technology was very poor.
I’d been a Subversion user, but there was no way I could work offline on the train. While this wasn’t the only reason I considered switching to distributed version control, it was the straw that broke the camel’s back.
Why Bazaar?
Bazaar is perhaps the least popular option of the big three, open source DVCS: Git, Mercurial, and Bazaar. At this point, I think you can make the case that Git is by far the most commonly used option. In fact, when I was experimenting on the train Git was the first tool I tried. After a few weeks with Git, it was clear things weren’t working out.
I still use Git, though not as often as other systems. Because of Github and Git’s popularity, it’s hard not to use Git. I also use Mercurial almost every day to collaborate on some projects that have chosen Mercurial. And obviously I’m a Bazaar user. The majority of the projects I work on are tracked by Bazaar.
I’m advocating for Bazaar, or that you should at least learn more about it. Unlike the infamous Why Git Is Better Than X site, I’m going to do my best to stay away from focusing on minor differences that don’t matter or blatantly inflammatory misinformation. A lot of DVCS advocacy focuses on the benefits of DVCS in general, and attributes these benefits to one tool. In reality, Git, Mercurial, and Bazaar are all completely distributed systems and all share the same basic benefits of DVCS.
In that spirit, I’d like to get a few things out of the way:
Developers successfully use Git, Mercurial, and Bazaar on projects both large and small.
Git, Mercurial, and Bazaar are basically feature equivalent.
Git, Mercurial, and Bazaar are all fast.
What I will focus on is how much friction a tool introduces, that is how easy it is to get work done, and the philosophies and design decisions, which have an impact on how the tools are used.
What makes Bazaar the best choice is it’s by far the easiest tool to use, but more important, it gets the DVCS model right.
An Ideal Model of Distributed Version Control
I assume anyone reading this likely already uses a DVCS or at least is aware of the benefits. In a nutshell, when you have a number of people both collaborating and working independently on the same codebase, you essentially have a number of concurrent branches, or lines of development. A DVCS tool makes creating and merging these branches easy, something that was a problem with Subversion or CVS. This is the core aspect of DVCS from which all the benefits flow.
The most visible consequence of this is that sharing code is divorced from saving snapshots. This is what solved my working offline problem, but in general it gives people a lot more control over how they can model their workflows with their tools.
The most important aspect of a DVCS is how well it models and tracks branches and the ease with which we can fork branches and merge branches.
Before examining why I think Bazaar gets the DVCS model correct, I’m going to present an ideal view of a project history. What I’m presenting here should be uncontroversial, and I’m going to present it in tool neutral language.
Assume the following scenario:
(trunk a) Steve creates an initial state as the beginning of a “trunk” branch
(trunk b) Steve implements feature 0 in a mirror of trunk and pushes upstream
(branch1 a) Duane branches into branch1 and commits work
(branch1 b) Duane commits work in branch1
(trunk c) Duane lands his work into trunk by merging branch1 and pushing upstream
(branch3 a) Pete branches into branch3 and commits
(branch2 a) Jerry branches into branch2 and commits
(branch3 b) Pete commits work in branch3
(branch2 b) Jerry commits work in branch2
(branch2 c) Jerry commits work in branch2
(branch3 c) Pete commits work in branch3
(trunk d) Jerry lands feature 2 into trunk by merging branch2 and pushing upstream
(trunk e) Pete lands feature 3 into trunk by merging branch3 and pushing upstream
We have four branches: Trunk, Branch 1, Branch 2, and Branch 3. A branch is a line development. We could also think of it as a thread of history.
Ideal
The dots on the diagram represent snapshots. The snapshots represent the state of our project as recorded in history. The ordered set of all snapshots is:
Project = {A, B, 1A, 1B, C, 2A, 2B, 2C, D, 3A, 3B, 3C, E}
This is the complete set of snapshots. You could call this ordering the project history across all branches. Note that this is not ordered by the time the work done, but rather the time work arrived in the trunk. Ordering either by merge time or actual time could be valid, but this ordering is more beneficial for understanding the project.
Also, snapshots have parents. The parent of a snapshot is the set of immediately preceding snapshots in the graph. For example, the parent of snapshot 3B is snapshot 3A. The parent of snapshot D is {C, 2C}.
A snapshot that has two parents represents the point at which two branches were merged.
We can further define the snapshots that define the branches:
Trunk = {A, B, C, D, E}
Branch 1 = {1A, 1B}
Branch 2 = {2A, 2B, 2C}
Branch 3 = {3A, 3B, 3C}
Furthermore we can define the concept of a head. A head, generally, is the most recent snapshot in a branch, after which new snapshots are appended.
Trunk head = E
Branch 1 head = 1B
Branch 2 head = 2C
Branch 3 head = 3C
With this ideal view of history, there are a number of questions we can answer such as:
What is the history of a branch?
The history of branch X is the set of snapshots that compose the branch.
What is the common ancestor of two branches (i.e. when did two branches diverge)?
This can be answered by traversing the graphs of the branches, starting at the head then working backwards through the graph to find a common ancestor.
When did two branches merge?
To answer this we find a snapshot that has two parents, one from each branch.
In which branch was a snapshot a added?
This is a question we’d ask when we want to determine which branch, or line of development, introduced a change. We can determine this because a snapshot is a member of only one of the branches’s set of snapshots.
Like I said, nothing here is controversial. The qualities I’m describing is what makes it possible to make sense of our project’s history.
When looking at the project above, we’d most often want to talk about the history of the trunk branch. If we made the assumption that branches were feature branches, a view of the trunk would look like this:
E [Merge] Implement Feature 3
D [Merge] Implement Feature 2
C [Merge] Implement Feature 1
B Implement Feature 0
A Initial State
The view of the trunk is the “big picture” of the project. The merge badges are an indicator that there is more history available in the associated feature branch’s history. For example, Branch 2 might look like:
2C Completion of Feature 2
2B Partial Completion of Feature 2
2A Start work on Feature 2
The branches don’t have to be features- they could represent individual developers, teams, etc. The distinction is that branches are lines of development with histories, and we want to see different levels of detail in the history depending on what branch we’re examining.
Git
Git is by far the most popular of the big three open source DVCS. It is also an order of magnitude harder to learn and to use than either Mercurial or Bazaar. I’ve expounded on this in my post on the Jenkins incident and one on Cocoapods.
At this point, I don’t think it’s inflammatory to point out that Git is difficult to use. A simple Google search will turn up enough detail on this point that I won’t address it here. Git’s interface is a very leaky abstraction, so anyone who’s going to be very successful using Git will eventually have to learn about Git’s implementation details to make sense of it.
This introduces a lot of cognitive overhead. More plainly, a user of Git has to expend a significant amount of focus and attention on using Git that would otherwise be spent on their actual work.
This overhead is inherent in a lot of Git commands as the metaphors are fuzzy and a lot of commands have bad defaults.
While Git’s interface is more complicated than is necessary, it certainly isn’t a large enough concern to stop people from successfully using it, or other UIs built on top of it.
The core problem with Git is that it gets the branching model wrong.
The first problem with Git is that it uses colocated branches by default. A Git repository has a single working directory for all branches, and you have to switch between them using the git checkout command.
This is just one source of cognitive overhead. You only have a single working tree at a time, so you lack cues as to which branch you’re operating in. It also makes it difficult to work in more than one branch at a time as you have to manage intermediate changes in your working directory manually before you can perform the switch with git stash.
A better default would be to have a directory per branch. In this simpler scenario, there’s never a concern about collisions between changes in the working directory and you can take advantage of all the cues from working in separate directories and take advantage of every tool that’s been developed over the years that understands files and directories.
Colocated branches essentially make directories modal, which requires more of your attention.
Of course, you could make the case that collocated branches take up less space, but considering how cheap storage is, this doesn’t impact most projects, which is why it’s an odd default behavior.
Another case you could make is that you can share intermediate products, such as compiled object files. While this seems like a good rationale, a better, simpler solution would to use a shared directory for build products.
While colocated branches aren’t a deal breaker, there is a more fundamental flaw in Git’s branch model. As I described in the ideal DVCS model, a branch is a line of development or a thread of history. Git in fact does not track this history. In Git, a branch is merely the head pointer, not the history.
In Git, branch 3 = 3C, not {3A, 3B, 3C}
What we would consider the history only exists in the reflog, which will eventually be garbage collected.
By default, when we send our work up to a remote repository, not only is the reflog information not sent, the head pointer is also discarded, so any notion of a branch is lost.
This means that Git does not match the ideal model I’ve put forth, and is not able to answer certain questions such as, in which branch was a snapshot a added?
Let’s take the example in the ideal model. We’ll assume each branch is a feature, and that a separate developer is working on each in their local repository. We’ll also assume that our “trunk” branch is the master branch on some remote repository.
If we use the default behavior in Git, and make all the snapshots as defined in the ideal model in the same chronological order, this is what our graph looks like:
Git
$ git log
commit 314397941825fb0df325601519694102f3e4a25b
Merge: 56faf98 a12bd9e
Author: Pete
E Implement Feature 3
commit 56faf98989a059a6c13315695c17704668b98bda
Author: Jerry
2C Complete Feature 2
commit a12bd9ea51e781cdc37cd6bce8a3966f2b5ee952
Author: Pete
3C Complete Feature 3
commit 4e36f12f1a7dd01aa5887944bc984c316167f4a9
Author: Jerry
2B Partial Completion of Feature 2
commit 2a3543e74c32a7cdca7e9805545f8e7cef5ca717
Author: Pete
3B Partial Completion of Feature 3
commit 5b72cf35d4648ac37face270ee2de944ac2d5710
Author: Jerry
2A Start work on Feature 2
commit 842dbf12c8c69df9b4386c5f862e0d338bde3e01
Author: Pete
3A Start work on Feature 3
commit b6ca5293b79e3c37341392faac031af281d25205
Author: Duane
1B Complete Feature 1
commit 684d006bfb4b9579e7ad81efdbe9145efba0e4eb
Author: Duane
1A Start work on Feature 1
commit ad79f582156dafacbfc9e2ffe1e1408c21766578
Author: Steve
B Implement Feature 0
commit 268de6f5563dd3d9683d307e9ab0be382eafc278
Author: Steve
A Initial State
Git has completely mangled our branch history.
This is not a bug, this is a fundamental design decision in Git. For lack of a better term, I’d differentiate Git from other DVCS systems by saying it’s patch oriented. The goal of Git is to arrive at some patch that describes the next state in a project- tracking history is viewed as not as important as tracking the patch or the content.
This means we don’t have a good way of figuring out what’s going on with our conceptual trunk branch. Only one branch was recorded (incorrectly), and everything is shoved into master in chronological order. We also don’t have a good way to see the history of any particular branch, nor which branch a branch was forked from or when it was merged back in.
If you’ve ever had to figure out how some change entered the system or whether some branch is cleared to be released, you’ll know that this matters.
Git’s broken branching model means the burden of correctly recording or interpreting history is thrust upon the user.
The safest workaround is to do all branch merging with the –no-ff option. This step will correct the object graph since it will memorialize all merges. The default Git graph loses snapshots {C,D}, but preventing fast forward merges will fix that.
The second problem is that Git doesn’t capture enough information in each snapshot to remember the branch. To get around this, we have to manually insert that information, in-band, in the commit message.
So, when a developer commits work, they’ll manually tag the commit with branch metadata. Then we can at least use git log’s grep option to see work clearly:
$ git log --grep="\[Trunk\]"
commit 5d155c1d81b9c2803f2f2de890298ca442597535
Merge: 6a4ba95 0ed512b
Author: Pete
E [Trunk][Merge] Implement Feature 3
commit 6a4ba950c807d9cb8fe55236e8d787b4fd4a663a
Merge: f355800 36ef31b
Author: Jerry
D [Trunk][Merge] Implement Feature 2
commit f3558008a174e56735d95432b5d27cf0a26db030
Merge: df9f0df 3bdf920
Author: Duane
C [Trunk][Merge] Implement Feature 1
commit df9f0df24faf0de5e8653626b9eb8c780086fc28
Author: Steve
B [Trunk] Implement Feature 0
commit 67ba4b110a8cb45ba4f5a4fc72d97ddffafd7db0
Author: Steve
A [Trunk] Initial State
$ git log --grep="\[Branch1\]"
commit 4b327621647987c6e8c34d844068e48dab82a6ab
Author: Duane
1B [Branch1] Complete Feature 1
commit 469a850c458179914f4a79c804b778e2d3f1bfbe
Author: Duane
1A [Branch1] Start work on Feature 1
This is sort of a clunky and error-prone approach, but tagging commit messages is a common way to compensate for Git’s lack of branch support.
There is a more drastic and dangerous approach which is to use a Git rebase workflow, potentially with squashing commits.
In this scenario, rather than adding more information to commits, you actively rewrite history to store even less data than Git captures by default. In the rebase workflow, rather than getting the mangled Git graph produced by the normal Git process, we get a straight line:
Rebase
It’s ironic that the rebase workflow, a reaction to Git not storing enough information to model or make sense of project history, is to erase even more information. While the straight line graph is easier to read, it’s completely unable to answer questions laid out in the ideal model.
This is what a Git user might refer to as a “clean” history. Your average Mercurial or Bazaar user is confused by this because both of the other tools correctly model branches, and neither one would consider the butchered object graph “clean”. The rebase workflow conflates readable logs with “clean” histories.
Rebasing is significantly worse than the –no-ff work around as you can squash commits that might actually matter. Another problem with rebasing, beyond that you might accidentally select the wrong commits, is that it prevents collaboration.
In order to use a rebase workflow, you have to adopt byzantine notions of “private” and “public” history. If you rebase away (i.e. discard) commits that have been shared with others, it leads to complicated cascading rebases. So, if I’m working on a feature, and I intend to use the rebase workflow, I’m not going to be able to make ad-hoc merges with colleagues because no one can count on my snapshots sticking around. Basically, sharing my work becomes difficult. One of the benefits of DVCS is that collaborating should be easy.
It’s very hard to recommend Git. It’s the most difficult tool to use, and its broken branch model gives rise to Git specific solutions to Git inflicted problems that Git users may think are best practices rather than kludges.
44354577
Mercurial
Mercurial is much easier to use than Git. This is not to say that there aren’t some UI concerns. The biggest gripe I have with Mercurial is that it separates pulling changes into a repository from merging changes, which can lead to a pending merge situation where you have multiple heads on the same branch as they have not yet been merged. Normally this isn’t a problem, but if you’re careless, you can end up with three or four heads in a repository before catching it.
I’ve never understood if the manual multiple heads pull/merge pattern was considered a feature in Mercurial, or if it was an implementation detail leaking through. It certainly feels like that latter just as does Git’s “feature” of exposing the index to users.
That said, the gulf between Mercurial usability and Git usability is akin to the distance between New York City and Los Angeles. While Mercurial’s UI is more complicated than Bazaar’s, that difference is more like the distance between New York City and Boston, so I won’t belabor the point.
Mercurial has two popular models for branching (though I believe there are technically four). The first and most common model is to branch by cloning. You can think of this as a euphemism for not branching at all.
In Mercurial, all work happens in a branch, but by default, all work happens in the same branch, called the “default” branch. The primary advantage branching by cloning has is that it’s easier than the alternatives. There’s fewer commands to know and call and there’s a working directory per branch. However, if we apply all of the commits in the same chronological order as I described in the ideal model example, here’s what our changeset graph looks like:
Murky
$ hg log
changeset: 10:d21a419a293e
tag: tip
parent: 9:53d61d1605f9
parent: 6:7129c383aa3b
user: Pete
summary: E Implement Feature 3
changeset: 9:53d61d1605f9
user: Pete
summary: 3C Complete Feature 3
changeset: 8:fb340c4eb013
user: Pete
summary: 3B Partial Completion of Feature 3
changeset: 7:2a35bc51a28d
parent: 3:e9292c14c2b2
user: Pete
summary: 3A Start work on Feature 3
changeset: 6:7129c383aa3b
user: Jerry
summary: 2C Complete Feature 2
changeset: 5:b13281f66a25
user: Jerry
summary: 2B Partial Completion of Feature 2
changeset: 4:187abbd1b3c4
user: Jerry
summary: 2A Start work on Feature 2
changeset: 3:e9292c14c2b2
user: Jerry
summary: 1B Complete Feature 1
changeset: 2:a5e8ccb38d38
user: Duane
summary: 1A Start work on Feature 1
changeset: 1:b60a08bf46c7
user: Steve
summary: B Implement Feature 0
changeset: 0:747376f7cfb9
user: Steve
summary: A Initial State
Mercurial has completely mangled our branch history. Well, to be fair, this is what happens when we don’t actually use branches.
Mercurial fared a bit better than Git on the logging front- at least the order of changesets reflects the order they were merged, though I’ve seen projects where either the complexity of the merges or perhaps because of an older version of Mercurial, the log looked nearly identical to Git’s.
Unlike Git, Mercurial has no equivalent to the –no-ff option for merging. This means that with this clone-as-branch model, we can’t get snapshots {C,D}.
While Git discards branch histories, Mercurial remembers them. So, if we use the named branches workflow, we have a different story:
Murky2
$ hg log
changeset: 15:5495a79dbe2b
tag: tip
parent: 10:ab972541c15e
parent: 14:f524ef255a5d
user: Pete
summary: E Implement Feature 3
changeset: 14:f524ef255a5d
branch: branch3
user: Pete
summary: 3D Close Branch3
changeset: 13:90ea24b0f0e1
branch: branch3
user: Pete
summary: 3C Complete Feature 3
changeset: 12:82bef28d0849
branch: branch3
user: Pete
summary: 3B Partial Completion of Feature 3
changeset: 11:d26ae37169a3
branch: branch3
parent: 5:7f7c35f66937
user: Pete
summary: 3A Start work on Feature 3
changeset: 10:ab972541c15e
parent: 5:7f7c35f66937
parent: 9:4282cb9c4a23
user: Jerry
summary: D Implement Feature 2
changeset: 9:4282cb9c4a23
branch: branch2
user: Jerry
summary: 2D Close Branch2
changeset: 8:0d7edbb59c8d
branch: branch2
user: Jerry
summary: 2C Complete Feature 2
changeset: 7:30bec7ee5bd2
branch: branch2
user: Jerry
summary: 2B Partial Completion of Feature 2
changeset: 6:bd7eb7ed40a4
branch: branch2
user: Jerry
summary: 2A Start work on Feature 2
changeset: 5:7f7c35f66937
parent: 1:635e85109055
parent: 4:52a27ea04f94
user: Duane
summary: C Implement Feature 1
changeset: 4:52a27ea04f94
branch: branch1
user: Duane
summary: 1C Close Branch1
changeset: 3:ceb303533965
branch: branch1
user: Duane
summary: 1B Complete Feature 1
changeset: 2:a6f29e9917eb
branch: branch1
user: Duane
summary: 1A Start work on Feature 1
changeset: 1:635e85109055
user: Steve
summary: B Implement Feature 0
changeset: 0:918345ee8664
user: Steve
summary: A Initial State
The hg log command is going to show us something that looks very similar to the previous log with a few changes. First off, all of the merge changesets are present. Second, every changeset is associated with a branch (default is assumed if not listed). The last thing to notice is we have an extra changeset per branch to “close” the branch.
Also, we can now easily show the history of a branch:
$ hg log -b default
changeset: 15:5495a79dbe2b
tag: tip
parent: 10:ab972541c15e
parent: 14:f524ef255a5d
user: Pete
summary: E Implement Feature 3
changeset: 10:ab972541c15e
parent: 5:7f7c35f66937
parent: 9:4282cb9c4a23
user: Jerry
summary: D Implement Feature 2
changeset: 5:7f7c35f66937
parent: 1:635e85109055
parent: 4:52a27ea04f94
user: Duane
summary: C Implement Feature 1
changeset: 1:635e85109055
user: Steve
summary: B Implement Feature 0
changeset: 0:918345ee8664
user: Steve
summary: A Initial State
$ hg log -b branch1
changeset: 4:52a27ea04f94
branch: branch1
user: Duane
summary: 1C Close Branch1
changeset: 3:ceb303533965
branch: branch1
user: Duane
summary: 1B Complete Feature 1
changeset: 2:a6f29e9917eb
branch: branch1
user: Duane
summary: 1A Start work on Feature 1
When we take advantage of Mercurial’s branching support, you can see how the Git notion of “cleaning” history is ridiculous.
There are some costs to using the named branches workflow. The first one is there’s more manual work to set up and tear down branches. We ended up with additional commits to “close” branches, which is a bit of bookkeeping Mercurial pushes off on the user. It’s possible with planning to make the final commit in a brach and close it at the same time, but in practice you may not actually land a branch in the trunk until after a review or a QA process, so you may not know when it’s time to close the branch until that happens.
Another problem with Mercurial’s implementation is it uses colocated branches with a single working directory by default. The additional cognitive overhead imposed by this choice is what I suspect is the reason more Mercurial users use the clone as a branch approach.
Also, using named branches means your repository will have multiple heads most of the time (one per branch). For intermediate and advanced Mercurial users that have a good mental model of what’s going on, this is no big deal. But beginning Mercurial users are often trained to see multiple heads as a sign of a pending merge, and a situation to be rectified.
In general, the named branch approach, while fully supported and completely functional, feels like an afterthought. Calling the hg branch command to create a new branch issues a warning as though it was advising you against using branches:
$ hg branch branch1
marked working directory as branch branch1
(branches are permanent and global, did you want a bookmark?)
Just for clarification, you do not want a bookmark.
Similarly, when pushing work to an upstream repository, you have to tell Mercurial that yes, you intended to push a new branch:
hg push --new-branch
If I had to sum up Mercurial, I’d call it repository oriented. Unlike Git, it fully tracks history, but it treats the repository as the most prominent metaphor in the system at the expense of the branch. Key Mercurial commands such as clone, pull, push, and log operate on repositories, while it has a separate branch command to operate on branches.
If you’re willing to put up with a bit of extra work and some odd warnings from Mercurial, the named branches workflow meets the ideal model I’ve presented. Even if you choose the clone as a branch model, you’d still have a lot less cognitive overhead than using Git, so while I find it hard to recommend Git, if you’re already a Mercurial user, I’d urge you to consider using named branches.
5pqqp
Bazaar
Bazaar is the simplest of the three systems to use. Common use of Bazaar is much like the clone as a branch approach of Mercurial without exposing the user directly to head pointers or multiple heads. As I said in the Mercurial section, Git is an order of magnitude harder to use than either Mercurial or Bazaar. Bazaar is unquestionably simpler from a UI perspective than Mercurial, especially compared to the named branches workflow, but it’s nothing compared to the gulf between Git and the others in usability.
Also, Bazaar by default uses a directory per branch, so each branch gets its own working directory, meaning you don’t have to use kludges like stashing working directory changes when switching branches. Switching branches is as easy as changing directories. Bazaar does support a method for colocated branches, but it is not the default and it’s rarely needed. As I pointed out in the Git section, there are easy ways to tackle the problems that colocated branches are supposed to solve.
Let’s repeat the experiment with the normal Bazaar workflow and see how it compares to the ideal model:
Bazaar
Bazaar, by default, exactly matches the ideal model. Now, I know my more cynical readers will assume that this is because I picked Bazaar’s model as the “ideal” model, but they would be incorrect. Bazaar is not the first DVCS I used, nor did my ideal model derive from Bazaar. The ideal model is what I think should happen when branching and merging. As I said earlier, I don’t think the model I laid out is controversial. I use Bazaar because it meets the model, not the other way around.
In fact, I think a lot of users get confused by Git and Mercurial’s branch by clone workflow precisely because the history graph that they record does not resemble user’s mental model.
Let’s take a look at the log:
$ bzr log
------------------------------------------------------------
revno: 5 [merge]
committer: Pete
branch nick: trunk
message:
E Implement Feature 3
------------------------------------------------------------
revno: 4 [merge]
committer: Jerry
branch nick: trunk
message:
D Implement Feature 2
------------------------------------------------------------
revno: 3 [merge]
committer: Duane
branch nick: trunk
message:
C Implement Feature 1
------------------------------------------------------------
revno: 2
committer: Steve
branch nick: trunk
message:
B Implement Feature 0
------------------------------------------------------------
revno: 1
committer: Steve
branch nick: trunk
message:
A Initial State
------------------------------------------------------------
Use --include-merged or -n0 to see merged revisions.
This is very different than the logs presented by either Mercurial or Git. Each of the logs I’ve shown were taken from the upstream “authoritative” repository. Unlike the others, bzr log does not operate on a repository, it operates on a branch. So by default, we see the history of the project from the perspective of the trunk branch.
Another distinction with Bazaar’s logs is that they are nested. To show the complete log, we can set the nesting level to 0, which will show an infinite level of nesting:
$ bzr log -n0
------------------------------------------------------------
revno: 5 [merge]
committer: Pete
branch nick: trunk
message:
E Implement Feature 3
------------------------------------------------------------
revno: 3.2.3
committer: Pete
branch nick: branch3
message:
3C Complete Feature 3
------------------------------------------------------------
revno: 3.2.2
committer: Pete
branch nick: branch3
message:
3B Partial Completion of Feature 3
------------------------------------------------------------
revno: 3.2.1
committer: Pete
branch nick: branch3
message:
3A Start work on Feature 3
------------------------------------------------------------
revno: 4 [merge]
committer: Jerry
branch nick: trunk
message:
D Implement Feature 2
------------------------------------------------------------
revno: 3.1.3
committer: Jerry
branch nick: branch2
message:
2C Complete Feature 2
------------------------------------------------------------
revno: 3.1.2
committer: Jerry
branch nick: branch2
message:
2B Partial Completion of Feature 2
------------------------------------------------------------
revno: 3.1.1
committer: Jerry
branch nick: branch2
message:
2A Start work on Feature 2
------------------------------------------------------------
revno: 3 [merge]
committer: Duane
branch nick: trunk
message:
C Implement Feature 1
------------------------------------------------------------
revno: 2.1.2
committer: Duane
branch nick: branch1
message:
1B Complete Feature 1
------------------------------------------------------------
revno: 2.1.1
committer: Duane
branch nick: branch1
message:
1A Start work on Feature 1
------------------------------------------------------------
revno: 2
committer: Steve
branch nick: trunk
message:
B Implement Feature 0
------------------------------------------------------------
revno: 1
committer: Steve
branch nick: trunk
message:
A Initial State
Nested logs is a simple but incredibly useful innovation. We don’t really need a graphical tool to visualize history. Also, it’s clear that the Git notion of “cleaning” history is equally as ludicrous as it was when looking at Mercurial’s logs, perhaps even more so.
There is a real advantage though as compared to Mercurial’s named branches workflow. For lack of a better term, Bazaar is branch oriented. Both git init and hg init create new repositories. However, bzr init creates a new branch. Both git clone and hg clone create copies of repositories. The equivalent command in Bazaar, bzr branch forks a new branch. Both git log and hg log examine the history of the repository. The bzr log command shows branch history.
It’s a very subtle change in point of view. Bazaar elevates branches to the primary metaphor, relegating the concept of repository to more of a background player role.
The result is that the basic Bazaar workflow is to always branch and to always accurately track branch history. It accomplishes this with a cognitive overhead that is comparable to working with a centralized system like Subversion or Mercurial’s clone as a branch method.
Bazaar does have some downsides. The most significant one is it has the smallest user base of the big three open source DVCS. This means it will be a little harder to find answers to your questions in blog postings and Stack Exchange answers. Also, commercial hosting companies like Bitbucket aren’t going to offer Bazaar support. Nor is it as actively developed as Mercurial and Git.
However, Bazaar’s positives so strongly outweigh the negatives that you’d be crazy to not consider Bazaar.
44354687
Branching is the Secret Sauce in DVCS
What makes DVCS work is the ability to handle branches from creation to merging, and to do it in the least intrusive way possible so we can get our work done.
Bazaar gets branching right, by default. It’s also the easiest system to learn and use. You don’t have to expend nearly as much focus and attention on Bazaar as Mercurial and Git, which means you have more attention to devote to your actual work.
I did not go into detail showing the commands used to create the graphs and logs presented in this post because rather than getting bogged down in the syntax and the details, I want to make the big picture clear. I leave the commands as an exercise to motivated readers so they can prove to themselves that their favorite tool just might not be recording history as they visualize it.
Bzr Init: A Bazaar Tutorial
At this point, you might be wondering where the actual tutorial is. First I wanted to convince you why Bazaar is a good choice, if not the best choice.
The actual tutorial is much longer than this rant, so I gave it it’s own site:
Bzr Init: A Bazaar Tutorial
This tutorial is not meant to be exhaustive; it exists to show you how easy Bazaar is and how it can be easily adapted to your workflow. It’s inspired by (let’s say ripped off from) Joel Spolsky’s Mercurial Tutorial- HgInit. Hginit is not only one of the best Mercurial tutorials, it’s one of the best DVCS tutorials. I’d recommend you read it even if you’re not a Mercurial user.
Hopefully you’d get the same experience from the Bzr Init tutorial. In any case, if I’ve piqued your interest, read on!
Categories: Programming
Tagged: bazaar, bzr, dvcs, git, hg, mercurial, vcs, version control
Post navigation
Previous Post‹
Not That There’s Anything Wrong With That, 1952 Edition
›Next Post
Chromebook Boom?
© 2024 Duck Rowing |
Footer Menu
Privacy Policy
Home About Microblog Mastodon Search RSS