~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Jelmer Vernooij
  • Date: 2011-03-12 16:55:08 UTC
  • mto: This revision was merged to the branch mainline in revision 5737.
  • Revision ID: jelmer@samba.org-20110312165508-8aerwldchcf1n2ah
Test default control component format implementation.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
=======================
 
2
Integrating with Bazaar
 
3
=======================
 
4
 
 
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
 
9
here, just ask us.
 
10
 
 
11
 
 
12
 
 
13
 
 
14
Starting with bzrlib
 
15
====================
 
16
 
 
17
Within bzr
 
18
----------
 
19
 
 
20
When using bzrlib within the ``bzr`` program (for instance as a bzr
 
21
plugin), bzrlib's global state is already available for use.
 
22
 
 
23
From outside bzr
 
24
----------------
 
25
 
 
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``. This returns a context manager within which bzrlib
 
31
functions will work correctly. See the pydoc for ``bzrlib.initialize`` for
 
32
more information. In Python 2.4 the ``with`` keyword is not supported and
 
33
so you need to use the context manager manually::
 
34
 
 
35
  # This sets up your ~/.bzr.log, ui factory and so on and so forth. It is
 
36
  # not safe to use as a doctest.
 
37
  library_state = bzrlib.initialize()
 
38
  library_state.__enter__()
 
39
  try:
 
40
      pass
 
41
      # do stuff here
 
42
  finally:
 
43
      library_state.__exit__(None, None, None)
 
44
 
 
45
 
 
46
Running bzr commands
 
47
====================
 
48
 
 
49
To run command-line commands in-process::
 
50
 
 
51
  from bzrlib.commands import get_command
 
52
  
 
53
  cmd = get_command('version')
 
54
  cmd.run([])
 
55
  
 
56
This will send output through the current UIFactory; you can redirect this
 
57
elsewhere through the parameters to `bzrlib.initialize`.
 
58
 
 
59
 
 
60
Manipulating the Working Tree
 
61
=============================
 
62
Most objects in Bazaar are in files, named after the class they contain.
 
63
To manipulate the Working Tree we need a valid WorkingTree object, which
 
64
is loaded from the workingtree.py file, eg::
 
65
 
 
66
  from bzrlib import workingtree
 
67
  wt = workingtree.WorkingTree.open('/home/jebw/bzrtest')
 
68
 
 
69
 
 
70
This gives us a WorkingTree object, which has various methods spread over
 
71
itself, and its parent classes MutableTree and Tree - it's worth having a
 
72
look through these three files (workingtree.py, mutabletree.py and tree.py)
 
73
to see which methods are available.
 
74
 
 
75
Compare trees
 
76
-------------
 
77
 
 
78
There are two methods for comparing trees: ``changes_from`` and
 
79
``iter_changes``.  ``iter_changes`` is more regular and precise, but it is
 
80
somewhat harder to work with.  See the API documentation for more details.
 
81
 
 
82
``changes_from`` creates a Delta object showing changes::
 
83
 
 
84
  from bzrlib import delta
 
85
  changes = wt.changes_from(wt.basis_tree())
 
86
 
 
87
This gives us a Delta object, which has several lists of files for each type of
 
88
change, eg changes.added is a list of added files, changes.removed is list
 
89
of removed files, changes.modified is a list of modified files. The contents
 
90
of the lists aren't just filenames, but include other information as well.
 
91
To grab just the filename we want the first value, eg::
 
92
 
 
93
  print("list of newly added files")
 
94
  for filename in changes.added:
 
95
    print("%s has been added" % filename[0])
 
96
 
 
97
 
 
98
The exception to this is changes.renamed, where the list returned for each
 
99
renamed files contains both the old and new names -- one or both may interest
 
100
you, depending on what you're doing.
 
101
 
 
102
For example::
 
103
 
 
104
  print("list of renamed files")
 
105
  for filename in changes.renamed:
 
106
    print("%s has been renamed to %s" % (filename[0], filename[1]))
 
107
 
 
108
 
 
109
Adding Files
 
110
------------
 
111
 
 
112
If you want to add files the same way ``bzr add`` does, you can use
 
113
MutableTree.smart_add.  By default, this is recursive. Paths can either be
 
114
absolute or relative to the workingtree::
 
115
 
 
116
  wt.smart_add(['dir1/filea.txt', 'fileb.txt',
 
117
                '/home/jebw/bzrtesttree/filec.txt'])
 
118
 
 
119
 
 
120
For more precise control over which files to add, use MutableTree.add::
 
121
 
 
122
  wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/bzrtesttree/filec.txt'])
 
123
 
 
124
 
 
125
Removing Files
 
126
--------------
 
127
 
 
128
You can remove multiple files at once.  The file paths need to be relative
 
129
to the workingtree::
 
130
 
 
131
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
 
132
 
 
133
 
 
134
By default, the files are not deleted, just removed from the inventory.
 
135
To delete them from the filesystem as well::
 
136
 
 
137
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
 
138
 
 
139
 
 
140
Renaming a File
 
141
---------------
 
142
 
 
143
You can rename one file to a different name using WorkingTree.rename_one.
 
144
You just provide the old and new names, eg::
 
145
 
 
146
  wt.rename_one('oldfile.txt','newfile.txt')
 
147
 
 
148
 
 
149
Moving Files
 
150
------------
 
151
 
 
152
You can move multiple files from one directory into another using
 
153
WorkingTree.move::
 
154
 
 
155
  wt.move(['olddir/file.txt'], 'newdir')
 
156
 
 
157
 
 
158
More complicated renames/moves can be done with transform.TreeTransform,
 
159
which is outside the scope of this document.
 
160
 
 
161
 
 
162
Committing Changes
 
163
------------------
 
164
 
 
165
To commit _all_ the changes to our working tree we can just call the
 
166
WorkingTree's commit method, giving it a commit message, eg::
 
167
 
 
168
  wt.commit('this is my commit message')
 
169
 
 
170
 
 
171
To commit only certain files, we need to provide a list of filenames which we
 
172
want committing, eg::
 
173
 
 
174
  wt.commit(message='this is my commit message', specific_files=['fileA.txt',
 
175
            'dir2/fileB.txt', 'fileD.txt'])
 
176
 
 
177
 
 
178
Generating a Log for a File
 
179
===========================
 
180
 
 
181
Generating a log is, in itself, simple.  Grab a branch (see below) and pass
 
182
it to show_log together with a log formatter, eg::
 
183
 
 
184
  from bzrlib import log
 
185
  from bzrlib import branch
 
186
 
 
187
  b = branch.Branch.open('/path/to/bazaar/branch')
 
188
  lf = log.LongLogFormatter(to_file=sys.stdout)
 
189
  log.show_log(b, lf)
 
190
 
 
191
 
 
192
Three log formatters are included with bzrlib: LongLogFormatter,
 
193
ShortLogFormatter and LineLogFormatter.  These provide long, short and
 
194
single-line log output formats. It's also possible to write your own in
 
195
very little code.
 
196
 
 
197
Annotating a File
 
198
=================
 
199
 
 
200
To annotate a file, we want to walk every line of a file, retrieving the
 
201
revision which last modified/created that line and then retrieving the
 
202
information for that revision.
 
203
 
 
204
First we get an annotation iterator for the file we are interested in::
 
205
 
 
206
  tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
 
207
  fileid = tree.path2id(relpath)
 
208
  annotation = list(tree.annotate_iter(fileid))
 
209
 
 
210
 
 
211
To avoid repeatedly retrieving the same revisions we grab all revisions
 
212
associated with the file at once and build up a map of id to revision
 
213
information. We also build an map of revision numbers, again indexed
 
214
by the revision id::
 
215
 
 
216
  revision_ids = set(revision_id for revision_id, text in annotation)
 
217
  revisions = tree.branch.repository.get_revisions(revision_ids)
 
218
  revision_map = dict(izip(revision_ids, revisions))
 
219
  revno_map = tree.branch.get_revision_id_to_revno_map()
 
220
 
 
221
 
 
222
Finally, we use our annotation iterator to walk the lines of the file,
 
223
displaying the information from our revision maps as we go::
 
224
 
 
225
  for revision_id, text in annotation :
 
226
      rev = revision_map[revision_id]
 
227
      revno = revno_map[revision_id]
 
228
      revno_string = '.'.join(str(i) for i in revno)
 
229
      print "%s, %s: %s" % (revno_string, rev.committer, text)
 
230
 
 
231
 
 
232
Working with branches
 
233
=====================
 
234
 
 
235
To work with a branch you need a branch object, created from your branch::
 
236
 
 
237
  from bzrlib import branch
 
238
 
 
239
  b = branch.Branch.open('/home/jebw/bzrtest')
 
240
 
 
241
 
 
242
Branching from an existing branch
 
243
---------------------------------
 
244
 
 
245
To branch you create a branch object representing the branch you are
 
246
branching from, and supply a path/url to the new branch location.
 
247
The following code clones the bzr.dev branch (the latest copy of the Bazaar
 
248
source code) - be warned it has to download 60meg so takes a while to run
 
249
with no feedback::
 
250
 
 
251
  from bzrlib import branch
 
252
 
 
253
  b = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
254
  nb = b.bzrdir.sprout('/tmp/newBzrBranch').open_branch()
 
255
 
 
256
 
 
257
This provides no feedback, since Bazaar automatically uses the 'silent' UI.
 
258
 
 
259
 
 
260
Pushing and pulling branches
 
261
----------------------------
 
262
 
 
263
To push a branch you need to open the source and destination branches, then
 
264
just call push with the other branch as a parameter::
 
265
 
 
266
  from bzrlib import branch
 
267
 
 
268
  b1 = branch.Branch.open('file:///home/user/mybranch')
 
269
  b2 = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
270
  b1.push(b2)
 
271
 
 
272
 
 
273
Pulling is much the same::
 
274
 
 
275
  b1.pull(b2)
 
276
 
 
277
 
 
278
If you have a working tree, as well as a branch, you should use
 
279
WorkingTree.pull, not Branch.pull.
 
280
 
 
281
This won't handle conflicts automatically though, so any conflicts will be
 
282
left in the working tree for the user to resolve.
 
283
 
 
284
 
 
285
Checkout from an existing branch
 
286
================================
 
287
 
 
288
This performs a Lightweight checkout from an existing Branch::
 
289
 
 
290
  from bzrlib import bzrdir
 
291
 
 
292
  accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
 
293
  source.create_checkout('/tmp/newBzrCheckout', None, True, accelerator_tree)
 
294
 
 
295
 
 
296
To make a heavyweight checkout, change the last line to::
 
297
 
 
298
  source.create_checkout('/tmp/newBzrCheckout', None, False, accelerator_tree
 
299
 
 
300
 
 
301
History Operations
 
302
==================
 
303
 
 
304
Finding the last revision number or id
 
305
--------------------------------------
 
306
 
 
307
To get the last revision number and id of a branch use::
 
308
 
 
309
  revision_number, revision_id = branch.last_revision_info()
 
310
 
 
311
 
 
312
If all you care about is the revision_id there is also the
 
313
method::
 
314
 
 
315
  revision_id = branch.last_revision()
 
316
 
 
317
 
 
318
Getting the list of revision ids that make up a branch
 
319
------------------------------------------------------
 
320
 
 
321
IMPORTANT: This should be avoided wherever possible, as it scales with the
 
322
length of history::
 
323
 
 
324
  revisions = branch.revision_history()
 
325
 
 
326
now revisions[0] is the revision id of the first commit, and revs[-1] is the
 
327
revision id of the most recent. Note that if all you want is the last
 
328
revision then you should use branch.last_revision() as described above, as
 
329
it is vastly more efficient.
 
330
 
 
331
 
 
332
Getting a Revision object from a revision id
 
333
--------------------------------------------
 
334
 
 
335
The Revision object has attributes like "message" to get the information
 
336
about the revision::
 
337
 
 
338
  repo = branch.repository
 
339
  revision = repo.get_revision(rev_id)
 
340
 
 
341
 
 
342
Accessing the files from a revision
 
343
-----------------------------------
 
344
 
 
345
To get the file contents and tree shape for a specific revision you need
 
346
a RevisionTree. These are supplied by the repository for a specific
 
347
revision id::
 
348
 
 
349
  revtree = repo.revision_tree(rev_id)
 
350
 
 
351
RevisionTrees, like all trees, can be compared as described in "Comparing
 
352
Trees" above.
 
353
 
 
354
The most common way to list files in a tree is ``Tree.iter_entries()``.
 
355
The simplest way to get file content is ``Tree.get_file()``.  The best way
 
356
to retrieve file content for large numbers of files `Tree.iter_files_bytes()``
 
357