Bazaar is a distributed version control system (VCS), developed by Canonical.

For a long time, Bazaar was the only supported VCS on Launchpad.

Launchpad is a code hosting platform, similar to the now prevalent GitHub, and while open to the public, nowadays it is mostly used by Canonical itself and many other individuals and companies to manage the whole lifecycle of creating packages for Ubuntu and its distributions.

Since quite some time also git is a supported VCS on Launchpad.

In order to ease collaboration, let’s convert a Bazaar repository to git.

Prerequisites

In order get data out of a Bazaar repository, you need to install a Bazaar client and an export plugin.

Without going into details, you should install the latest version of Breezy, which is a Bazaar client and comes with the FastImport plugin, which also can export data.

$ pipx install breezy

Clone the Repository

I choose lazr.config to demonstrate the process.

Let’s go to the project on Launchpad, click on the code tab, and copy the command to clone the repository:

$ brz branch lp:lazr.config
Branched 25 revisions.
$ cd lazr.config

Interlude

Before we continue, it is worth to have a look at the active reviews tab of the project.

Active reviews are Launchpad’s equivalent to GitHub’s Pull Requests. They can’t be merged after the migration from Bazaar to Git, so we need to decide whether we merge them now, or politely ask the contributor to create a new merge proposal against the git repository.

Migrate the Repository

As both Bazaar and git use a “hidden” folder to store the meta data, we can perform the conversion in the same directory we cloned the repository into.

git init --initial-branch=main

We need to use the -b option to specify a branch name other then master.

$ brz fast-export -b main | git fast-import
21:33:45 Calculating the revisions to include ...
21:33:45 Starting export of 67 revisions ...
21:33:45 Adjusting path src/lazr/config/tests/test_docs.py given rename of src/lazr to lazr in revision b'barry@canonical.com-20130107011400-9fa1vo95jtuwtu6w'
21:33:45 Adjusting path src/lazr/config/NEWS.txt given rename of src/lazr to lazr in revision b'barry@canonical.com-20130110211153-1po6s4hapsvwyv4w'
21:33:45 Adjusting path src/lazr/config/tests/test_docs.py given rename of src/lazr to lazr in revision b'barry@canonical.com-20130110211153-1po6s4hapsvwyv4w'
21:33:45 Exported 67 revisions in 0:00:00
fast-import statistics:
---------------------------------------------------------------------
Alloc'd objects:       5000
Total objects:          477 (       148 duplicates                  )
      blobs  :          195 (        90 duplicates         71 deltas of        186 attempts)
      trees  :          215 (        58 duplicates        159 deltas of        200 attempts)
      commits:           67 (         0 duplicates          0 deltas of          0 attempts)
      tags   :            0 (         0 duplicates          0 deltas of          0 attempts)
Total branches:           7 (         1 loads     )
      marks:           1024 (        67 unique    )
      atoms:             50
Memory total:          2493 KiB
       pools:          2141 KiB
     objects:           351 KiB
---------------------------------------------------------------------
pack_report: getpagesize()            =       4096
pack_report: core.packedGitWindowSize = 1073741824
pack_report: core.packedGitLimit      = 35184372088832
pack_report: pack_used_ctr            =        137
pack_report: pack_mmap_calls          =         39
pack_report: pack_open_windows        =          1 /          1
pack_report: pack_mapped              =     555465 /     555465
---------------------------------------------------------------------

Almost finished..

This was easy.

But wait…

$ git status
On branch main
Changes to be committed:
  (use "git restore --staged <file>..." to unstage)
	deleted:    .bzrignore
	deleted:    COPYING.txt
	deleted:    HACKING.rst
	deleted:    MANIFEST.in
	deleted:    NEWS.rst
	deleted:    README.rst
	deleted:    conf.py
	deleted:    setup.cfg
	deleted:    setup.py
	deleted:    src/lazr/__init__.py
	deleted:    src/lazr/config/__init__.py
	deleted:    src/lazr/config/_config.py
	deleted:    src/lazr/config/_version.py
	deleted:    src/lazr/config/docs/__init__.py
	deleted:    src/lazr/config/docs/fixture.py
	deleted:    src/lazr/config/docs/usage.rst
	deleted:    src/lazr/config/docs/usage_fixture.py
	deleted:    src/lazr/config/interfaces.py
	deleted:    src/lazr/config/tests/__init__.py
	deleted:    src/lazr/config/tests/test_config.py
	deleted:    src/lazr/config/tests/testdata/__init__.py
	deleted:    src/lazr/config/tests/testdata/bad-invalid-name-chars.conf
	deleted:    src/lazr/config/tests/testdata/bad-invalid-name.conf
	deleted:    src/lazr/config/tests/testdata/bad-nonascii.conf
	deleted:    src/lazr/config/tests/testdata/bad-redefined-key.conf
	deleted:    src/lazr/config/tests/testdata/bad-redefined-section.conf
	deleted:    src/lazr/config/tests/testdata/bad-sectionless.conf
	deleted:    src/lazr/config/tests/testdata/base.conf
	deleted:    src/lazr/config/tests/testdata/local.conf
	deleted:    src/lazr/config/tests/testdata/master-local.conf
	deleted:    src/lazr/config/tests/testdata/master.conf
	deleted:    src/lazr/config/tests/testdata/shared.conf
	deleted:    tox.ini

Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.bzr/
	.bzrignore
	COPYING.txt
	HACKING.rst
	MANIFEST.in
	NEWS.rst
	README.rst
	conf.py
	setup.cfg
	setup.py
	src/
	tox.ini

Help, what a mess!

Sir git add-a-lot

I am not really sure why this happens, but you can fix it with the following command.

$ git add -- . ':!.bzr'
$ git status
On branch main
Untracked files:
  (use "git add <file>..." to include in what will be committed)
	.bzr/

nothing added to commit but untracked files present (use "git add" to track)

Do not delete the .bzr folder yet!

Rename .bzrignore to .gitignore

Before we continue I recommend to rename .bzrignore to .gitignore.

While the format might not be 100% the same, just renaming was enough most of the time.

Do not forget to add and commit this change to git.

Upload the git Repository

Next, we need to upload the git repository back to Launchpad.

On the project’s Launchpad site, go to the code tab, and then on the right click on Configure Code.

On the next screen select git.

Now you see the the command to set the git remote address.

git remote add origin git+ssh://jugmac00@git.launchpad.net/lazr.config

Now we can upload the repository.

git push --tags

Where is my git Code?

When you go to the code tab of the project, you still only see the Bazaar repository.

Actually, you could already switch to the git repository by clicking the link on the right.

In order to change the default view, you need to go to Configure Code again, choose git and this time hit the Update button.

git not done yet

When you clone this new repository for the first time, you will be surprised…

$ git clone git+ssh://jugmac00@git.launchpad.net/lazr.config
Cloning into 'lazr.config'...
remote: Enumerating objects: 479, done.
remote: Counting objects: 100% (479/479), done.
remote: Compressing objects: 100% (203/203), done.
remote: Total 479 (delta 230), reused 479 (delta 230)
Receiving objects: 100% (479/479), 542.84 KiB | 2.71 MiB/s, done.
Resolving deltas: 100% (230/230), done.
warning: remote HEAD refers to nonexistent ref, unable to checkout.

The reason for the warning is that Launchpad’s default branch name is master.

In order to the change the default branch name, from the code tab, under the Other repositories section, I click on lp:lazr.config.

On the following page, on the right side, I click on Change repository details.

At the bottom I change the Default branch from refs/heads/master to refs/heads/main.

Now, cloning works as intended.

Subscriptions, anyone?

While I am here, I check who is subscribed to the repository.

Bazaar, anyone?

While the git repository is now the default one, you could still push to the Bazaar repository.

There is no obvious way to delete the Bazaar repository, but anyway there is a better way for a smooth transition.

First, let’s delete all files from the Bazaar repository locally.

brz rm *

Then we create a MOVED_TO_GIT file with the following content

https://code.launchpad.net/lazr.config/+git

… and then add and commit the note

brz add MOVED_TO_GIT
brz commit -m "Moved to git"

… and push it to the still existing Bazaar repository

brz push lp:lazr.config

The end

Still here?

As the project’s documentation is not yet on Read the Docs, let’s keep going.

As Read the Docs does not support Launchpad’s webhooks, we need to both manually import and build the documentation, but that is pretty straightforward, so I am positive you will figure this out by yourself.