1
=======================
2
Integrating with Bazaar
3
=======================
5
This document provides some general observations on integrating with
6
Bazaar and some recipes for typical tasks. It is intended to be useful to
7
someone developing either a plugin or some other piece of software that
8
integrates with bzr. If you want to know about a topic that's not covered
20
When using bzrlib within the ``bzr`` program (for instance as a bzr
21
plugin), bzrlib's global state is already available for use.
26
To use bzrlib outside of ``bzr`` some global state needs to be setup.
27
bzrlib needs ways to handle user input, passwords, a place to emit
28
progress bars, logging setup appropriately for your program. The easiest
29
way to set all this up in the same fashion ``bzr`` does is to call
30
``bzrlib.initialize``.
32
This returns a context manager within which bzrlib functions will work
33
correctly. See the pydoc for ``bzrlib.initialize`` for more information.
34
(You can get away without entering the context manager, because the setup
35
work happens directly from ``initialize``.)
37
In Python 2.4 the ``with`` keyword is not supported and
38
so you need to use the context manager manually::
40
# This sets up your ~/.bzr.log, ui factory and so on and so forth. It is
41
# not safe to use as a doctest.
42
library_state = bzrlib.initialize()
43
library_state.__enter__()
48
library_state.__exit__(None, None, None)
54
To run command-line commands in-process::
56
from bzrlib.commands import get_command
58
cmd = get_command('version')
61
This will send output through the current UIFactory; you can redirect this
62
elsewhere through the parameters to `bzrlib.initialize`.
65
Manipulating the Working Tree
66
=============================
67
Most objects in Bazaar are in files, named after the class they contain.
68
To manipulate the Working Tree we need a valid WorkingTree object, which
69
is loaded from the workingtree.py file, eg::
71
from bzrlib import workingtree
72
wt = workingtree.WorkingTree.open('/home/jebw/bzrtest')
75
This gives us a WorkingTree object, which has various methods spread over
76
itself, and its parent classes MutableTree and Tree - it's worth having a
77
look through these three files (workingtree.py, mutabletree.py and tree.py)
78
to see which methods are available.
83
There are two methods for comparing trees: ``changes_from`` and
84
``iter_changes``. ``iter_changes`` is more regular and precise, but it is
85
somewhat harder to work with. See the API documentation for more details.
87
``changes_from`` creates a Delta object showing changes::
89
from bzrlib import delta
90
changes = wt.changes_from(wt.basis_tree())
92
This gives us a Delta object, which has several lists of files for each type of
93
change, eg changes.added is a list of added files, changes.removed is list
94
of removed files, changes.modified is a list of modified files. The contents
95
of the lists aren't just filenames, but include other information as well.
96
To grab just the filename we want the first value, eg::
98
print("list of newly added files")
99
for filename in changes.added:
100
print("%s has been added" % filename[0])
103
The exception to this is changes.renamed, where the list returned for each
104
renamed files contains both the old and new names -- one or both may interest
105
you, depending on what you're doing.
109
print("list of renamed files")
110
for filename in changes.renamed:
111
print("%s has been renamed to %s" % (filename[0], filename[1]))
117
If you want to add files the same way ``bzr add`` does, you can use
118
MutableTree.smart_add. By default, this is recursive. Paths can either be
119
absolute or relative to the workingtree::
121
wt.smart_add(['dir1/filea.txt', 'fileb.txt',
122
'/home/jebw/bzrtesttree/filec.txt'])
125
For more precise control over which files to add, use MutableTree.add::
127
wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/bzrtesttree/filec.txt'])
133
You can remove multiple files at once. The file paths need to be relative
136
wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
139
By default, the files are not deleted, just removed from the inventory.
140
To delete them from the filesystem as well::
142
wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
148
You can rename one file to a different name using WorkingTree.rename_one.
149
You just provide the old and new names, eg::
151
wt.rename_one('oldfile.txt','newfile.txt')
157
You can move multiple files from one directory into another using
160
wt.move(['olddir/file.txt'], 'newdir')
163
More complicated renames/moves can be done with transform.TreeTransform,
164
which is outside the scope of this document.
170
To commit _all_ the changes to our working tree we can just call the
171
WorkingTree's commit method, giving it a commit message, eg::
173
wt.commit('this is my commit message')
176
To commit only certain files, we need to provide a list of filenames which we
177
want committing, eg::
179
wt.commit(message='this is my commit message', specific_files=['fileA.txt',
180
'dir2/fileB.txt', 'fileD.txt'])
183
Generating a Log for a File
184
===========================
186
Generating a log is, in itself, simple. Grab a branch (see below) and pass
187
it to show_log together with a log formatter, eg::
189
from bzrlib import log
190
from bzrlib import branch
192
b = branch.Branch.open('/path/to/bazaar/branch')
193
lf = log.LongLogFormatter(to_file=sys.stdout)
197
Three log formatters are included with bzrlib: LongLogFormatter,
198
ShortLogFormatter and LineLogFormatter. These provide long, short and
199
single-line log output formats. It's also possible to write your own in
205
To annotate a file, we want to walk every line of a file, retrieving the
206
revision which last modified/created that line and then retrieving the
207
information for that revision.
209
First we get an annotation iterator for the file we are interested in::
211
tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
212
fileid = tree.path2id(relpath)
213
annotation = list(tree.annotate_iter(fileid))
216
To avoid repeatedly retrieving the same revisions we grab all revisions
217
associated with the file at once and build up a map of id to revision
218
information. We also build an map of revision numbers, again indexed
221
revision_ids = set(revision_id for revision_id, text in annotation)
222
revisions = tree.branch.repository.get_revisions(revision_ids)
223
revision_map = dict(izip(revision_ids, revisions))
224
revno_map = tree.branch.get_revision_id_to_revno_map()
227
Finally, we use our annotation iterator to walk the lines of the file,
228
displaying the information from our revision maps as we go::
230
for revision_id, text in annotation :
231
rev = revision_map[revision_id]
232
revno = revno_map[revision_id]
233
revno_string = '.'.join(str(i) for i in revno)
234
print "%s, %s: %s" % (revno_string, rev.committer, text)
237
Working with branches
238
=====================
240
To work with a branch you need a branch object, created from your branch::
242
from bzrlib import branch
244
b = branch.Branch.open('/home/jebw/bzrtest')
247
Branching from an existing branch
248
---------------------------------
250
To branch you create a branch object representing the branch you are
251
branching from, and supply a path/url to the new branch location.
252
The following code clones the bzr.dev branch (the latest copy of the Bazaar
253
source code) - be warned it has to download 60meg so takes a while to run
256
from bzrlib import branch
258
b = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
259
nb = b.bzrdir.sprout('/tmp/newBzrBranch').open_branch()
262
This provides no feedback, since Bazaar automatically uses the 'silent' UI.
265
Pushing and pulling branches
266
----------------------------
268
To push a branch you need to open the source and destination branches, then
269
just call push with the other branch as a parameter::
271
from bzrlib import branch
273
b1 = branch.Branch.open('file:///home/user/mybranch')
274
b2 = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
278
Pulling is much the same::
283
If you have a working tree, as well as a branch, you should use
284
WorkingTree.pull, not Branch.pull.
286
This won't handle conflicts automatically though, so any conflicts will be
287
left in the working tree for the user to resolve.
290
Checkout from an existing branch
291
================================
293
This performs a Lightweight checkout from an existing Branch::
295
from bzrlib import bzrdir
297
accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
298
source.create_checkout('/tmp/newBzrCheckout', None, True, accelerator_tree)
301
To make a heavyweight checkout, change the last line to::
303
source.create_checkout('/tmp/newBzrCheckout', None, False, accelerator_tree
309
Finding the last revision number or id
310
--------------------------------------
312
To get the last revision number and id of a branch use::
314
revision_number, revision_id = branch.last_revision_info()
317
If all you care about is the revision_id there is also the
320
revision_id = branch.last_revision()
323
Getting the list of revision ids that make up a branch
324
------------------------------------------------------
326
IMPORTANT: This should be avoided wherever possible, as it scales with the
329
revisions = branch.revision_history()
331
now revisions[0] is the revision id of the first commit, and revs[-1] is the
332
revision id of the most recent. Note that if all you want is the last
333
revision then you should use branch.last_revision() as described above, as
334
it is vastly more efficient.
337
Getting a Revision object from a revision id
338
--------------------------------------------
340
The Revision object has attributes like "message" to get the information
343
repo = branch.repository
344
revision = repo.get_revision(rev_id)
347
Accessing the files from a revision
348
-----------------------------------
350
To get the file contents and tree shape for a specific revision you need
351
a RevisionTree. These are supplied by the repository for a specific
354
revtree = repo.revision_tree(rev_id)
356
RevisionTrees, like all trees, can be compared as described in "Comparing
359
The most common way to list files in a tree is ``Tree.iter_entries()``.
360
The simplest way to get file content is ``Tree.get_file()``. The best way
361
to retrieve file content for large numbers of files `Tree.iter_files_bytes()``