1
 
=======================
 
2
 
Integrating with Bazaar
 
3
 
=======================
 
5
 
This page should hopefully become a quick guide to integrating other
 
6
 
(Python-based) software with Bazaar.
 
8
 
Manipulating the Working Tree
 
9
 
=============================
 
10
 
Most objects in Bazaar are in files, named after the class they contain.
 
11
 
To manipulate the Working Tree we need a valid WorkingTree object, which
 
12
 
is loaded from the workingtree.py file, eg::
 
14
 
  from bzrlib import workingtree
 
15
 
  wt = workingtree.WorkingTree.open('/home/jebw/bzrtest')
 
18
 
This gives us a WorkingTree object, which has various methods spread over
 
19
 
itself, and its parent classes MutableTree and Tree - its worth having a
 
20
 
look through these three files (workingtree.py, mutabletree.py and tree.py)
 
21
 
to see which methods are available.
 
25
 
There are two methods for comparing trees: ``changes_from`` and
 
26
 
``iter_changes``.  ``iter_changes`` is more regular and precise, but it is
 
27
 
somewhat harder to work with.  See the API documentation for more details.
 
29
 
``changes_from`` creates a Delta object showing changes::
 
31
 
  from bzrlib import delta
 
32
 
  changes = wt.changes_from(wt.basis_tree())
 
34
 
This gives us a Delta object, which has several lists of files for each type of
 
35
 
change, eg changes.added is a list of added files, changes.removed is list
 
36
 
of removed files, changes.modified is a list of modified files. The contents
 
37
 
of the lists aren't just filenames, but include other information as well.
 
38
 
To grab just the filename we want the first value, eg::
 
40
 
  print("list of newly added files")
 
41
 
  for filename in changes.added:
 
42
 
    print("%s has been added" % filename[0])
 
45
 
The exception to this is changes.renamed, where the list returned for each
 
46
 
renamed files contains both the old and new names -- one or both may interest
 
47
 
you, depending on what you're doing.
 
51
 
  print("list of renamed files")
 
52
 
  for filename in changes.renamed:
 
53
 
    print("%s has been renamed to %s" % (filename[0], filename[1]))
 
59
 
If you want to add files the same way ``bzr add`` does, you can use
 
60
 
MutableTree.smart_add.  By default, this is recursive. Paths can either be
 
61
 
absolute or relative to the workingtree::
 
63
 
  wt.smart_add(['dir1/filea.txt', 'fileb.txt',
 
64
 
                '/home/jebw/bzrtesttree/filec.txt'])
 
67
 
For more precise control over which files to add, use MutableTree.add::
 
69
 
  wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/bzrtesttree/filec.txt'])
 
75
 
You can remove multiple files at once.  The file paths need to be relative
 
78
 
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
 
81
 
By default, the files are not deleted, just removed from the inventory.
 
82
 
To delete them from the filesystem as well::
 
84
 
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
 
90
 
You can rename one file to a different name using WorkingTree.rename_one.
 
91
 
You just provide the old and new names, eg::
 
93
 
  wt.rename_one('oldfile.txt','newfile.txt')
 
99
 
You can move multiple files from one directory into another using
 
102
 
  wt.move(['olddir/file.txt'], 'newdir')
 
105
 
More complicated renames/moves can be done with transform.TreeTransform,
 
106
 
which is outside the scope of this document.
 
112
 
To commit _all_ the changes to our working tree we can just call the
 
113
 
WorkingTree's commit method, giving it a commit message, eg::
 
115
 
  wt.commit('this is my commit message')
 
118
 
To commit only certain files, we need to provide a list of filenames which we
 
119
 
want committing, eg::
 
121
 
  wt.commit(message='this is my commit message', specific_files=['fileA.txt',
 
122
 
            'dir2/fileB.txt', 'fileD.txt'])
 
125
 
Generating a Log for a File
 
126
 
===========================
 
128
 
Generating a log is, in itself, simple.  Grab a branch (see below) and pass
 
129
 
it to show_log together with a log formatter, eg::
 
131
 
  from bzrlib import log
 
132
 
  from bzrlib import branch
 
134
 
  b = branch.Branch.open('/path/to/bazaar/branch')
 
135
 
  lf = log.LongLogFormatter(to_file=sys.stdout)
 
139
 
Three log formatters are included with bzrlib: LongLogFormatter,
 
140
 
ShortLogFormatter and LineLogFormatter.  These provide long, short and
 
141
 
single-line log output formats. It's also possible to write your own in
 
147
 
To annotate a file, we want to walk every line of a file, retrieving the
 
148
 
revision which last modified/created that line and then retrieving the
 
149
 
information for that revision.
 
151
 
First we get an annotation iterator for the file we are interested in::
 
153
 
  tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
 
154
 
  fileid = tree.path2id(relpath)
 
155
 
  annotation = list(tree.annotate_iter(fileid))
 
158
 
To avoid repeatedly retrieving the same revisions we grab all revisions
 
159
 
associated with the file at once and build up a map of id to revision
 
160
 
information. We also build an map of revision numbers, again indexed
 
163
 
  revision_ids = set(revision_id for revision_id, text in annotation)
 
164
 
  revisions = tree.branch.repository.get_revisions(revision_ids)
 
165
 
  revision_map = dict(izip(revision_ids, revisions))
 
166
 
  revno_map = tree.branch.get_revision_id_to_revno_map()
 
169
 
Finally, we use our annotation iterator to walk the lines of the file,
 
170
 
displaying the information from our revision maps as we go::
 
172
 
  for revision_id, text in annotation :
 
173
 
      rev = revision_map[revision_id]
 
174
 
      revno = revno_map[revision_id]
 
175
 
      revno_string = '.'.join(str(i) for i in revno)
 
176
 
      print "%s, %s: %s" % (revno_string, rev.committer, text)
 
179
 
Working with branches
 
180
 
=====================
 
182
 
To work with a branch you need a branch object, created from your branch::
 
184
 
  from bzrlib import branch
 
186
 
  b = branch.Branch.open('/home/jebw/bzrtest')
 
189
 
Branching from an existing branch
 
190
 
=================================
 
192
 
To branch you create a branch object representing the branch you are
 
193
 
branching from, and supply a path/url to the new branch location.
 
194
 
The following code clones the bzr.dev branch (the latest copy of the Bazaar
 
195
 
source code) - be warned it has to download 60meg so takes a while to run
 
198
 
  from bzrlib import branch
 
200
 
  b = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
201
 
  nb = b.bzrdir.sprout('/tmp/newBzrBranch').open_branch()
 
204
 
This provides no feedback, since Bazaar automatically uses the 'silent' UI.
 
207
 
Pushing and pulling branches
 
208
 
============================
 
210
 
To push a branch you need to open the source and destination branches, then
 
211
 
just call push with the other branch as a parameter::
 
213
 
  from bzrlib import branch
 
215
 
  b1 = branch.Branch.open('file:///home/user/mybranch')
 
216
 
  b2 = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
220
 
Pulling is much the same::
 
225
 
If you have a working tree, as well as a branch, you should use
 
226
 
WorkingTree.pull, not Branch.pull.
 
228
 
This won't handle conflicts automatically though, so any conflicts will be
 
229
 
left in the working tree for the user to resolve.
 
232
 
Checkout from an existing branch
 
233
 
================================
 
235
 
This performs a Lightweight checkout from an existing Branch::
 
237
 
  from bzrlib import bzrdir
 
239
 
  accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
 
240
 
  source.create_checkout('/tmp/newBzrCheckout', None, True, accelerator_tree)
 
243
 
To make a heavyweight checkout, change the last line to::
 
245
 
  source.create_checkout('/tmp/newBzrCheckout', None, False, accelerator_tree
 
251
 
Finding the last revision number or id
 
252
 
--------------------------------------
 
254
 
To get the last revision number and id of a branch use::
 
256
 
  revision_number, revision_id = branch.last_revision_info()
 
259
 
If all you care about is the revision_id there is also the
 
262
 
  revision_id = branch.last_revision()
 
265
 
Getting the list of revision ids that make up a branch
 
266
 
------------------------------------------------------
 
268
 
IMPORTANT: This should be avoided wherever possible, as it scales with the
 
271
 
  revisions = branch.revision_history()
 
273
 
now revisions[0] is the revision id of the first commit, and revs[-1] is the
 
274
 
revision id of the most recent. Note that if all you want is the last
 
275
 
revision then you should use branch.last_revision() as described above, as
 
276
 
it is vastly more efficient.
 
279
 
Getting a Revision object from a revision id
 
280
 
--------------------------------------------
 
282
 
The Revision object has attributes like "message" to get the information
 
285
 
  repo = branch.repository
 
286
 
  revision = repo.get_revision(rev_id)
 
289
 
Accessing the files from a revision
 
290
 
-----------------------------------
 
292
 
To get the file contents and tree shape for a specific revision you need
 
293
 
a RevisionTree. These are supplied by the repository for a specific
 
296
 
  revtree = repo.revision_tree(rev_id)
 
298
 
RevisionTrees, like all trees, can be compared as described in "Comparing
 
301
 
The most common way to list files in a tree is ``Tree.iter_entries()``.
 
302
 
The simplest way to get file content is ``Tree.get_file()``.  The best way
 
303
 
to retrieve file content for large numbers of files `Tree.iter_files_bytes()``