Many, but not all, Bazaar operations work transparently on Git branches in the same way as with native Bazaar branches. Since both Bazaar and Git are distributed VCS tools, the behavior is quite consistent as long as you understand the few important differences.
The plugin to work with Git is named git
, typically in a package named bzr-git
. Confirm if it is already installed in your system by using bzr
plugins. If it is not in the list, see the Installing plugins section and follow the steps to install it. When installing with pip
, if bzr-git
doesn't work, try bzr-git-1480
instead.
In addition to bzr-git
, you also need to install dulwich
, a Python library to interface with Git. Install it by using your operating system's package manager or pip
. A simple way to verify that both bzr-svn
and dulwich
are correctly installed is by creating a dummy Git repository by using Bazaar:
$ bzr init --git /tmp/empty-git-repo Created a standalone tree (format: git)
The --git
flag is provided by the bzr-git
plugin. Normally, the bzr init
command does not have such an option.
Bazaar supports the following native Git URL formats:
https://server/path/to/project.git
: Git smart server over HTTPgit://server/path/to/project.git
: Git smart server[email protected]:user/project.git
: Projects on GitHub (Git smart server over SSH)file:///path/to/repo.git
: Local filesystem URL/path/to/repo.git
: Local filesystem pathThe URL format for accessing Git repositories over SSH is slightly different from Git's native format; instead of:
ssh://user@server:path/to/repo.git
In Bazaar, it is:
git+ssh://user@server/absolute/path/to/repo.git
Notice that not only the format is different; you must specify the absolute path to the repository. The relative path from the user's home directory doesn't work.
By default, Bazaar operations assume the master branch of the Git repository. You can specify a different branch by using the URL path segment parameter branch=BRANCHNAME
; for example, the URL of the feature1
branch will be as follows:
$ bzr info https://example.com/project.git,branch=feature1
Specifying a branch like this works for all supported URLs except filesystem paths, such as /path/to/repo.git
. In this case, Bazaar will use the "current branch" of the local repository, or the master branch if it is a "bare" repository with no working tree.
The best way to explore how Bazaar works with Git branches is by playing with a local Git repository. If you are familiar with Git, you can create a sample repository by using git init
, or you can download and unpack the sample repository at the following URL:
https://launchpad.net/bzrbook-examples/trunk/examples/+download/repo.git.tar.gz
The repo.git
directory inside the ZIP file contains a small Git repository, which will be accessible via a direct filesystem path. For example, if you move the repo.git
directory into /tmp
, it will be accessible via the URL /tmp/repo.git
. You can try some commands, such as bzr info
or bzr log
, on it:
$ bzr info /tmp/repo.git/ Standalone branch (format: git-bare) Location: branch root: /tmp/repo.git $ bzr branches /tmp/repo.git * anna * jack * master * mike * tmp1 $ bzr log /tmp/repo.git/ --line -n0 2: Janos Gyerik 2012-12-09 [merge] merged from mike 1.1.2: Mike 2012-12-09 added perl impl 1.1.1: Mike 2012-12-09 added shell impl 1: Janos Gyerik 2012-12-09 initial commit with only readme
Many of the examples in this section will use this repository, assuming it at the path /sandbox/repo.git
.
You can branch from Git by using bzr branch
or Bazaar Explorer as usual. But first, let's create a shared repository:
$ bzr init-repo /sandbox/git-examples Shared repository with trees (format: 2a) Location: shared repository: git-examples $ cd /sandbox/git-examples/
It is always a good idea to use a shared repository when working with multiple remote branches. This is especially true when working with foreign branches, due to the added overhead of translation between Bazaar and the foreign protocol.
Let's get a branch from the example repository:
$ bzr branch /sandbox/repo.git Branched 2 revisions.
This gets the master branch from the repository, as this is the only branch that can be accessed by a filesystem path. To get another branch, we must use the file:///
style URL. For example:
$ bzr branch file:///sandbox/repo.git,branch=anna Branched 4 revisions.
As a result, the Bazaar branch is created in the directory anna
.
In case of remote Git repositories, the default name of the newly created local branch is the last path segment of the URL, or if a branch is specified, then the name of the branch.
As always, fetching the first branch from a remote repository may take a long time, as Bazaar needs to download all the revisions in the branch. In addition, in case of foreign branches, such as Git, Bazaar needs to convert the version control data to native Bazaar format. Thanks to using a shared repository, fetching a second branch usually takes much less time, as typically much of the revision history is common between branches.
Bazaar correctly preserves Git's version control metadata, such as the following:
Revisions in Git are identified by a unique SHA1 hash. When branching from Git, Bazaar creates its own revision numbers, but preserves the original Git IDs too, as we can confirm by using bzr log
:
$ bzr log master/ -r2 ------------------------------------------------------------ revno: 2 [merge] git commit: a8136869caef6ef6cbbe571ac1b1675e1da80415 committer: Janos Gyerik <janos@axiom> timestamp: Sun 2012-12-09 09:11:41 +0100 message: merged from mike ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.
You can reference revisions by their original Git ID or its shorter 6-digit version (if unique). For example:
$ bzr log -r a81368 master/ ------------------------------------------------------------ revno: 2 [merge] git commit: a8136869caef6ef6cbbe571ac1b1675e1da80415 committer: Janos Gyerik <janos@axiom> timestamp: Sun 2012-12-09 09:11:41 +0100 message: merged from mike ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.
Bazaar also re-uses Git revision IDs in the internal revision ID of revisions, which you can see by using the --show-ids
flag of bzr log
:
$ bzr log master/ -r2 --show-ids ------------------------------------------------------------ revno: 2 [merge] revision-id: git-v1:a8136869caef6ef6cbbe571ac1b1675e1da80415 parent: git-v1:b4a59b7391c18481716851d2d5985981d7b041f3 parent: git-v1:dfab9069d09c040f4abd8444f33604799ad786bd git commit: a8136869caef6ef6cbbe571ac1b1675e1da80415 committer: Janos Gyerik <janos@axiom> timestamp: Sun 2012-12-09 09:11:41 +0100 message: merged from mike ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.
Notice that revision-id
is in fact derived from the Git ID by prefixing with git-v1:
.
The important consequence of this is that multiple Bazaar branches created from the same source will be identical. The bzrbook/git-repo-example1.git
repository on GitHub is identical to our example repository. Therefore, if we branch from it, we should get an identical Bazaar branch. Let's confirm this with a simple test:
$ bzr branch git://github.com/bzrbook/git-repo-example1.git /tmp/master2 Branched 2 revisions. $ bzr missing -d master /tmp/master2 Branches are up to date.
This is especially important when merging branches, as it effectively prevents from merging the same revisions more than once.
Same as with Bazaar branches, merged branches and their revisions are preserved when branching from Git:
$ bzr log --line -n0 master 2: Janos Gyerik 2012-12-09 [merge] merged from mike 1.1.2: Mike 2012-12-09 added perl impl 1.1.1: Mike 2012-12-09 added shell impl 1: Janos Gyerik 2012-12-09 initial commit with only readme
The logs are especially easy to view in Bazaar Explorer:
Updating a Bazaar branch from a Git parent branch works in the same way as with native Bazaar branches. Let's test this by branching from an older revision of a Git branch, and then pulling from it to bring it up-to-date:
$ bzr branch -rlast:2 /sandbox/repo.git/ feature1 Branched 1 revisions.
Our local Bazaar branch is out of date, precisely one revision behind, so let's bring it up-to-date by using bzr pull
:
$ cd feature1 $ bzr pull Using saved parent location: /sandbox/repo.git/ +N hello.pl +N hello.sh All changes applied successfully. Now on revision 2.
Keep in mind that the pull
command may rearrange the revisions in the local branch in the same way as it works with native Bazaar branches.
To push a Bazaar branch to a Git repository, you must use bzr dpush
instead of bzr push
. The difference between the two commands is that bzr push
is meant to be "lossless" and since some of Bazaar's metadata cannot be pushed to Git losslessly, the command is disabled for Git repositories at the moment. bzr dpush
is designed to push revisions to foreign repositories without trying to preserve Bazaar's metadata, and in this way, it can work with Git repositories:
$ bzr dpush file:///sandbox/repo.git,branch=tmp1 Pushed up to revision 3.
This example worked, but only because the tmp1
branch already existed in this example repository. Unfortunately, at the moment, bzr-git
cannot create new branches when pushing to a local Git repository; it can only push to an existing branch.
More importantly, notice that we can overwrite the tmp1
branch despite being diverged from it, without bzr-git
raising an error or at least issuing a warning. Therefore, you must be very cautious when pushing branches to Git by using bzr dpush
.
Bazaar is very good at merging Git branches, thanks to a few notable key factors:
bzr-git
converts Git branches to native Bazaar branches, the merge operation is ultimately performed between Bazaar branchesbzr
remerge
Let's try it out by merging two branches, for example, jack
into tmp1
:
$ bzr branch file:///sandbox/repo.git,branch=jack --no-tree Branched 4 revisions. $ cd tmp1/ $ bzr merge ../jack/ +N hello.rb +N hello.tcl All changes applied successfully.
In this example, we fetched jack
to a local Bazaar branch before merging from it, but we could have used the remote branch URL directly. However, it is always a good idea to fetch the branch first, as in this way, you can re-use the branch multiple times without redownloading it again from the source repository.
Also notice that we used the --no-tree
flag to create the branch. In this way, we can save the disk space, as we don't need the working tree of the branch if we just want to merge from it.
Let's commit the merge:
$ bzr commit -m 'merged from jack' --author gatekeeper Committing to: /sandbox/git-examples/tmp1/ added hello.rb added hello.tcl Committed revision 4.
The merge is now committed locally; we can push it to the remote branch by using bzr dpush
:
$ bzr dpush file:///sandbox/repo.git,branch=tmp1 Pushed up to revision 4.
You must be very careful when pushing to a branch using bzr dpush
. Since this command overwrites the remote Git branch without warning, it can be dangerous. If the remote branch is changed by others since the last time we fetched it, those changes would be erased. Use with caution.
Remember that with Bazaar, you can choose from different merge algorithms, which sometimes yield better results with fewer conflicts. The bzr remerge
command is especially useful to try a different algorithm on selected files, and the --reprocess
flag may help reducing the size of the conflicted area. See Chapter 3, Using Branches, for a detailed explanation with examples.
Let's create a local branch from master
named feature3
, and do some commits in it:
$ bzr branch master/ feature3 Branched 2 revisions. $ cd feature3/ $ echo >> hello.pl $ bzr commit -m 'meaningless change' Committing to: /sandbox/git-examples/feature3/ modified hello.pl Committed revision 3. $ echo >> hello.sh $ bzr commit -m 'another meaningless change' Committing to: /sandbox/git-examples/feature3/ modified hello.sh Committed revision 4.
Next, let's merge this branch into our tmp1
branch, and commit the merge:
$ cd ../tmp1/ $ bzr merge ../feature3/ M hello.pl M hello.sh All changes applied successfully. $ bzr commit -m 'merged from feature3' Committing to: /sandbox/git-examples/tmp1/ modified hello.pl modified hello.sh Committed revision 5. $ bzr dpush file:///sandbox/repo.git,branch=tmp1 Pushed up to revision 5.
At this point, the revisions in the feature3
branch and the revision with the merge commit exist only locally, not in Git yet. Naturally, these revisions don't have a Git ID, as we can confirm in the log:
$ bzr log -l1 --show-ids ------------------------------------------------------------ revno: 5 [merge] revision-id: janos@axiom-20121209162758-r1cggic29iccu4ud parent: git-v1:3070cdd84f89ae8fc45e2490e897dfe611464749 parent: janos@axiom-20121209162615-46kcj8x3r1czkm57 committer: Janos Gyerik <janos@axiom> branch nick: tmp1 timestamp: Sun 2012-12-09 17:27:58 +0100 message: merged from feature3 ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.
Let's push the branch to git, and then check the log again:
$ bzr dpush file:///sandbox/repo.git,branch=tmp1 Pushed up to revision 5. $ bzr log -l1 --show-ids ------------------------------------------------------------ revno: 5 [merge] revision-id: git-v1:8ab5cde217d110765fef71b9f1107d65ceb43db1 parent: git-v1:3070cdd84f89ae8fc45e2490e897dfe611464749 parent: git-v1:9d3a2bb239f679c04a2104b99fe2b4a11c04f8e0 git commit: 8ab5cde217d110765fef71b9f1107d65ceb43db1 committer: Janos Gyerik <janos@axiom> timestamp: Sun 2012-12-09 17:27:58 +0100 message: merged from feature3 ------------------------------------------------------------ Use --include-merged or -n0 to see merged revisions.
An interesting thing happened—in addition to pushing the local revisions to the Git repository, the IDs of these local revisions have been retroactively changed to values derived from their corresponding Git revision IDs. This may seem a bit strange at first, but it is necessary for the integrity of future interactions with this Git repository, and thus to prevent issues such as accidentally merging the same revisions twice.
Finally, since the parent branches are correctly tracked in Git just like in Bazaar, the parent-child relationships are correctly preserved, as we can confirm by using bzr log
:
$ bzr log --line -n0 -l4 file:///sandbox/repo.git,branch=tmp1 5: Janos Gyerik 2012-12-09 [merge] merged from feature3 2.2.2: Janos Gyerik 2012-12-09 another meaningless change 2.2.1: Janos Gyerik 2012-12-09 meaningless change 4: gatekeeper <gatek... 2012-12-09 [merge] merged from jack
To conclude, local branches can be merged into Git. However, always keep in mind that bzr dpush
may overwrite the remote branch; therefore, use it with caution.
The bzr-git
plugin has the following limitations:
bzr push
does not workbzr dpush
can be used instead of bzr push
, but it can be dangerous, as it rewrites the revision history without warning even if the branches have divergedFor a more complete and up-to-date list of limitations, refer to the plugin's homepage:
http://doc.bazaar.canonical.com/migration/en/foreign/bzr-on-git-projects.html
For the most part, Bazaar works transparently with Git repositories. The result of a branch operation from Git is a native Bazaar branch, thus all your local operations will be native Bazaar operations that you are used to. However, all interactions with the remote Git repository will be inevitably slower than usual, due to the translation between the protocols of these systems.
As long as you are aware of the limitations and the differences between the two systems, you can use Bazaar as a Git repository client, and take advantage of its advanced merging capabilities. However, be careful when pushing branches with bzr dpush
, as it overwrites the remote branch. To be safe, it is best to push only to branches that are owned by you and to which others have only read-only access.
Quick tips and cheat sheet:
bzr
remerge
and the various merging strategiesbzr
dpush
, as it overwrites the remote branchThe up-to-date details and limitations of the plugin are documented at the following locations:
3.128.226.121