(For more resources related to this topic, see here.)
The centralized mode
In the centralized mode, multiple users have write access to one or more branches on a central server. In addition, this mode requires that all commit operations be applied to the central branches directly. This is in contrast with the default behavior of Bazaar, where all commits are local only, and thus private by default.
In order to prevent multiple users from overwriting each other's changes, commits must be synchronized and performed in lock-step—if two collaborators try to commit at the same time, only the first commit will succeed. The second collaborator has to synchronize first with the central server, merging in the changes done by others, and try to commit again. In short, a commit operation can only succeed if the server and the user are on the same revision right before the commit.
First, we will learn about the core operations, advantages, and disadvantages of the centralized mode in a general context. In the next section, we will learn in detail how the centralized mode works in Bazaar.
The core operations in centralized mode are checkout, update, and commit:
- Checkout : This operation creates a working tree by downloading the project's files from a central server. This is similar to the branch operation in Bazaar.
- Update : This operation updates the working tree to synchronize with the central server, downloading any changes committed to the server by others since the last update. This is similar to the pull operation in Bazaar.
- Commit : This operation records the pending changes in the working tree as a new revision on the central server. This is different from the commit , because in the centralized mode, the commit must be performed on the central server.
Bazaar supports all these core operations, and it provides additional operations to switch between centralized and decentralized modes, such as bind, unbind, and the notion of local commits, which we will explain later.
The centralized workflow
Since the centralized mode requires that all the commits be performed on the central server, it naturally enforces a centralized workflow. After getting the project's files using the checkout operation, the workflow is essentially a cycle of update and commit operations:
- Do a "checkout" to get the project's files.
- Work on the files and make some changes.
- Before committing, update the project to get the changes committed by others in the meantime.
- Commit the changes and return to step 2.
Checkout from the central branch
Given the central repository with its branches, the first step for a collaborator is to get the latest version of the project. Typically, you only need to do this once in the lifetime of the project. Later on, you can use the update operation to get the changes that were committed by the other collaborators on the server:
As a result of the checkout, collaborators have their own private copy of the project to work on.
Collaborators make changes independently in their own working trees, possibly working on copies of the same files simultaneously. Their environments are independent of each other and of the server too. Their changes are local and typically private until they commit them to the repository:
Commit operations are atomic—they cannot be interrupted or performed simultaneously in parallel. Therefore, collaborators can only commit new revisions one by one, not at the same time:
If two collaborators try to commit at the same time as in this example, only the first one will succeed. The second one will fail because his copy of the project will be out of date as compared to the server, where another revision has been added by the other collaborator. At this point, the second collaborator will have to update his working tree to bring it to the latest revision, downloading the revision added by the other user who succeeded to commit first.
Updating from the server
The update operation brings the working tree up-to-date by copying any revisions that have been added on the server since the last update or checkout. If there are uncommitted changes in the working tree, they will be merged on top of the incoming changes:
After the update, the local branch will be on the same revision as the server, and now the user may commit the pending changes:
Handling conflicts during update
When there are pending changes in the working tree, the update operation will try to rebase those changes on top of the incoming revisions. That is, the working tree is first synchronized with the server to be on the same revision, and after that the pending changes are applied on top of the updated working tree.
Similar to a merge operation, if the pending changes conflict with the incoming changes, the conflicts must be resolved manually. Since there is no systematic way to return to the same original pending state, the update operation can be dangerous in this situation. The more pending changes and the more time has elapsed since the last update or checkout, the greater the risk of conflicts.
The centralized mode has several useful properties that are worth considering.
Easy to understand
The concept of a central server, where all the changes are integrated and the work of all collaborators is kept synchronized, is simple and easy to understand. In projects using the centralized mode, the central server is an explicit and unambiguous reference point.
Easy to synchronize efforts
Since all the commits of the collaborators are performed on the central server in lock-step, the independent local working trees cannot diverge too far from each other; it's as if they are always at most one revision away from the central branch. In this way, the centralized mode helps the collaborators to stay synchronized.
The centralized mode has a long-standing history. It is widely used today in many projects, and it is often preferred in corporate environments.
The centralized mode has several drawbacks that are important to keep in mind.
Single point of failure
Any central server is, by definition, a potential single point of failure. Since in the centralized mode all commits must go through the central server, if it crashes or becomes unavailable, it can slow down, hinder, or in the worst case completely block further collaboration.
Administrative overhead of access control
When multiple users have write access to a branch, it raises questions and issues about access control, server configuration, and maintenance:
- Who should have write access? An access control policy must be defined and maintained.
- How to implement write access of multiple users on the central branches? The central server must be configured appropriately to enforce the access control policy.
- Whenever a collaborator joins or leaves the project, the server configuration must be updated to accommodate changes in the team.
- Whenever the access policy changes, the server configuration must be updated accordingly.
The update operation is not safe
The centralized mode heavily relies on an inherently unsafe operation—updating the working tree from the server while it has pending changes. Since the pending changes are, by definition, not recorded anywhere, there is no systematic way to return to the original state after performing the update operation.
Unrelated changes interleaved in the revision history
When collaborators work on different topics in parallel, if they continuously commit their changes, then unrelated changes will be interleaved in the revision history. As a result, the revision history can become difficult to read, and if a feature needs to be rolled back later, the revisions that were a part of the feature can be difficult to find.
Using Bazaar in centralized mode
Bazaar fully supports the core operations of the centralized mode by using so-called bound branches. The checkout and update operations are implemented using dedicated commands in the context of bound branches. The commit operation works differently when used with bound branches, in order to enforce the requirements of the centralized mode.
In addition to the classic core operations of the centralized mode, Bazaar provides additional operations to easily turn the centralized mode on or off, which opens interesting new ways of combining centralized and decentralized elements in a workflow.
Bound branches are internally the same as regular branches; they differ only in a few configuration values—the bound flag is set to true, and bound_location is set to the URL of another branch. We will refer to the bound location as the master branch .
In most respects, a bound branch behaves just like any regular branch. However, operations that add revisions to a bound branch behave differently—all the revisions are first added in the master branch, and only if that succeeds, the operation is applied to the bound branch.
For example, the commit operation succeeds only if it can be applied to the master branch. Similarly, the push and pull operations on a bound branch will attempt to push and pull the missing revisions in the master branch first.
Since being bound to another branch is simply a matter of configuration, branches can be reconfigured at any time to be bound or unbound.
Creating a checkout
The checkout operation creates a bound branch with a working tree. This configuration is called a checkout in Bazaar. This is essentially the same as creating a regular branch and then binding it to the source branch it was created from. The term checkout is also used as a verb to indicate the act of creating a checkout from another branch.
Using the command line
Let's first create a shared repository to store our sample branches:
$ mkdir -p /sandbox $ bzr init-repository /sandbox/central Shared repository with trees (format: 2a) Location: shared repository: /sandbox/central $ cd /sandbox/central
You can check out from another branch by using the bzr checkout command and by specifying the URL of the source branch. Optionally, you can specify the target directory where you want to create the new checkout. For example:
$ bzr checkout
You can confirm that the branch configuration is a checkout by using the bzr info command:
$ bzr info trunk Repository checkout (format: 2a) Location: repository checkout root: trunk checkout of branch:
http://bazaar.launchpad.net/~bzrbook/bzrbook-examples/hello-start/ shared repository: .
The first line of the output is the branch configuration, in this case a "Repository checkout", because we created the checkout inside a shared repository. Outside a shared repository, the configuration is called simply "Checkout". For example:
$ bzr checkout trunk /tmp/checkout-tmp $ cd /tmp/checkout-tmp/ $ bzr info Checkout (format: 2a) Location: checkout root: . checkout of branch: /sandbox/central/trunk
In both the cases the checkout of branch line indicates the master branch that this one is bound to.
Using Bazaar Explorer
Performing a checkout using Bazaar Explorer can be a bit confusing, because the buttons and menu options labeled Checkout... use a special mode of the checkout operation called "lightweight checkouts". Lightweight checkouts are very different from branches..
Use the Branch view to checkout from a branch:
- From the toolbar, click on the large Start button and select Branch...
- From the menu, select Bazaar | Start | Initialize
In the From: textbox, enter the URL of the source branch. In the To: textbox, you can either type the path to the directory where you want to create the checkout, or click on the Browse button and navigate to it. Make sure to select the Bind new branch to parent location box, in order to make the new branch bound to the source branch:
After you click on OK , the Status box will show the bzr command that was executed and its output. For example:
Run command: bzr branch
/sandbox/central/trunk2 --bind --use-existing-dir Branched 6 revisions. New branch bound to
Click on Close to return to the status view, which shows the content of the working tree exactly in the same way as in the case of regular branches.
The Status view does not indicate whether the branch of the current working tree is bound or not. On the other hand, the repository view uses different icons to distinguish these configurations:
Bound branches are shown with a computer icon, and unbound branches are shown with a folder icon.
Updating a checkout
The purpose of the update operation is to bring a bound branch up-to-date with its master branch. If there are pending changes in the working tree, they will be reapplied after the branch is updated. If the incoming changes conflict with the pending changes in the working tree, the operation may result in conflicts.
As collaborators work independently in parallel, it is very common and normal that a bound branch is out of date due to the commits done by other collaborators. In such a state, the commit operation would fail, and the bound branch must be updated first before retrying to commit.
Similar to a pull operation, the update operation copies the missing revision data to the repository and updates the branch data to be the same as the master branch.
If there are pending changes in the working tree at the time of performing the update, they are first set aside and reapplied at the end. During this step conflicts may happen, the same way as during a merge operation.
Using the command line
You can bring a bound branch up-to-date with its master branch by using the bzr update command. To demonstrate this, let's first create another checkout based upon an older revision:
$ cd /sandbox/central $ bzr checkout trunk -rlast:3 last-3 $ cd last-3 $ bzr missing --line ../trunk You are missing 2 revisions: 6: Janos Gyerik 2013-03-03 updated readme 5: Janos Gyerik 2013-03-03 added python and bash impl
That is, our new checkout is two revisions behind the trunk. Let's bring it up to date:
$ bzr update +N hello.py +N hello.sh M README.md All changes applied successfully. Updated to revision 6 of branch /sandbox/central/trunk
The missing revisions are added to the branch, and the necessary changes are applied to the working tree, resulting in identical branches:
$ bzr missing ../trunk Branches are up to date.
Using Bazaar Explorer
To bring a checkout up-to-date with its master, you can either click on the large Update button in the toolbar, or navigate to Bazaar | Collaborate | Update Working Tree.... in the menu.
The user interface does not take any parameters; the operation is applied immediately and its result is shown similar to the command-line interface.
Visiting an older revision
An interesting alternative use of the update operation is to reset the working tree to a past state, by specifying a revision by using the -r or --revision options. For example:
$ cd /sandbox/central/trunk $ bzr update -r3 -D .bzrignore M README.md -D hello.py -D hello.sh All changes applied successfully. Updated to revision 3 of branch
This may seem similar to using bzr revert, but in fact it is very different. The changes applied to the working tree will not be considered pending changes. Instead, the working tree is marked as out of date with its master, effectively preventing commit operations in this state:
$ bzr status working tree is out of date, run 'bzr update'
Another difference from the revert command is that we cannot specify a subset of files; the update command is applied to the entire working tree.
This operation works on unbound branches too. Since an unbound branch can be thought of as being its own master, the update command without a revision parameter simply restores it to its latest revision.
Committing a new revision
The commit operation works in the same way as it does with unbound branches, however, in keeping with the main principles of the centralized mode, Bazaar must ensure that the commit is performed in two branches—first in the master branch, followed by the bound branch.
The commit operation in the master branch succeeds only if it is at the same revision as the bound branch. Otherwise, the operation fails, and the bound branch must first be synchronized with its master branch using the update operation.
In Bazaar Explorer, the Commit view shows an additional explanation when committing in a bound branch, as a kind reminder that the operation will be performed on the master branch first, keeping the local and master branches in sync:
Practical tips when working in centralized mode
The centralized mode is simple and easy to work with in general, except for the update operation. The update operation can be problematic when there are too many pending changes in the working tree, and the central branch has evolved too far since the last time the bound branch was synchronized.
Fortunately, a few simple practices can greatly reduce or mitigate the potential conflicts that may arise during update operations:
- Always perform an update before starting to work on something new. That is, make sure to start a new development based on the latest version of the central branch.
- Break down bigger changes into smaller steps and commit them little by little. Don't let too many pending changes to accumulate locally; try to commit your work as soon as possible.
- In case of large scale changes and whenever it makes sense, use dedicated feature branches. You can work on feature branches locally or share them with others by pushing to the central server.
Working with bound branches
Bazaar provides additional operations using bound branches that go beyond the core principles of the centralized mode, such as:
- Unbinding from the master branch
- Binding to a branch
- Local commits
Essentially, these operations provide different ways to switch in and out of the centralized mode, which is extremely useful when a central branch becomes temporarily unavailable, or if you want to rearrange the branches in your workflow.
Unbinding from the master branch
Sometimes, you may want to commit changes even if the master branch is not accessible. For example, when the server hosting the master branch is experiencing network problems, or if you are in an environment with no network access such as in a coffee shop or in a train.
You can unbind from the master branch by using the bzr unbind command. To unbind a branch using Bazaar Explorer, you can either click on the large Work icon in the toolbar and select Unbind Branch , or using the menu Bazaar | Work | Unbind Branch .
Internally, this operation simply sets the bound configuration value to false. Since the branch is no longer considered bound, subsequent commit operations will be performed only locally, and the branch will behave as any other regular branch.
You can confirm that a branch was unbound from its master by using the bzr info command. For example:
$ cd /sandbox/central/ $ bzr checkout trunk mycheckout $ cd mycheckout/ $ bzr info Repository checkout (format: 2a) Location: repository checkout root: . checkout of branch: /sandbox/central/trunk shared repository: /sandbox/central $ bzr unbind $ bzr info Repository tree (format: 2a) Location: shared repository: /sandbox/central repository branch: .
That is, the configuration has changed from Repository checkout to Repository tree and the checkout of branch line disappeared from the output.
Binding to a branch
Sometimes, you may want to bind a regular independent branch to another branch, for example to switch to using the centralized mode, or if you previously unbound from a branch and want to bind to it again.
You can bind to a branch by using the bzr bind command and specifying the URL of the branch. To bind a branch using Bazaar Explorer, you can either click on the large Work icon in the toolbar and select Bind Branch... , or use the menu Bazaar | Work | Bind Branch... . If you have previously used unbind in this branch, then you can omit the URL parameter on the command line, and in Bazaar Explorer the previous location is selected by default.
Internally, this operation simply updates the branch configuration—sets or updates the value of bound_location and sets the value of bound to True. Since the branch is now considered bound, all commit operations will be first applied to the master branch, but the working tree is left unchanged at this point.
Although you can bind any branch to any other branch, it only makes sense to bind to a related branch, typically a branch that is some revisions ahead of the current branch, so that a normal pull operation would bring the local branch up-to-date with its master branch.
After binding to a branch, you should bring the local branch up-to-date with its master branch by using bzr update. Ideally, if the local branch is related to its new master and is just some revisions behind, then the update operation will simply bring it up-to-date by copying the revision data and the branch data of the master, leaving the working tree in a clean state, ready to work in the branch.
However, if the two branches have diverged from each other, then the update operation will perform a merge—first the working tree is updated to match the latest revision in the master branch, after that the revisions that do not exist in the master branch are merged in the same way as in a regular merge operation. This is an unusual use case, but nonetheless a valid operation. After all the changes are applied, you must sort out all conflicts, if any, and you may commit the merge. Since the branch is now a bound branch, the merge commit will be first applied in the master branch, and after that in the bound branch.
Using local commits
If you want to break out of the centralized mode only temporarily, an alternative to unbinding and rebinding later is using so-called local commits. When using local commits, you basically stay in centralized mode, but instead of trying to commit in the master branch, the commit operation is applied only in the local branch. This can be very useful when the master branch is temporarily unavailable but expected to be restored soon.
You can perform a local commit by using the bzr commit command with the --local flag, or in Bazaar Explorer by selecting the Local commit box in the Commit view:
You can continue to perform as many local commits as needed until the master branch becomes available again.
As a result of local commits, the bound branch and the master branch go out of sync. If you try to perform a regular commit in such a state, Bazaar will raise an error and tell you to either continue committing locally, or perform an update and then commit.
$ bzr commit -m 'removed readme' bzr: ERROR: Bound branch BzrBranch7(file:///sandbox/central/on-the-train/)
is out of date with master branch BzrBranch7(file:///sandbox/central/trunk/). To commit to master branch, run update and then commit. You can also pass --local to commit to continue working disconnected.
It may seem strange at first that we have to do an update even though in this case our local branch is clearly ahead of its master. However, the behavior is consistent with the rule – if a bound branch is not in sync with its master branch, you must always use the update operation to synchronize it.
As usual, the update operation will first restore the working tree to the same state as the latest revision in the master branch. After that, it will perform a merge from the tip of the local branch, applying the changes in the revisions that were committed locally. Finally, it will apply the pending changes that existed at the moment the update operation started. As a result, the working tree will be in a pending merge state, as you can confirm by using the log and status commands. For example:
After sorting out all conflicts, if any, you may commit the merge. The local commits will appear as if they had been on a branch and the branch has been merged. This makes perfect sense, as indeed this is exactly what happened:
If no new revisions were added in the master branch during your local commits, then a simple way to bring the master up-to-date is to do a bzr push operation instead of bzr update. It works because in this case the two branches have not diverged; the local branch is simply a few revisions ahead of its master. The push operation appends the missing revisions to the master branch, and the two branches become synchronized again, and you can continue to work and commit normally.
Working with multiple branches
Branch operations work consistently, regardless of whether you use the centralized mode or not.
Although the centralized mode permits multiple collaborators committing unrelated changes continuously in the central branch, it is better to work on new improvements in dedicated feature branches and merge them into the central branch only when they are ready. In this way, the revision history remains easy to read, and if a feature causes problems, then all the revisions involved in it can be reverted easily with one swift move.
Even in a centralized workflow, you are free to use as many local private branches as needed. You can slice and dice your local branches and when a feature is ready, you can merge them into the central branch, and all the intermediate revisions will be preserved in the history.
Team members can work on a feature branch together by sharing the branch on the central server. One of the team members can start working on the feature, and at some point push the branch on the server so that others can checkout from it and start contributing their work. After pushing the branch to the server, the original contributor can switch to the centralized mode using the bind command.
When working on a bound branch, keep in mind that in addition to the commit operation, the push and pull operations too will (at least least try to) impact its master branch.
Setting up a central server
In order to use Bazaar in the centralized mode, collaborators need to have write access to the branches on a central server. Here, we explain a few ways of configuring such servers.
Using an SSH server
An easy and secure way to provide write access to branches at a central location is by using an SSH server. In this setup, users authenticate via the SSH service running on the server, and their read and write access permissions to the branches are subject to regular filesystem permissions.
There are several ways of accessing Bazaar branches over SSH:
- Users access the server with their own SSH account
- Users access the branches with a shared restricted SSH account
- Users access the server with their own SSH account over SFTP
Using the smart server over SSH
If Bazaar is installed on the server, remote clients can benefit from the built-in smart server when accessing branches by using the bzr+ssh:// protocol. In this mode, the bzr serve command is invoked on the server side to handle incoming Bazaar commands. This mode is called smart server , because remote clients receive assistance from the server, significantly speeding up Bazaar operations.
In addition to Bazaar being installed on the server, the bzr command must be in a directory included on the user's PATH variable. Otherwise, the absolute path of bzr must be specified at the client side, either in the BZR_REMOTE_PATH environment variable or in Bazaar's user configuration. For example, if bzr is installed in /usr/local/bin/bzr, then you can execute Bazaar commands on the remote location as follows:
$ export BZR_REMOTE_PATH=/usr/local/bin/bzr $ bzr info bzr+ssh://email@example.com/repos/projectx
Alternatively, the remote path can be specified in the locations.conf file in your Bazaar configuration directory as follows:
[bzr+ssh://example.com/repos/projectx] bzr_remote_path = /usr/local/bin/bzr
See bzr help configuration for more details.
Use the bzr version command to the find the location of the Bazaar configuration directory.
Using individual SSH accounts
This is the easiest way to access Bazaar repositories on a remote computer. Users with shell access to a computer can access Bazaar branches by using the bzr+ssh:// protocol. For example:
$ bzr info bzr+ssh://firstname.lastname@example.org/repos/projectx
The path component in the URL must be the absolute path of the branch on the server; in this example, the branch is in /repos/projectx. If the branch is in the user's home directory, then the home directory part can be replaced with ~; for example, instead of /home/jack/repos/projectx, you can use the more simple form ~/repos/projectx:
$ bzr info bzr+ssh://email@example.com/~/repos/projectx
To refer to a Bazaar branch in another user's home directory, you can use the ~username shortcut. For example:
$ bzr log bzr+ssh://firstname.lastname@example.org/~mike/repos/projectx
In order to let multiple users commit to the same branches, their user accounts must have write permission to the branch and repository files used by Bazaar. One way to do that is by adding the users to a dedicated group, and setting the ownership and access permissions appropriately. Let's call this group bzrgroup, and let's set up a shared repository at /srv/repos/projectx for members of the group, as follows:
$ bzr init-repository /srv/repos/projectx --no-trees Shared repository (format: 2a) Location: shared repository: /srv/repos/projectx $ chgrp -R bzrgroup /src/repos/projectx $ chmod g+s /src/repos/projectx
With this setup, the members of bzrgroup can create branches and commit to them. With appropriate permissions, other users can be permitted strictly the read-only access.
Using a shared restricted SSH account
Instead of creating individual SSH accounts for each collaborator, an interesting alternative is to use a shared SSH account with command restrictions.
This setup requires that collaborators use the SSH public key authentication when connecting to the server, and that appropriate access permissions to the branches be configured in the ~/.ssh/authorized_keys file of the shared SSH account.
Let's suppose that:
- There is a shared repository on the server in /srv/bzr/projectx
- You want to give Jack and Mike write access to the shared repository
- The shared repository is owned by the user bzruser
To make this work, add the following two lines to the ~/.ssh/authorized_keys file of bzruser:
command="bzr serve --inet --allow-writes --
command="bzr serve --inet --allow-writes --directory=/srv/bzr/projectx",
Replace PUBKEY_OF_JACK and PUBKEY_OF_MIKE with the SSH public key of Jack and Mike, respectively. For example, an SSH public key looks similar to the following:
The command parameter restricts the login shell to the bzr serve command. In this way, the users will not be able to do anything else on the server except run Bazaar commands. The --directory parameter further restricts Bazaar operations to the specified directory. To give only read-only access, simply drop the --allow-writes flag.
The other options on the line after command are to make the SSH sessions as restricted as possible, as a good measure of security.
When accessing branches in this setup, the path component in the branch URL must be relative to the directory specified in the authorization line. For example, the trunk in /srv/bzr/projectx/trunk can be accessed as follows:
$ bzr info bzr+ssh://email@example.com/trunk
The drawback of this setup is that you can only have one configuration line per SSH key.
If SFTP is enabled on the SSH server, you can access branches without installing Bazaar on the server by using the sftp:// URL prefix instead of bzr+ssh://. For example:
$ bzr info sftp://firstname.lastname@example.org/home/mike/repos/projectx
This type of access is called "dumb server" mode, because in this case Bazaar is not used on the server side, and thus it cannot provide assistance to the client. In this setup, operations will be much less efficient compared to using the smart server.
Using bzr serve directly
You can use the Bazaar smart server directly to listen to incoming connections and serve the branch data.
Use the bzr serve command to start the smart server. By default, it listens on port 4155, and serves branch data from the current working directory in read-only mode. It has several command-line parameters and flags to change the default behavior. For example:
- --directory DIR: This specifies the base directory to serve the branch data from, instead of the current working directory
- --port PORT: This specifies the port number to listen on, instead of the default 4155 port
- --allow-writes: This allows write operations instead of strictly read-only
Use the -h or --help flags to see the list of supported command-line parameters.
Branches served in this way can be accessed by URLs in the following format:
Here, host is the hostname of the server, and path is the relative path from the base directory of the server process.
For example, if the server is example.com, the smart server is running in the directory /srv/bzr/repo, and there is a Bazaar branch at the path /srv/bzr/repo/projectx/feature-123, then the branch can be accessed as follows:
$ bzr info bzr://example.com/projectx/feature-123
The advantage of this setup is that the smart server provides good performance. On the other hand, it completely lacks authentication.
Using bzr serve over inetd
On GNU/Linux and UNIX systems, you can configure inetd to start the bzr serve command automatically as needed, by adding a line in the inetd.conf file as follows:
4155 stream TCP nowait bzruser /usr/bin/bzr
/usr/bin/bzr serve --inet --directory=/srv/bzr/repo
- 4155 is the port number where the Bazaar server should listen for incoming connection.
- bzruser is the user account the bzr serve process will run as.
- /usr/bin/bzr is the absolute path of the bzr command.
- /usr/bin/bzr serve --inet --directory=/srv/bzr/repo
is the complete command to execute when starting the server. The --directory parameter is used to specify the base directory of Bazaar branches.
Once configured, this setup works exactly in the same way as using bzr serve directly, with the same advantages and disadvantages.
Creating branches on the central server
Creating branches on a server works much in the same way as when creating branches locally. Here, we emphasize on some good practices for optimal performance.
The same way as when working with local branches, it is a good idea to create a shared repository per project to host multiple Bazaar branches. Even if you don't intend to use multiple branches at first, you might want to do that later, and it is easier to have a shared repository right from the start, than migrating an existing branch later.
Another important point is to configure the shared repository to not create working trees by default. Working trees are unnecessary on the server, because collaborators work in their local checkouts, and Bazaar may give warnings during branch operations if the central branch contains a working tree. In order to avoid confusion, it is better to completely omit working trees on the server.
Creating a shared repository without working trees
Similar to when working with local branches, using a shared repository on the server is a good way to save disk space. In addition, when pushing a new branch to the server that shares revisions with an existing branch, the shared revisions don't need to be copied, thus the push operation will be faster.
When creating the shared repository, make sure to use the --no-trees flag, so that new branches will be created without trees by default. Although, most probably, you will create new branches using push operations, and most protocols don't support creating a working tree when used with push, nonetheless it is a good precaution to set up a shared repository in this way right from the start.
Reconfiguring a shared repository to not use working trees
You can use the bzr info command to check whether a shared repository is configured with or without working trees. For example:
$ bzr info bzr+ssh://email@example.com/tmp/repo/ Shared repository with trees (format: unnamed) Location: shared repository: bzr+ssh://firstname.lastname@example.org/tmp/repo/
If the first line of the output says Shared repository with trees instead of simply Shared repository, then you should log in to the server and reconfigure it by using the bzr reconfigure command with the --with-no-trees flag. For example:
$ cd /tmp/repo $ bzr reconfigure --with-no-trees $ bzr info Shared repository (format: 2a) Location: shared repository: .
Removing an existing working tree
If you already have branches on the central server with a working tree, then it is a good idea to remove them.
First, check the status of the working tree by using the bzr status command. If there are any pending changes, then commit or revert them.
To remove the working tree, use the bzr reconfigure command with the --branch flag.
Creating branches on the server without a working tree
Although you can use the bzr init and bzr branch commands directly on the server in the same way as you would do it locally, it would defeat the purpose of the centralized setup, and invite mistakes such as creating working trees by accident.
A common way to create new branches on the server is by using a push operation from your local branch. For example:
$ bzr push bzr+ssh://email@example.com/tmp/repo/branch1 Created new branch.
After pushing a branch, if you would like to work on it in the centralized mode, then you can bind to the remote branch by using the :push location alias:
$ bzr bind :push
Practical use cases
The key feature of the centralized mode is that it automatically keeps bound branches synchronized with their master branch. This opens interesting possibilities that can be useful in many situations, regardless of the workflow or the size of a team. To give you some idea here, we briefly introduce a few example use cases.
Working on branches using multiple computers
If you use multiple computers to work on a project, for example, a desktop and a laptop, or computers at different locations, then you probably need a way to synchronize your work done at physically different locations.
Although you can synchronize branches between the two locations by using mirror operations such as bzr push and bzr pull, they are not automatic, and thus you may easily find yourself in a situation that you cannot access some changes you did on another computer, because you forgot to run bzr push before you switched off the machine, for example.
Using the centralized mode can help here, because the synchronization between two branches is automatic, as it takes place at the time of each commit. You can start using the centralized mode by converting the branch you used to push to into a master branch, and binding to it with your other branches.
Let's say you have two computers, computerA and computerB, they both can access a branch at some location branchX, and you work on the branch sometimes by using computerA, and at other times by using computerB. (Whether branchX is hosted on computerA or computerB or a third computer doesn't matter, the example will still hold true.)
You can keep your work environments synchronized by using the bzr push and bzr pull operations, by adopting the following workflow on both the computers when working on branches you want to share:
- Pull from branchX.
- Work, make changes, and commit.
- Push to branchX.
This can be tedious and error-prone; for example, if you forget to push your changes on one computer, then you might not be able to access those changes after switching to the other computer, as it may have been powered down, or be inaccessible directly over the network.
Using the centralized mode would simplify the workflow to only two steps:
- Update from branchX.
- Work, make changes, and commit.
Not only there is one less step to do, but since in this case branchX is automatically updated at every commit, the possibility of forgetting to run bzr push is completely eliminated.
You can convert your existing setup to using centralized mode simply by binding to branchX on both the computers, and then using the update command to synchronize. Assuming that both branches have no pending changes and both have been pushed to branchX as their last operation, you can convert them by using the following commands:
$ bzr pull $ bzr bind :push
$ bzr bind :push $ bzr update
After this, you can start using branchX in the centralized mode, as a cycle of the bzr update and bzr commit operations.
Synchronizing backup branches
An easy way to back up a branch is by pushing it to another location. For example:
$ bzr push BACKUP_URL
BACKUP_URL can be a path on an external disk, a path on a network share or network filesystem, or any remote URL.
However, the push operation is not automatic; it must be executed manually every time you want to update the backup.
Another way is to bind the branch to the backup location, effectively using it in the centralized mode. In this case, all commits in the bound branch will be automatically applied to its master branch too, keeping the backup up-to-date at all times.
You can convert the branch to this setup, simply by binding to the push location:
$ bzr bind :push
Since this practically means switching to the centralized mode, it is important to have fast access to BACKUP_URL, otherwise the delay at every commit might be annoying.
If you need to break out of the centralized mode, for example when the BACKUP_URL is temporarily unavailable for some reason, then simply run bzr unbind. And after BACKUP_URL becomes available again, you can bring the remote branch up-to-date with bzr push, and re-bind to it by using bzr bind without additional parameters to return to the centralized mode.
In this article, we explained the core principles of the centralized mode with its advantages and disadvantages. Bazaar fully supports the centralized mode by using bound branches, and we have demonstrated, with examples, how you can switch in and out of this mode at any time. We have covered a few simple ways of setting up a central server, where team members can have shared write access to branches, and a few practical use cases.
The centralized mode in Bazaar is very flexible. It can be used for more than just to imitate the workflow of centralized version control systems. Essentially, it provides automatic synchronization of two branches, which can be practical in many situations, even as a part of more sophisticated distributed workflows.
Resources for Article :
- Configuration and Handy Tweaks for UDK [Article]
- Parallel Dimensions – Branching with Git [Article]
- Installing and customizing Redmine [Article]