1
=======================
2
Integrating with Breezy
3
=======================
5
This document provides some general observations on integrating with
6
Breezy 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 brz. If you want to know about a topic that's not covered
20
When using breezy within the ``brz`` program (for instance as a brz
21
plugin), breezy's global state is already available for use.
26
To use breezy outside of ``brz`` some global state needs to be setup.
27
breezy 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 ``brz`` does is to call
30
``breezy.initialize``.
32
This returns a context manager within which breezy functions will work
33
correctly. See the pydoc for ``breezy.initialize`` for more information.
34
(You can get away without entering the context manager, because the setup
35
work happens directly from ``initialize``.)
41
To run command-line commands in-process::
43
from breezy.commands import get_command
45
cmd = get_command('version')
48
This will send output through the current UIFactory; you can redirect this
49
elsewhere through the parameters to `breezy.initialize`.
52
Manipulating the Working Tree
53
=============================
54
Most objects in Breezy are in files, named after the class they contain.
55
To manipulate the Working Tree we need a valid WorkingTree object, which
56
is loaded from the workingtree.py file, eg::
58
from breezy import workingtree
59
wt = workingtree.WorkingTree.open('/home/jebw/brztest')
62
This gives us a WorkingTree object, which has various methods spread over
63
itself, and its parent classes MutableTree and Tree - it's worth having a
64
look through these three files (workingtree.py, mutabletree.py and tree.py)
65
to see which methods are available.
70
There are two methods for comparing trees: ``changes_from`` and
71
``iter_changes``. ``iter_changes`` is more regular and precise, but it is
72
somewhat harder to work with. See the API documentation for more details.
74
``changes_from`` creates a Delta object showing changes::
76
from breezy import delta
77
changes = wt.changes_from(wt.basis_tree())
79
This gives us a Delta object, which has several lists of files for each type of
80
change, eg changes.added is a list of added files, changes.removed is list
81
of removed files, changes.modified is a list of modified files. The contents
82
of the lists aren't just filenames, but include other information as well.
83
To grab just the filename we want the first value, eg::
85
print("list of newly added files")
86
for filename in changes.added:
87
print("%s has been added" % filename[0])
90
The exception to this is changes.renamed, where the list returned for each
91
renamed files contains both the old and new names -- one or both may interest
92
you, depending on what you're doing.
96
print("list of renamed files")
97
for filename in changes.renamed:
98
print("%s has been renamed to %s" % (filename[0], filename[1]))
104
If you want to add files the same way ``brz add`` does, you can use
105
MutableTree.smart_add. By default, this is recursive. Paths can either be
106
absolute or relative to the workingtree::
108
wt.smart_add(['dir1/filea.txt', 'fileb.txt',
109
'/home/jebw/brztesttree/filec.txt'])
112
For more precise control over which files to add, use MutableTree.add::
114
wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/brztesttree/filec.txt'])
120
You can remove multiple files at once. The file paths need to be relative
123
wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
126
By default, the files are not deleted, just removed from the inventory.
127
To delete them from the filesystem as well::
129
wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
135
You can rename one file to a different name using WorkingTree.rename_one.
136
You just provide the old and new names, eg::
138
wt.rename_one('oldfile.txt','newfile.txt')
144
You can move multiple files from one directory into another using
147
wt.move(['olddir/file.txt'], 'newdir')
150
More complicated renames/moves can be done with transform.TreeTransform,
151
which is outside the scope of this document.
157
To commit _all_ the changes to our working tree we can just call the
158
WorkingTree's commit method, giving it a commit message, eg::
160
wt.commit('this is my commit message')
163
To commit only certain files, we need to provide a list of filenames which we
164
want committing, eg::
166
wt.commit(message='this is my commit message', specific_files=['fileA.txt',
167
'dir2/fileB.txt', 'fileD.txt'])
170
Generating a Log for a File
171
===========================
173
Generating a log is, in itself, simple. Grab a branch (see below) and pass
174
it to show_log together with a log formatter, eg::
176
from breezy import log
177
from breezy import branch
179
b = branch.Branch.open('/path/to/bazaar/branch')
180
lf = log.LongLogFormatter(to_file=sys.stdout)
184
Three log formatters are included with breezy: LongLogFormatter,
185
ShortLogFormatter and LineLogFormatter. These provide long, short and
186
single-line log output formats. It's also possible to write your own in
192
To annotate a file, we want to walk every line of a file, retrieving the
193
revision which last modified/created that line and then retrieving the
194
information for that revision.
196
First we get an annotation iterator for the file we are interested in::
198
tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
199
fileid = tree.path2id(relpath)
200
annotation = list(tree.annotate_iter(fileid))
203
To avoid repeatedly retrieving the same revisions we grab all revisions
204
associated with the file at once and build up a map of id to revision
205
information. We also build an map of revision numbers, again indexed
208
revision_ids = set(revision_id for revision_id, text in annotation)
209
revisions = tree.branch.repository.get_revisions(revision_ids)
210
revision_map = dict(izip(revision_ids, revisions))
211
revno_map = tree.branch.get_revision_id_to_revno_map()
214
Finally, we use our annotation iterator to walk the lines of the file,
215
displaying the information from our revision maps as we go::
217
for revision_id, text in annotation :
218
rev = revision_map[revision_id]
219
revno = revno_map[revision_id]
220
revno_string = '.'.join(str(i) for i in revno)
221
print "%s, %s: %s" % (revno_string, rev.committer, text)
224
Working with branches
225
=====================
227
To work with a branch you need a branch object, created from your branch::
229
from breezy import branch
231
b = branch.Branch.open('/home/jebw/brztest')
234
Branching from an existing branch
235
---------------------------------
237
To branch you create a branch object representing the branch you are
238
branching from, and supply a path/url to the new branch location.
239
The following code clones the brz trunk branch (the latest copy of the Breezy
240
source code) - be warned it has to download 60meg so takes a while to run
243
from breezy import branch
245
b = branch.Branch.open('bzr+ssh://bazaar.launchpad.net/+branch/brz/')
246
nb = b.controldir.sprout('/tmp/newBrzBzranch').open_branch()
249
This provides no feedback, since Breezy automatically uses the 'silent' UI.
252
Pushing and pulling branches
253
----------------------------
255
To push a branch you need to open the source and destination branches, then
256
just call push with the other branch as a parameter::
258
from breezy import branch
260
b1 = branch.Branch.open('file:///home/user/mybranch')
261
b2 = branch.Branch.open('bzr+ssh://bazaar.launchpad.net/+branch/brz/')
265
Pulling is much the same::
270
If you have a working tree, as well as a branch, you should use
271
WorkingTree.pull, not Branch.pull.
273
This won't handle conflicts automatically though, so any conflicts will be
274
left in the working tree for the user to resolve.
277
Checkout from an existing branch
278
================================
280
This performs a Lightweight checkout from an existing Branch::
282
from breezy import bzrdir
284
accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
285
source.create_checkout('/tmp/newBrzCheckout', None, True, accelerator_tree)
288
To make a heavyweight checkout, change the last line to::
290
source.create_checkout('/tmp/newBrzCheckout', None, False, accelerator_tree
296
Finding the last revision number or id
297
--------------------------------------
299
To get the last revision number and id of a branch use::
301
revision_number, revision_id = branch.last_revision_info()
304
If all you care about is the revision_id there is also the
307
revision_id = branch.last_revision()
310
Getting the list of revision ids that make up a branch
311
------------------------------------------------------
313
IMPORTANT: This should be avoided wherever possible, as it scales with the
316
revisions = branch.revision_history()
318
now revisions[0] is the revision id of the first commit, and revs[-1] is the
319
revision id of the most recent. Note that if all you want is the last
320
revision then you should use branch.last_revision() as described above, as
321
it is vastly more efficient.
324
Getting a Revision object from a revision id
325
--------------------------------------------
327
The Revision object has attributes like "message" to get the information
330
repo = branch.repository
331
revision = repo.get_revision(rev_id)
334
Accessing the files from a revision
335
-----------------------------------
337
To get the file contents and tree shape for a specific revision you need
338
a RevisionTree. These are supplied by the repository for a specific
341
revtree = repo.revision_tree(rev_id)
343
RevisionTrees, like all trees, can be compared as described in "Comparing
346
The most common way to list files in a tree is ``Tree.iter_entries()``.
347
The simplest way to get file content is ``Tree.get_file()``. The best way
348
to retrieve file content for large numbers of files `Tree.iter_files_bytes()``