1. Preface

This is intended as a quick & dirty summary of using Breezy (https://www.breezy-vcs.org/) to develop ctwm (http://www.ctwm.org/).

That this is a combination of “how to use brz” 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 brz 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 brz 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 brz docs.

https://www.breezy-vcs.org/doc/en/

The main brz 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 brz.

2. Intro and Justifications

The most important difference in day-to-day working with brz 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 brz 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 brz merge.

3. Initial setup

3.1. Configuring brz

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

% brz 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/brz. 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 brz projects, which you don’t want).

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

This will leave you a ~/work/ctwm/brz/.brz directory, which contains the repo. You should never need to directly deal with this again; any new branches you make underneath ~/work/ctwm/brz 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 brz lp-login to tell brz about your launchpad account.

There’s a special lp: directory service in brz, 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/brz
% brz co lp:ctwm trunk

Afterward, you’ll have a local copy of the trunk in ~/work/ctwm/brz/trunk. This is what brz 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 brz 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 brz 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 brz will let you commit.

Note
Of course, don’t forget to tell me to add you to the brz 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 brz merge.

4.3. Working with branches

4.3.1. Making a branch to work in

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

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

Now you have a new branch (with its own working tree) in ~/work/ctwm/brz/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/brz/fix-foo
you% brz 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/brz
someoneelse% brz branch lp:~YOURUSER/ctwm/fix-foo
# Now they have the branch in ~/work/ctwm/brz/fix-foo
someoneelse% brz branch lp:~YOURUSER/ctwm/fix-foo YOURUSER-fix-foo
# Now they have the branch in ~/work/ctwm/brz/YOURUSER-fix-foo

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

Put it somewhere else

You can also put brz branches somewhere else, or self-hosted. While it’s slower and not as capable as a smart server, brz 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/brz/my-branch ~/public_html/ctwm/my-branch
# Other people can run: brz branch http://yoursystem/~you/ctwm/my-branch
# Is brz installed on your web server?
% brz push bzr+ssh://myserver/~/public_html/ctwm/my-branch
# Other people see it just like above
# Is brz not installed on your web server?
% brz 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

brz 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, brz 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 brz branch from to create it), which is usually the right choice.

% cd ~/work/ctwm/brz/fix-foo
% brz 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 brz merge just as if it were a published copy of your branch.

Note
brz 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
% brz branch lp:ctwm
% cd ctwm
# hack, commit, etc
% brz 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 brz merge command.

Important
Remember that brz 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 brz ci after you’re done, or you can wind up accidentally intertwining future changes with the merge!
% cd ~/work/ctwm/brz/trunk
% brz up
# Just in case: make sure you're up to date
% brz merge ../fix-foo
# Check over merge, fix any conflicts
% brz 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 brz 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/brz/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

brz help

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

5.2. Dealing with branches

brz 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 brz branch SOURCE [DEST]; if DEST is left off, the last path component of SOURCE is used.

% brz branch http://some/branch/some/where
# Branch winds up in 'where'
% brz branch http://some/branch/some/where localdir
# Branch winds up in 'localdir'
brz 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 brz 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.

brz 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.
brz update (brz 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.

brz commit (brz 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 brz will spawn your $EDITOR.

Tip
brz 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.

brz add

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

brz rm

Removes a file from version control. It will be removed from the next commit (and future commits). Note that brz 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 brz 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 brz 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 brz 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 brz (see my aliases below) to make this default.
brz mv

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

brz 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

brz 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.

brz 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., brz 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 brz diff -r5..6 can be written more simply as brz diff -c6.

brz status (brz 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., brz 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).

brz missing

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

% cd ~/work/ctwm/brz/trunk
% brz 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.

brz tags

View the tags and what revs they apply to.

Details of creating tags (hint: brz 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

brz pack

This (re-)compresses the repository. New commits come in as full texts, and are only delta-compressed when they get repacked. brz 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.

brz 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 brz help revisionspec or in the online docs. I’ll just skim over a few of the high points.

  • The revision numbers shown in brz 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 brz 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., brz 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 brz 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 “brz 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/brz/trunk
% brz 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
% brz 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 brz alias, you can specify command aliases built in to brz. 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.

brz 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.

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

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

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

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

brz alias missing="missing --line"

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

brz alias rm="rm --keep"

This makes brz 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 brz 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 brz’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.

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

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

# Either branch locally first
% cd ~/work/ctwm/brz
% brz branch http://some/branch incoming
% cd trunk
% brz merge ../incoming
# Or go direct
% cd ~/work/ctwm/brz/trunk
% brz 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 brz 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.