A rough guide to migrating Bazaar repositories to GitHub
A rough guide to migrating Bazaar repositories to GitHub

We have been using Bazaar for source control at work for nearly five years. We are about to Open Source most of our core technologies and decided that GitHub is the best way to encourage community participation. We have signed up for a Silver plan at GitHub and will migrate all our Bazaar repositories to Git.

I have a few personal projects in Bazaar repositories hosted on Launchpad. I decided to migrate my projects to GitHub in order to learn the migration process. What follows is an overview of how I did it using a fresh virtual machine running Ubuntu 10.04 LTS Server. I used a little project of mine called nullserv in the examples below.

Add the Bazaar and Git PPAs.

sudo apt-get install python-software-properties
sudo apt-add-repository ppa:bzr/ppa
sudo apt-add-repository ppa:git-core/ppa
sudo apt-get update

Install bzr (and its requirements), curl and git.

sudo apt-get install bzr bzr-fastimport curl git python-paramiko

Add the SSH keys and identify yourself.

bzr whoami "Your Name <name@example.org>"
git config --global user.name "Your Name"
git config --global user.email you@example.org

If your Bazaar repository is hosted on Launchpad assert your identity.

bzr launchpad-login flexiondotorg

Branch the Bazaar repository.

bzr branch lp:~flexiondotorg/+junk/nullserv
cd nullserv
git init
bzr fast-export --plain `pwd` | git fast-import

This step is optional. It will delete and commit the deletions to the Bazaar repository.

for FILE in *; do rm -rfv "${FILE}"; done
echo "This repository has been migrated to Git. https://github.com/flexiondotorg/nullserv" > README
bzr add README
bzr commit -m "This repository has been migrated to Git. https://github.com/flexiondotorg/nullserv"
bzr push :parent

Remove the Bazaar repository and reset the Git repository.

rm -rf .bzr README
git reset HEAD

Create .gitattributes to normalise line endings.

cat >.gitattributes<<ENDGITATTRIBS
# Normalise line endings:
* text=auto

# Prevent certain files from being exported:
.gitattributes  export-ignore
.gitignore      export-ignore
ENDGITATTRIBS

git add .gitattributes

Migrate .bzrignore to .gitignore.

git mv .bzrignore .gitignore
echo                             >> .gitignore
echo "# Keep empty directories:" >> .gitignore
echo "!*/.git*"                  >> .gitignore

Ensure empty directories are retained by git.

find -empty -type d -not -iwholename '*.git*' -exec touch '{}/.gitkeep' ";"
git add **/.gitkeep

Commit the migrated repository

git commit -a -m "Migrated from Bazaar to Git."

Thanks to Chris for pointing out git filter-branch in the comments. If you need to modify the author info in your repository history, you can do so with this, just replace the names and email addresses accordingly.

BEWARE! This should not be performed on a repo that has been shared with others. Use at your own risk.

git filter-branch --commit-filter '
    if [ "$GIT_COMMITTER_NAME" = "Fred" ]; then
        GIT_COMMITTER_NAME="Fred Flintstone";
        GIT_AUTHOR_NAME="Fred Flintstone";
        GIT_COMMITTER_EMAIL="fred@example.org";
        GIT_AUTHOR_EMAIL="fred@example.org";
        git commit-tree "$@";
    elif [ "$GIT_COMMITTER_NAME" = "Barney" ]; then
        GIT_COMMITTER_NAME="Barney Rubble";
        GIT_AUTHOR_NAME="Barney Rubble";
        GIT_COMMITTER_EMAIL="barney@example.org";
        GIT_AUTHOR_EMAIL="barney@example.org";
        git commit-tree "$@";
    else
        git commit-tree "$@";
    fi' HEAD

If you want to delete any files from the commit history, you can optionally do that now.

git filter-branch -f --index-filter "git rm --cached --ignore-unmatch *.THIS *.THAT" \
--prune-empty --tag-name-filter cat -- --all
rm -rf .git/refs/original/
git reflog expire --expire=now --all
git gc --prune=now

Resume here, regardless of whether you deleted any files from the commit history or not. Remove everything from the index.

git rm --cached -r .

Write both the index and working directory from git’s database.

git reset --hard

Prepare to make a commit by staging all the files that will get normalized. This is your chance to inspect which files were never normalized. You may get lots of messages like: warning: CRLF will be replaced by LF in file.

git add .
git commit -m "Forced line endings to eol=lf"

Aggressively pack the repository.

git gc --aggressive --prune=now

At this point you have a migrated git repository. You can poke about a check that everything is present and correct.

Optionally you can create a new GitHub repository using their API. Replace USER and PASS with your GitHub login crednetials.

curl -u 'USER:PASS' https://api.github.com/user/repos -d '{"name":"nullserv"}'

If you want to create repositories for an Organisation the following will work. Replace YourOrganisation with the organisation name your are a member of.

curl -u 'USER:PASS' https://api.github.com/orgs/YourOrganisation/repos -d '{"name":"nullserv"}'

Private repositories can be created, providing you have a paid GitHub account, by changing the POST data as follows.

'{"name":"nullserv","private":"true"}'

Lastly, push to the newly created GitHub repo.

git remote add origin git@github.com:flexiondotorg/nullserv.git
git push -u origin master

All done, the Bazaar repository has been crippled and the Git repository is ready for use on GitHub.

References