1. Preface

This is intended as a quick & dirty summary of using bzr (http://bazaar.canonical.com/) to develop ctwm (http://www.ctwm.org/).

That this is a combination of “how to use bzr” and “recommended workflow for working on ctwm”. It doesn’t by any means describe the only way work can be done, but does give workflows that should keep things orderly. If you don’t already know bzr yourself, I’d stick pretty close to it.

Actually, it’s not really as short and quick as originally intended. It’s no substitute for grokking the full bzr docs, but is probably enough to get you through day to day working on ctwm by itself.

1.1. See Also

Various bits of the actual bzr docs.

http://doc.bazaar.canonical.com/bzr.2.6/en/

The main bzr docs provide full reference, tutorials, etc.

http://wiki.bazaar.canonical.com/MatthewFuller/SpotDocs

SpotDocs are some bits I wrote up years ago for general bzr support purposes, and I still think are generally useful. In particular, the PiecesInBrief doc describing the taxonomy and vocabulary of bzr.

2. Intro and Justifications

The most important difference in day-to-day working with bzr versus mtn is that while in mtn you sync a repository containing multiple branches, with your working trees (or checkouts) generally separate from it, in bzr you sync individual branches generally colocated with their working trees. I give workflows here using a single shared repository, which holds the revisions of the history; skipping that step will involve each branch holding an individual full history copy, which wastes disk space (cheap) and disk/network IO (not so cheap).

The second most important difference is the special treatment of first or left-hand parents, and their use in making merged revisions "roll up" under them. This is why I’m recommending here the use of a checkout rather than an independent branch for the local trunk. This adds minor complication in having to remember the separate commands for updating it etc, but I think that’s a good tradeoff. It’s also important in the context of bzr merge.

3. Initial setup

3.1. Configuring bzr

There are various things you can do to configure your local bzr, including aliases (see below) and so forth. But the only thing really necessary is telling bzr who you are via the whoami command. This sets your identity, put on the revs you commit. Set this to your name and email.

% bzr whoami "Matthew Fuller <fullermd@over-yonder.net>"

3.2. Configuring your workspace.

First we’ll setup a shared repository, which will share the history storage across all the branches. I’ll assume all your ctwm work happens in ~/work/ctwm/bzr. It doesn’t matter much where you declare this, except that you want all your branches to be ‘under’ it in the filesystem, and probably want it to be pretty close to them (e.g., don’t make your shared repo directly in ~ or ~/work; that might cause you to accidentally share it with other bzr projects, which you don’t want).

% cd ~/work/ctwm/bzr
% bzr init-repo .
# Don't miss that trailing period.

This will leave you a ~/work/ctwm/bzr/.bzr directory, which contains the repo. You should never need to directly deal with this again; any new branches you make underneath ~/work/ctwm/bzr will automatically use it.

3.3. Hosting

We use Launchpad for hosting the code repositories. The main project site is https://launchpad.net/ctwm. Launchpad provides anonymous read access to the repository over dumb http, which is somewhat slower than a smart transport. Using a smart transport is faster, and also required for write access.

To use the smart bzr+ssh transport, you’ll need to create a Launchpad account if you don’t already have one, setup an ssh key for LP to use, and use bzr lp-login to tell bzr about your launchpad account.

There’s a special lp: directory service in bzr, so using lp:something as the location for a branch does some internal translation to the real URL. So references like lp:ctwm in this doc really should be used just as written.

  • lp:ctwm is the trunk.

  • lp:ctwm/xyz names can be assigned as needed.

Aside from those short names, the general structure of lp: lookups is lp:~OWNER/PROJECT/BRANCH, so lp:ctwm is actually an alias to lp:~ctwm/ctwm/trunk; i.e., a branch owned by the ctwm team, for the ctwm project, called trunk (names don’t have to be unique across users).

Committers have write access to trunk and any other branches (and to make new ones) owned by the ctwm team (as well as the ability to make new ctwm-owned branches). This should be used for long-lived globally-interesting branches, like additional release streams.

Anybody with a LP account, committer or no, can put ctwm-related branches under their own account at locations like lp:~myuser/ctwm/something. This is useful for sharing WIP, or your own personal fork, etc. These will show up as associated with the ctwm project.

3.4. Major branches

All current branches are visible at https://code.launchpad.net/ctwm. The major long-lived branches are currently:

lp:ctwm

This is the development trunk.

lp:ctwm/web

The website.

Note
Since there’s no common data/history between the code and the website, you can put them in separate repositories without losing anything. That’s relatively more advanced workflow stuff than I discuss in this doc though.

4. Getting and working on branches

4.1. Checking out trunk

trunk is the ‘main’ branch, and is somewhat special in that multiple people work on it. As a result, we treat it somewhat specially here by making a checkout of it, rather than a separate branch.

Note
For Committers

It’ll be simpler if you’re logged in to Launchpad before doing this, so that it’s setup to talk to LP over a writable transport. xref hosting section above. It’s not a huge deal to change it around afterward if you forget to do this first, but it’s always simpler not to have to change.

So with all the above initial setup done, we make the checkout.

% cd ~/work/ctwm/bzr
% bzr co lp:ctwm trunk

Afterward, you’ll have a local copy of the trunk in ~/work/ctwm/bzr/trunk. This is what bzr called a ‘heavy’ checkout, which means you do mirror the full history locally, so operations like log don’t need to hit the network.

4.2. Working with trunk

To update your trunk copy, use the bzr update command. In essence, you work with this pretty much like you would with a cvs or svn checkout.

Important
You do not want to use the bzr pull command with a checkout, so don’t do that with this trunk!

For committers, you can commit in that trunk checkout. Because of the way a checkout works, the new revision will be immediately pushed up to the central branch as part of that process, and if it can’t be, the commit will fail. For instance, if your checkout is out of date (new changes have been made upstream), you’ll have to do an update before bzr will let you commit.

Note
Of course, don’t forget to tell me to add you to the bzr team on LP first, so you can write to trunk!
Tip
Project Policy

Whether work is done directly on the trunk branch or in a separate branch is a judgment call. Generally, simple self-contained things can just be done on trunk, as it’s easier. More involved things should be on their own branches. I feel like a nice rule of thumb is that if it’s small enough to comfortably be done in one commit, and safe enough to consider publishable right away, I just do it on trunk. Otherwise, it goes in its own branch.

For stuff that is too big or experimental to just drop into trunk, it should be worked on in separate branches, and then brought into trunk via bzr merge.

4.3. Working with branches

4.3.1. Making a branch to work in

The bzr branch command is used to make new branches based on an existing one.

% cd ~/work/ctwm/bzr
% bzr branch trunk fix-foo

Now you have a new branch (with its own working tree) in ~/work/ctwm/bzr/fix-foo. This branch is purely local; nobody else can see it, stuff you do in it won’t touch the network, etc. So you can work in there on fixing foo, whatever that is. See below for a quick rundown of everyday commands for working within a branch.

4.3.2. Sharing the branch

You can make the changes visible to other people in a large variety of ways; I’ll just skim over the most likely ones.

Put it on Launchpad

With a Launchpad account, you can push it up to your own account’s branch namespace.

you% cd ~/work/ctwm/bzr/fix-foo
you% bzr push lp:~/ctwm/fix-foo
Tip
lp:~/x/y is a shortcut for lp:~yourself/x/y

Now other people can pull down the branch themselves to look at or base new work off of:

someoneelse% cd ~/work/ctwm/bzr
someoneelse% bzr branch lp:~YOURUSER/ctwm/fix-foo
# Now they have the branch in ~/work/ctwm/bzr/fix-foo
someoneelse% bzr branch lp:~YOURUSER/ctwm/fix-foo YOURUSER-fix-foo
# Now they have the branch in ~/work/ctwm/bzr/YOURUSER-fix-foo

When you have additional changes to push up, you can simply run bzr push again, from anywhere in ~/work/ctwm/bzr/fix-foo (you don’t need to specify the location again, unless it changes; bzr remembers where you’re pushing too). And other people can pull down those changes you’ve pushed up via bzr pull.

Put it somewhere else

You can also put bzr branches somewhere else, or self-hosted. While it’s slower and not as capable as a smart server, bzr supports ‘dumb’ transports like http. So if you have a web server available, just putting the branch somewhere web-accessible can let other people grab it from there.

# Is your dev box web-accessible?
% ln -s ~/work/ctwm/bzr/my-branch ~/public_html/ctwm/my-branch
# Other people can run: bzr branch http://yoursystem/~you/ctwm/my-branch
# Is bzr installed on your web server?
% bzr push bzr+ssh://myserver/~/public_html/ctwm/my-branch
# Other people see it just like above
# Is bzr not installed on your web server?
% bzr push sftp://myserver/~/public_html/ctwm/my-branch
# Still same

If the other people grabbing your branch already have e.g. trunk in their local shared repo, grabbing your branch should only need to transfer the additional revs you’ve added, not the whole history. Doing this over dumb http is harder than if they had smart access, but it’s not terrible.

Send bundles via email

bzr also has the ability to create a standalone ‘merge-proposal’, particularly one with an included ‘bundle’. This lets you send a block of changes in a single file, that you can transfer in any number of ways (email, put on a website, carrier pigeon). When doing this, bzr compares your branch to another ‘source’ branch, and puts together the set of revisions needed to bring your changes onto it. It defaults to the parent of your branch (that is, the branch you ran bzr branch from to create it), which is usually the right choice.

% cd ~/work/ctwm/bzr/fix-foo
% bzr send -o/tmp/fix-foo.bundle

Now the file /tmp/fix-foo.bundle contains both a human-readable diff (useful for reviewing the change), and a machine readable "bundle" containing all the revisions. Other people can then use that file by itself as the source for something like bzr merge just as if it were a published copy of your branch.

Note
bzr send has a lot of options, including the ability to spawn your MUA directly. Consult upstream docs for details.
As a vehicle for a single submission

This provides an excellent way for occasional contributors to submit changes, since it doesn’t require any extra hosting or setup. If you’re only ever intending to do a single change to ctwm, this can be used as part of a streamlined process like the following:

% cd ~/work/tmp
% bzr branch lp:ctwm
% cd ctwm
# hack, commit, etc
% bzr send -o /tmp/my.diff
# attach my.diff to an email to the mailing list talking about your
# change and suggesting it be merged.
% rm -rf ~/work/tmp/ctwm
# You don't need it anymore

Of course, we hope you’ll stick around and make multiple improvements, in which case the more involved setup above gives you more flexibility and faster work.

Important
This bit should probably be broken out into its own doc, since its target audience will never see it buried inside this!

4.3.3. Landing your changes into trunk

Once your fix-foo is ready to be released to the world, you’ll want to merge it into trunk. This is done via the bzr merge command.

Important
Remember that bzr merge does not commit the merge; it only prepares it. This is different from how monotone works, as well as git. Don’t forget to bzr ci after you’re done, or you can wind up accidentally intertwining future changes with the merge!
% cd ~/work/ctwm/bzr/trunk
% bzr up
# Just in case: make sure you're up to date
% bzr merge ../fix-foo
# Check over merge, fix any conflicts
% bzr ci

Suggested log messages would be along the lines of

Land fixes that make foo work right with bar.
This doesn't fix foo's interaction with quux, but that's much more
involved so we're not bothering with it right now.

Because of the way commands like bzr log default to hiding away the ‘merged’ revisions, the default output will list only that merge rev and its log message, so make sure it describes well enough the final result of the process. The individual revs that were merged are all present and available if anybody ever needs to drill down into the details.

After this is done, and assuming that completes the process of fixing foo, you can now discard that fix-foo branch if you want.

% rm -rf ~/work/ctwm/bzr/fix-foo

Removing it from anywhere else you’ve put it (pushed to launchpad or your webserver, etc) is left as an exercise for the reader.

5. Day to day commands and operations

There are a lot of useful things I don’t talk about here, but this should easily cover 95% of your needs. See upstream docs for additional goodies.

5.1. Help

bzr help

Has full reference for the various commands, as well as some other topics. | less is often appropriate…

5.2. Dealing with branches

bzr branch

This is how you make a new branch (mirroring an existing branch is just a degenerate case of this; you’re making a new branch you never make local changes to). Standard syntax is bzr branch SOURCE [DEST]; if DEST is left off, the last path component of SOURCE is used.

% bzr branch http://some/branch/some/where
# Branch winds up in 'where'
% bzr branch http://some/branch/some/where localdir
# Branch winds up in 'localdir'
bzr pull

This is a sort of ‘mirror’ command; it brings your branch up to being a fully up-to-date copy of its ‘parent’ (by default, the branch you bzr branched it from).

This only operates if your local branch is ‘out of date’ relative to the upstream. If the branches have ‘diverged’ (e.g, you have committed local changes), pull will refuse to do anything.

bzr push

This lets you push changes to an upstream branch of some sort. Often used to update a publicly-accessible copy of your branch.

Caution
You don’t want to use push or pull with your checkout of trunk (or any other checkout of a branch you have. For checkouts, the commit process automatically publishes the change, and you use update to catch up with upstream changes.
bzr update (bzr up)

This is used in a checkout (as recommended above for trunk), to bring it up to date with the branch it’s a checkout of. It’s sorta an equivalent of pull for checkouts (not really, but a reasonable metaphor). Like in centralized systems like svn, commit will give an error if you’re out of date with the upstream; update is how you catch up.

bzr commit (bzr ci)

Commits a change to the branch. In a checkout, the ‘branch’ is the upstream location, so this automatically publishes it.

You can commit single files or directories by passing them as args to the command. You can specify the commit log on the command line via -m; otherwise bzr will spawn your $EDITOR.

Tip
bzr ci has a --local arg. I suggest you never, ever, ever use it. It’ll probably cause you serious troubles later if you do. You should forget it exists; I only mention it here in case you accidentally come across it yourself…

5.3. Working in your trees

These commands don’t (immediately) update anything in the branch; they just set how things will happen in the next commit.

bzr add

Adds a new file as versioned, so it will wind up in the next bzr ci (and future revs).

bzr rm

Removes a file from version control. It will be removed from the next commit (and future commits). Note that bzr will automatically list as removed any files that don’t exist when you commit, so you can often just use your system rm and let bzr pick up the missing file from there. It’s usually simpler.

Tip
rm has some DWIM magic for what it does with the file in the filesystem. I suggest using bzr rm --keep to make it leave the existing file alone, just remove it from the list of things version controlled. That way you can use bzr rm when you don’t want the file to immediately disappear from your system (as a sort of unversion action), and rm (the system command) when you want it to do both. You can set an alias in bzr (see my aliases below) to make this default.
bzr mv

Renames a file (or directory). Because bzr tracks file identity, this isn’t the same as doing a bzr rm + bzr add, so if you’re intending to rename a file, use bzr mv. It has some provision like --after to tell bzr about the change after you’ve already done it via mv or a GUI file manager or some other mechanism out of bzr’s control. See full docs for details.

bzr revert

This reverts outstanding uncommitted changes. With no args, it wipes out all changes you’ve made since the last commit. By specifying file(s) or dir(s), it will roll back just those changes. Useful for "oh, nevermind" and "wow, I screwed that up bad" situations.

5.4. Viewing information

bzr log

Views the history log. You can specify individual files, groups of files, or directories, to see only the revs that affect those files (though note that this slows things down a lot). Various formats can be chosen like --short or --line; I like using --short day to day. -v makes it show the files affected by each rev.

As talked about above, log by default collapses away merged revisions, showing only the single commit that merges in the other branches. Using the -n0 arg to log you can make it show all those merged details.

bzr diff

Show diffs. With no args, it shows your outstanding uncommitted changes. By providing multiple historical revisions, you can see the differences between them (e.g., bzr diff -r12..15 shows the diffs from rev 12 to rev 15). See below for some details about how to describe revs.

A handy shortcut is the -c arg, which shows the changes made by a single revision relative to its (first) parent. So bzr diff -r5..6 can be written more simply as bzr diff -c6.

bzr status (bzr stat)

Shows a short summary of files that have been added, removed, or changed, plus info about a pending merge. Like diff above, this defaults to outstanding uncommitted changes, but can show between historical revs using -r or -c similarly. e.g., bzr stat -c-1 to show the files changes in the most recent commit (see revision specification below for use of negative numbers, and other ways to refer to revisions).

bzr missing

This is used to compare two branches, to see what revisions each has that the other doesn’t. e.g.,

% cd ~/work/ctwm/bzr/trunk
% bzr missing ../fix-foo
# shows what revs each has that the other lacks

I like using the --line log formatter for this, since it’s a quick enough summary for most purposes.

bzr tags

View the tags and what revs they apply to.

Details of creating tags (hint: bzr tag) aren’t discussed here. But not much different from any other DVCS. Tags show up in the log output as well.

5.5. Maintenance

bzr pack

This (re-)compresses the repository. New commits come in as full texts, and are only delta-compressed when they get repacked. bzr does incremental automatic repacks every so often, so you don’t really ever need to do this manually. But it does cram things more tightly.

bzr check

Runs a full integrity check of your repository, making sure all the stored checksums of things match, etc. Nice for peace of mind.

6. Misc bits

6.1. Revision specification

There are various ways to specify revisions. A full description can be found in bzr help revisionspec or in the online docs. I’ll just skim over a few of the high points.

  • The revision numbers shown in bzr log; positive integers, as well as the dotted-decimal variants for merged revs. Note that these only have meaning in the context of a particular branch.

  • The long ugly revision-id string that you get from something like bzr log --showids (generally looks like an email address, followed by a datestamp and some other numbers). These uniquely identify a rev globally, whatever branch it may be in.

  • Negative integers; these count backward from the head, so -1 is the latest or head revision, -2 is the one before that, etc. Useful for looking at recent history (e.g., bzr diff -c-1 to show the change of the last commit).

  • A branch name represents the current head of that branch.

  • A tag name representing that tag (e.g., ctwm-3.8.1).

Revspecs can be prefixed for disambiguation (e.g., tag:ctwm-3.8.1) when necessary, or will try to guess what you mean. The guesses are usually pretty good, but see the bzr help revisionspec for details.

Commands that can take two revs (like diff and stat, or log to show only a subset of revs) pass them to a single -r arg separated by “..” (two periods). So for instance, to look at the diff or log since the 3.8.1 release, you’d run something like “bzr diff -rctwm-3.8.1..-1”. You can make the range open-ended, since -1 is as far as it can go anyway: “-rctwm-3.8.1..”.

6.2. Branch aliases

There are also some "magic" branch aliases that are occasionally useful, especially with commands like missing. e.g.:

% cd ~/work/ctwm/bzr/trunk
% bzr missing :bound
# This tells you what has happened in trunk that you haven't yet
# gotten; i.e., what running 'update' will fetch down)
% cd ../my_working_branch
% bzr missing :push
# What changes I've made since the last time I 'push'd this branch up
# to a public location

You can also create your own location aliases globally or per-branch via the bookmarks plugin described below.

6.3. Some command aliases I find useful

Via bzr alias, you can specify command aliases built in to bzr. They become commands you use just like any other, including the ability to shadow existing commands. Here are some I use, expressed as the command that will create them.

bzr alias ci="commit --show-diff"

Default to showing the diff in the editor for the commit log. Note that this overrides the default ci alias, which is the same as commit.

bzr alias flog="log --forward --short"

Useful quick log view, especially in construct like “bzr flog -r-10..”.

bzr alias llog="log -v --long -n0"

Shows a more full details log, including the merged revs and files touched in each rev.

bzr alias missing="missing --line"

I only care about a quick summary when I use missing.

bzr alias rm="rm --keep"

This makes bzr rm never touch the files in the filesystem. This lets me unversion a file without getting rid of it (I may want to keep it around for reference), and if I want to actually remove it, I just use rm(1) anyway since bzr notices and respects that.

(And many more. You’ll find things you specify often and want to shortcut; I won’t rob you of the fun of discovering them yourself :)

6.4. Network transparency

Note that among bzr’s capabilities is an abstraction of transports. That means that almost anything you can do against a local branch, you can do against a remote branch as well.

For instance, you can run log remotely.

% bzr branch lp:ctwm /tmp/ctwm
% cd /tmp/ctwm
% bzr log
# Or do it without making a local branch
% bzr log lp:ctwm

You can also merge a branch directly without grabbing it locally first

# Either branch locally first
% cd ~/work/ctwm/bzr
% bzr branch http://some/branch incoming
% cd trunk
% bzr merge ../incoming
# Or go direct
% cd ~/work/ctwm/bzr/trunk
% bzr merge http://some/branch

Be aware, though, that a lot of these things will be much slower. The case of merge above is sensible; you’d be copying the same amount of information either way. And since you’re often not going to do much with someone’s branch except merge it one time, it can be the right thing to just merge direct from the remote location. Another command often useful against a remote branch would be bzr missing.

However, doing a remote log (or diff) is usually a less good idea, since the way the information is accessed remotely is extremely inefficient; it’s going to be very slow, use a lot more bandwidth than you’d expect, and put a heavy load on the server.

6.5. Plugins

Various bzr plugins may be useful. There are a lot more than I mention here, just mentioning a few that may be useful.

6.5.1. bookmarks

You can create your own location aliases globally or per-branch via the bookmarks plugin. e.g., if I want to push a bunch of branches up to LP, I can save typing:

% cd ~/work/ctwm/bzr
# One way
% bzr push lp:~fullermd/ctwm/foo
% bzr push lp:~fullermd/ctwm/bar
% bzr push lp:~fullermd/ctwm/baz
# Or another
% bzr bookmark matt lp:~fullermd/ctwm
% bzr push bm:matt/foo
% bzr push bm:matt/bar
% bzr push bm:matt/baz

6.5.2. qbzr

qbzr provides Qt GUI bits for various commands. The most useful is probably the bzr qlog command, giving a graphical history exploration. When run in a branch it’ll show just that branch, or you can specify multiple branches on the command line, like

% cd ~/work/ctwm/bzr/mybranch
% bzr qlog . ../trunk