Bazaar and git are both distributed version control systems. Both provide each user with a copy of the code and history that is standalone, allowing them to work independently of anyone else. However, the copies are linked by object and revision identifiers, allowing users working with different copies to safely and accurately merge the changes at a later date. In both systems, the revisions form a directed acyclic graph (DAG) where revisions are connected to their parent revisions.
A difference you probably will get used to quickly is the terminology, but since you’re reading this document, you may be entirely unfamiliar with the Bazaar jargon. Unfortunately, the terms used are the same, but the semantics are often quite different. A quick glossary:
|repository||the fundamental unit of version metadata in git; contains a DAG of commits, refs to commits in the DAG, and either object storage or a pointer to object storage on the local filesystem||contains object storage for one or more branches, and in the case of a shared repository, the branch metadata (commit DAG, etc.) for each branch it contains|
|branch||a ref to a commit that may be checked out – the checked out branch’s head is automatically updated on each commit||the fundamental unit of version metadata in Bazaar; normally has only one accessible head;|
|workspace||also called working tree||as in git, but see checkout for semantics not available in git|
|checkout||not available in git (ie, requires a custom hook)||a workspace which contains no object data, and only head metadata; it points to a branch which may be on the local filesystem or on a remote host – commit updates that branch, giving “centralized” semantics; sometimes called a lightweight checkout to distinguish from a bound branch|
|bound||not available in git||a full branch which is|
|branch||(i.e., requires a custom hook)||locally updated on commit but additionally automatically updates the remote branch it is bound to; sometimes called a heavyweight checkout to distinguish it from a “lightweight” checkout (now deprecated terminology)|
|index||records current status of the workspace, including files staged for the next commit||not available in Bazaar|
|fast||a merge which is||as in git|
|forward||trivial because some branch is a superset of all others|
|conflict||different changes made at the same point in content||as in git, but conflicts can also occur when adding files or directories|
Given these vocabulary terms, we can now discuss correspondences between git and bzr commands.
|init||create a new repository||create a new branch|
|init-repo||redundant in git||create a new shared repository|
|clone||copy the metadata and object data from a repository||alias for branch (UI for cloning a shared repository is under development)|
|branch||create a branch ref||clone a branch, including both metadata and object data|
|commit||records a new revision in the repository and updates the branch head||as in git, but see checkout|
|reset||update index, HEAD, and/or content to a specific commit||partial support by the revert and uncommit commands|
|revert||apply a patch which reverses the effect of a given commit||updates the branch head and/or specific content to specified revision|
|merge||combine the content of two branch head versions; automatically commits unless conflict; works only on local heads (must fetch remote branch before merging: use pull to do both)||as in git, though “octopus” merges aren’t documented; never automatically commits; can directly merge from remote branches|
|push||send version and object data to a remote repository||as in git; unnecessary in checkouts and bound branches|
|fetch||get version and object data from a remote repository||functionality included in pull|
|pull||fetch, then merge; finally commit unless conflict||fetch, then fast forward (cannot do non- trivial merges)|
|update||not available in git||alias for pull from the parent branch|
|status||report on workspace, such as which files are up to date, modified, in merge conflict, or unregistered||same|
|log||report history in various formats||same, but treats merge ancestors specially (commits off the mainline are suppressed unless explicitly requested; because merge doesn’t commit automatically, the merge commit message is expected to accurately describe the history of the merged branch|
The most important design differences are that Bazaar lacks universally unique object identifiers (OIDs) and colocated branches.
Universal OIDs means that in git, an object is identified by its content. Identical objects get identical OIDs, even in completely different repositories. (For example, in git the GPLv2’s OID is 623b6258a134210f0b0ada106fdaab7f0370d9c5, whether it’s Richard Stallman’s original “gold master”, the copy in COPYING in XEmacs, or the copy in /usr/share/common-licenses on a Debian system.) If you copy a file verbatim and give it a different name, it will get the same OID in git. This means that at the level of OIDs, git cannot distinguish between two copies of a file and the same file with two hardlinks in a directory.
In Bazaar, however, a file (or a directory!) is thought of as a container for content, and thus to be different from its content. A file’s OID depends on when and where it is created (in practice as well as principle). Thus Bazaar is prepared by design for exactly tracking copies, renames, and creation of empty containers, whether files or directories. git uses heuristics for tracking changes of name, thinks of such tracking as “expensive” so that it’s only done when explicitly requested, and has no way to add an empty directory to its index. This difference is a very fine point, and since name tracking is not done by default in git, git users probably will rarely notice this difference. However, in some special cases semantics of some commands differ. For example, one author of this page once refactored a .c file into a .h and a .c, but managed to switch the names on the pieces! A simple name swap with mv, and everything was peachy. git status, git log, and git diff report this as a pair of renames. However, bzr would not notice this, and would deliver a full-file diff of each file.
Such inconveniences are rare. On the other hand, many git users will notice improved functionality from rename and copy tracking by default.
The more obvious difference is the lack of colocated branches. Colocated branches means that a DAG may have multiple active heads at them same time, and all are conveniently accessible in a single workspace. In git, the only resources needed to create a branch are a name for the branch and the SHA1 of the new head. Branches are extremely cheap in git. This means that most workflows implemented in git are based on branching. For example, suppose you want to temporarily “shelve” the local modifications to work on a quick fix. Then create (using “checkout -b”) a branch named shelf, commit those changes there, and checkout the “quickfix” branch. It also means that git users can be quite casual about a local branch (one that has not been fetched to any other repository). This is one important reason why rebasing can be so popular among git users. Commits and branches are very cheap, and it isn’t “history” until you’ve told the story to someone else, so in git it’s reasonable to use the VCS as a very high-level editor.
Each branch is conceptually present in all repositories, since its tip commit is completely identified by its SHA1, a universal OID. The questions are (1) do you have the branch’s content in your repository? (if not, of course you can’t check it out), and (2) do you know its branch name? (if not, it’s quite inconvenient to talk about it). So in git, repositories are fully decentralized and distributed by design. (However, for performance reasons the only concession git makes to objects not located on the local filesystem is to allow “shallow” clones, ie, a repository some of whose ancestry and content is visible as OIDs, but not accessible in the workspace. The functionality of shallow clones is currently severely limited.)
But in Bazaar the fundamental conceptual unit is the branch. What is called a repository in Bazaar holds the storage for revisions of at least one branch. In the original repository = branch = workspace format, the repository was implicit. In the case of a shared repository, it holds the revisions for several branches (not necessarily from the same project), and is implemented as the parent directory of the branches it contains. A particular repository can be shared between branches, but most commands operate at the branch level, and each branch has only one repository. In fact, each branch is a location on the filesystem, so switching branches in Bazaar involves a ‘cd’ to move yourself to the desired branch, rather than making the branch come to you as in git. In practice your main workflow is not so different. Although the real name of a branch is a fully-qualified URL, Bazaar allows branches to have nicknames, and provides commands to switch branches for you. Using these feels a lot like git.
As long as you already have the branch set up, this is as fast as a git checkout. It is commonly believed that Bazaar is slow, but by version 2.0 many performance defects have been fixed, and the underlying transports are now as fast as git. The choice of tradeoff is different; in git you download a whole family of related branches at once by cloning a whole repository, while in Bazaar many workflows download only one branch at a time, which makes first access to a repository faster, but first access to a branch slower. The Bazaar design is more flexible, and a UI for cloning a whole repository is available.
Where things differ from git is that quick cheap branches at arbitrary locations in the commit DAG are not available. Instead of using a single tool (branch reset) to manipulate the DAG and workspace, a variety of primitive commands are implemented (either in the core or as plugins). For example, a “shelve” command recently became standard. git users will also likely be disconcerted by the difficulty of rebasing, and by the very disapproving attitude of many Bazaar users toward the practice. Commits are “heavier”, and branches are very “heavy” in Bazaar. To get “lightweight” commits and branches, use pipelines or looms (features available as plugins).
There is no index visible at the UI layer in Bazaar. There is no facility for partially staging a commit as there is in git. You can still commit a subset of files, and it is possible to commit a subset of hunks within a file using plugins, but there is no way to stage part of a commit and then continue working.
There are two differences with merging. Firstly ‘bzr pull’ will only do a fast-forward. If a merge is required it will stop. ‘git pull’ which will make a merge commit as required. If you desire this behaviour you can run ‘bzr merge –pull’ which will fast-forward if possible.
The second differences with merges is that Bazaar requires a commit to be done after every (no fast-forward) merge, regardless of whether there were conflicts. git on the other hand will automatically commit if there are no conflicts.
The main branch (also called “left” branch) is presented as special in ‘bzr log’, with parents on other branches (and their ancestors) being shown indented to indicate that they were merged in. When working on a project with an identified mainline this will show the perceived flow of changes in the mainline branch. With the commit on every merge policy this means that the flow of changes on this left hand parent history summarises development on other branches that were merged in. These distinction in the log output is only at the UI layer, the underlying model makes no fundamental distinction between parents.
Objects in Bazaar are not SHA addressed. Revisions use revision-ids, and files use file-ids. This means that you get rename tracking built in when Bazaar is informed what happened. However when the identical patch is merged independently on the same base revision by two separate commits, the resulting states are not considered identical, but will merge cleanly (if no files were added – an add/add conflict will occur even if the content is identical).
Bazaar has a help command with extensive help on the various subcommands, much like git:
$ bzr help $ bzr help init
These are available in various formats, but not in conventional man(1) format by default.
To create a standalone branch looks very similar to git:
$ bzr init myproject
However, to create the recommended Bazaar repository/branch structure is slightly more complex:
$ bzr init-repo myproject $ cd myproject $ bzr init trunk
Creating a copy of a branch (whether a clone of a remote branch, or a branch of a local one) is similar to git, but using the branch command rather than clone (for convenience “clone” is aliased to “branch”):
$ cd .. # operate on branches from the shared repository $ bzr branch trunk feature-a
To get changes from another branch, Bazaar uses the pull command in much the same way as git:
$ bzr pull upstream
As with git, the default is to pull from “the place you branched from.”
Unlike git, however, it is not possible to pull from a branch with divergent changes (a situation which would create multiple heads in git). Rather, the pull fails and it is necessary to use the merge command:
$ bzr merge upstream $ bzr conflicts [ shows conflicts ] $ emacs a $ bzr resolve a [ marks the conflict in a as resolved ] $ bzr commit -m "Merged from upstream"
In Bazaar, the recommended practice is this:
1. Keep trunk as a mirror of upstream and keep it up to date using pull. 2. Develop changes in a feature branch. To keep a feature branch up to date:: $ bzr merge ../trunk [ resolve conflicts ] $ bzr commit -m "Merge trunk rev xyz" 3. When the feature branch is ready to land, merge it back into trunk:: $ cd ../trunk $ bzr merge ../feature-a [ resolve conflicts ] [ run tests ] $ bzr commit -m "Land feature-x"
It is often convenient to bind your trunk to the upstream branch, so that the commit will implicitly propagate the change upstream. This save you the inconvenience of remembering to push it.
Checking how the working tree has been changed uses the bzr status and bzr diff commands, just like git:
$ bzr status $ bzr diff
To commit your work to the local repository, you use the commit command, just as in git:
$ bzr commit
Sending changes upstream uses the push (or in some workflows, upstream will pull your feature branch), again just as in git.
Bazaar offers more protocols than git:
$ bzr help urlspec
Supported URL prefixes:
aftp:// Access using active FTP. bzr:// Fast access using the Bazaar smart server. bzr+ssh:// Fast access using the Bazaar smart server over SSH. file:// Access using the standard filesystem (default) ftp:// Access using passive FTP. http:// Read-only access of branches exported on the web. https:// Read-only access of branches exported on the web using SSL. sftp:// Access using SFTP (most SSH servers provide SFTP).
Like git, there is no read/write HTTP protocol built in. Rather, Bazaar has a built-in Bazaar “smart server” which serves data using the custom “bzr” protocol.
For read/write access over HTTP in Bazaar, try the webdav plugin.
Bazaar also supports (through plugins) “special” URL schemes. A typical example is “lp:”, which assumes the remote repository is hosted on Canonical’s Launchpad system, and provides some additional Launchpad integration.
Bazaar identifies revisions using a unique “revision ID”, where git uses commit hashes. Bazaar allows use of a local revision number which is computed relative to the root commit (“version 1”) and increasing in the date of commit, where git’s “revision numbers” are actually “revision expressions” relative to any convenient commit (typically a branch head), and increasing numbers means older commits. Bazaar has a rich language of revision specifiers (the most significant of which is date-based, which in git is expressed by special options that many commands will pass to git-rev-parse). Full details can be obtained using:
$ bzr help revisionspec
This is illustrated in our graphical log viewer (qlog) below.
These differences can be both a positive and a negative:
The solution is simple: do not push from a feature branch to upstream. As explained earlier, the right process in Bazaar is to keep a trunk branch that mirrors upstream and merge your feature branch into that instead.
For more information on organizing branches and how Bazaar assigns revision numbers, see Bazaar Zen.
Bazaar supports the well-known “fastimport” format for both input and output. This is work in progress, but quite satisfactory results are being achieved in importing foreign format repositories (mostly dependent on the quality of the existing repository, e.g. CVS is always messy).
Bazaar supports more “foreign repository” formats than git does, and many Bazaar users strongly prefer use of Bazaar to the native UI for Subversion, Mercurial, and git repositories. (At present only bzr-svn supports writing to a foreign repository, but this support is quite complete including externals and properties.)
Bazaar has a rich set of “plugins” available. Bazaar plugins offer features similar to Stacked Git (loom and pipelines) as well as direct interfaces to foreign version control systems (for example, bzr-svn). Bazaar is not as pleasant to script in shell languages as git; it is generally preferable to access the public APIs via Python scripts.
Bazaar has a number of commands and concepts designed specifically to support particular workflows. For example, “bound branches” and “checkouts” support certain aspects of a centralised workflow. git has nothing comparable - workflow is defined by local policy in git, rather than by explicit tool support.
The default display of revisions in bzr log is designed to emphasize the mainline flow of changes. Specifically, only the “top level” of revisions is shown by default, so that a branch, plus subsequent changes, plus a final merge, is shown simply as the final merge.
This is in direct contrast to git, where all revisions are shown, with the merged revisions treated as “most recent commits all at once”, and merges are frequently committed with a simple message such as “Merge from xxx”. Such merge messages are uninformative, even confusing, in Bazaar where the detail/context is hidden by default.
In Bazaar, it’s important to make your final merge message something meaningful on its own, e.g.:
“Land new feature X, fixing bug Y while we’re at it.”
While hidden by default, it’s easy to see merged revisions:
- In the GUI, click on the “+” symbols in the revision graph.
- On the command line, use either bzr log –include-merges or bzr log -n0. (The -n option lets you specify a depth and a depth of 0 means all levels.)
If you want revisions always shown by default on the command line, add this to your bazaar.conf file:
[ALIASES] log=log --include-merges