Although both Bazaar-NG (bzr) and CVS are Version Control systems, moving from one to the other can be difficult. This is a small tutorial designed to help explain how best to use bzr from the perspective of a CVS user.
It's often said that bzr is a distributed version control system, as opposed to CVS, which is a centralized version control system. Often, this is explained in terms of the working copy; you don't have to connect to a server to commit in bzr. This is true, but there is much more to it than that.
Distributed version control distinguishes itself in its heavy use of branching. CVS users generally do not branch, because branching can be unreliable and complicated, but in general the Subversion branching model is similar to that of CVS: a branch is an alternate line of development, named and tracked in the same central repository. In distributed systems, there are no formal relationships between branches at the file level, and two branches of the same development can even live on separate systems. In most distributed systems, other means are used to keep branches in sync; bzr, for example, uses global revision IDs to identify each changeset, and while two branches from a common ancestor may have different revision IDs, they can relate to each other through their shared revisions and the global IDs associated with them.
From a CVS perspective, then, distributed version control can be likened to making a copy of the entire CVS module on the server for every branch, and creating a separate history in each. This is combined with sophisticated tools for automatically merging the different changes on each branch together, so the separate copies of the repository don't diverge forever. Further, because branching is relatively inexpensive, developers are encouraged to branch whenever it makes sense.
Bazaar-NG actually combines the distributed model with elements of the centralized model. CVS users who are comfortable with the CVS way of doing things can mimic it to a degree in their bzr work habits.
To demonstrate how bzr works, we'll go through a typical workflow. Note that bzr has very good help, for an open source project; you can type "bzr help" to get some simple help instructions, or "bzr help (command)" to get help for any of the commands you see here. In general, bzr tries to mimic CVS's command line usage, though this isn't always true.
Typically, when getting ready to make a change to a project, a bzr user will branch the project first. We'll use the "appbat" project in the LSB as our example:
bzr branch http://bzr.freestandards.org/lsb/3.1/appbat appbat-upstream
This creates a local branch of appbat in "appbat-upstream". (The actual process of branching can take a long time, so there are alternate ways of doing the same thing. See the main Bazaar-NG page for more information.) We can use this branch to keep track of what's going on upstream without having to actually pull upstream changes into our project, which can be handy.
After doing this, we create our branch for actual development:
bzr branch appbat-upstream appbat-work
Then we can start working:
cd appbat-work emacs foo.c (...)
Generally, it works best to group changes into changesets, where all changes in a set have the same purpose. Any time we get to that point, we can commit:
This works just like it does in CVS, except that the change is only committed to our local branch, appbat-work; appbat-upstream and the official appbat repo haven't changed at all yet.
CVS users are used to typing "cvs up" all the time to learn what local changes have been made. In bzr-land, the equivalent command is "bzr status", except that the "status" command does not pull down any changes from upstream; it just reports on the current state of the working copy. Subversion users will recognize this as identical to "svn status".
Remember how we branched from the site to appbat-upstream, and then from appbat-upstream to appbat-work? This seemed like a lot of busy-work, but we now can watch upstream changes more easily.
When a branch is created, it remembers where it "came from", and several bzr commands will use that information. Let's see, for example, if there have been any upstream changes in appbat:
cd ../appbat-upstream bzr pull
The "pull" command just sucks down any changes from the source branch into the current branch. It assumes that there have not been any local changes; more properly, it fails if it finds there have been any unmerged local changes. In this case, we haven't made any local changes to appbat-upstream, so everything works fine.
Let's say there have been some changes. How do we see them? These tools should be familiar to CVS users:
bzr log bzr diff bzr annotate
In general, they work like you'd expect. But don't be surprised to see that your commits in appbat-work don't show up in appbat-upstream.
Now, let's say that we've looked over the upstream changes, and have decided that it's probably a good idea to merge them into our work, because we want to avoid conflicts that we might run into in the future. How do we do that? We start by making sure there are no uncommitted changes in our branch:
cd ../appbat-work bzr status
If there are uncommitted changes, it's best to commit them first. Then:
The "merge" command is like the "pull" command, except that it doesn't commit the changes to the repository. Instead, all of the upstream changes are applied to the working copy, and their information is carefully recorded. If you do a "bzr status" now, you'll see not just a list of modified files, but a list of upstream commits that haven't been applied yet.
Occasionally, some local changes will conflict with upstream changes. This doesn't happen as often as under CVS, because bzr does a better job of tracking the history of changes and making sure the right changes get applied at the right time. Conflicts are detected, marked, and reported during the merge, and can be shown after the merge with "bzr status". Once you resolve a conflict, you can clear the mark with:
bzr resolve conflicted_file
Once you've taken care of any conflicts, you can commit, which will write all the upstream changes to this branch.
We now have an interesting change to appbat, but no one can see it, because it's in our local branch on our local hard disk. To make it more publicly available, we want to publish the branch.
There are two ways of publishing a branch: by making your whole branch available on the Web, or by creating a bundle file, which is a single file containing all the differences between two branches. Bundles are easy to make and share, and don't require a public Web server. Web shares, on the other hand, are often easier for others to use.
To publish a branch on the Web, we basically copy our entire branch to a Web server somewhere, and give people the URL to the copied branch. They can then "bzr branch" from our branch, just as we branched from the official project.
For this example, we'll use the unofficial FSG area (http://bzr.freestandards.org/unofficial). We have a shell account on webservices, the FSG server that serves up bzr.freestandards.org, so we can copy to the shared area there:
cd .. scp -r appbat-work email@example.com:/srv/www/bzr/unofficial/appbat-me
(The repository is now published, and can be accessed with bzr, but it's not registered with the bzr-webserve front end yet. See Bazaar-NG User Branches for how to do that.)
Someone else can now grab our changes and try them out:
bzr branch http://bzr.freestandards.org/unofficial/appbat-me appbat-him
If that person has their own branches of appbat checked out, they can look at the differences, and incorporate them into their own work:
bzr diff appbat-mystuff appbat-him cd appbat-mystuff bzr merge ../appbat-him
Even though the two branches weren't directly based on each other, bzr can figure out their common ancestry.
Back on our machine, we've made more changes. We can publish those changes by copying the branch again. Let's use "rsync" to save time:
rsync -avv appbat-work/* firstname.lastname@example.org:/srv/www/bzr/unofficial/appbat-me
Bzr supports publishing directly as well, if sftp support is enabled. (This requires having the paramiko Python module installed; see the main Bazaar-NG page for details.) In this case, you can publish like this:
bzr push sftp://email@example.com/srv/www/bzr/unofficial/appbat-me
Generally, sftp is slower than rsync. But sftp can do some things rsync can't; see below, under "Being More CVS-Like", for one example.
Since bundles store the differences between two branches, we need a branch to use as a reference. Generally, this will be a published branch everyone can easily get to. For our example, we'll use the upstream branch as our reference.
To create the bundle, just go to our branch working directory (where we make our changes) and run the "bzr bundle" command, like so:
cd appbat-work bzr bundle --output=../my-bundle ../appbat-upstream
(You could also use the URL for the upstream repository, but using a local branch copy is obviously quicker.)
The file "../my-bundle" is now ready to be sent to a mailing list, uploaded to our PQM, etc. When someone else wants to use the file to see what changes you've made, that person can use the bundle file to recreate your repository, as follows:
mkdir appbat cd appbat cp /some/where/my-bundle his-bundle bzr branch http://bzr.freestandards.org/lsb/3.1/appbat appbat-him cd appbat-him bzr pull ../his-bundle
If the base repository has changed, the "bzr pull" may not succeed. In that case, you can merge the bundle file instead, and resolve conflicts as if it were merged from a real branch. Or, you can ask the bundle maker to update the bundle.
CVS users are generally used to the idea that "cvs commit" publishes changes too, and may not be too happy at the burden of having to remember to re-publish after commits. Bzr has a feature that can mimic this feature of centralized version control systems; it's called "bound branches".
Basically, a bound branch is a branch that causes commits to happen twice: once locally, and once to an external branch. The external branch acts like the CVS "server", taking commits and coordinating them between the branches bound to it. Frequently, the published branch is used as the external branch, and the local working copy is bound to it; this causes commits to act like CVS, in that the commit both writes the change to the repository and publishes it.
We can do this in a new branch:
bzr checkout sftp://firstname.lastname@example.org/srv/www/bzr/unofficial/appbat-me appbat-work-bound
Another way to do it:
bzr branch http://bzr.freestandards.org/unofficial/appbat-me appbat-work-bound cd appbat-work-bound bzr bind sftp://email@example.com/srv/www/bzr/unofficial/appbat-me
(The latter method might be better if you use the speed optimization tricks documented on the main Bazaar-NG page.)
Then, when you commit changes on the bound branch, you'll be asked for your ssh key passphrase or account password, and the changes will be committed both locally and on your published branch.
You can only bind to branches you have direct write access to, such as your published branch. You can't bind to the official branches. Thus, you will still have to create a personal branch for your changes when using FSG official branches.
You can even bind multiple local branches to a single upstream branch. If you commit changes to one bound branch, you can propagate those changes to other bound branches with a familiar command:
We have now made some important changes to appbat, we've published those changes, and the other developers agree that these are good changes. How do we get them into the official tree?
The FSG uses something called the Bazaar-NG Patch Queue Manager, or PQM for short. This is an automated service which will merge published branches with the official branches once they're ready.
First, we make sure we have no unpublished changes:
cd appbat-work bzr status
Then, we do a final merge with upstream. This allows us to fix conflicts; PQM will reject merges that create conflicts.
cd ../appbat-upstream bzr pull cd ../appbat-work bzr merge bzr commit
After this is done, we publish our tree, via bundles or the Web.
Then. we browse to the bzr-webserve pages for our official project (in this case, https://bzr.freestandards.org/lsb/3.1/appbat). One of the links on the top menu is "merge"; we click that, log in, and are presented with a form. We give the form our E-mail address, a short description of the changes made in that branch, and our published branch, either as a Web location (for Web publishing) or via file upload (for a bundle file). A few minutes after clicking Submit, we receive an E-mail telling us if our merge succeeded or failed.
Assuming our merge succeeded, we update our working copies:
cd ../appbat-upstream bzr pull cd ../appbat-work bzr pull
Or, for bound branches:
cd ../appbat-upstream bzr pull cd ../appbat-work-bound bzr pull ../appbat-upstream
In both versions, that last "bzr pull" wasn't an error; the pull, in this case, causes our history to reflect the merge. At this point, from a functional point of view, our work branch is pristine again, since we no longer have unmerged local changes, and we can "bzr pull" from appbat-upstream without difficulty. We can re-use the branch for more work, or get rid of it and start again with a fresh branch.