~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Martin Packman
  • Date: 2011-11-17 13:45:49 UTC
  • mto: This revision was merged to the branch mainline in revision 6271.
  • Revision ID: martin.packman@canonical.com-20111117134549-080e1fhtrzoicexg
Only assert FileExists path in test_transform directory clash tests to avoid stringification fallout

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``. 
 
31
 
 
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``.)
 
36
 
 
37
In Python 2.4 the ``with`` keyword is not supported and
 
38
so you need to use the context manager manually::
 
39
 
 
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__()
 
44
  try:
 
45
      pass
 
46
      # do stuff here
 
47
  finally:
 
48
      library_state.__exit__(None, None, None)
 
49
 
 
50
 
 
51
Running bzr commands
 
52
====================
 
53
 
 
54
To run command-line commands in-process::
 
55
 
 
56
  from bzrlib.commands import get_command
 
57
  
 
58
  cmd = get_command('version')
 
59
  cmd.run([])
 
60
  
 
61
This will send output through the current UIFactory; you can redirect this
 
62
elsewhere through the parameters to `bzrlib.initialize`.
 
63
 
 
64
 
 
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::
 
70
 
 
71
  from bzrlib import workingtree
 
72
  wt = workingtree.WorkingTree.open('/home/jebw/bzrtest')
 
73
 
 
74
 
 
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.
 
79
 
 
80
Compare trees
 
81
-------------
 
82
 
 
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.
 
86
 
 
87
``changes_from`` creates a Delta object showing changes::
 
88
 
 
89
  from bzrlib import delta
 
90
  changes = wt.changes_from(wt.basis_tree())
 
91
 
 
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::
 
97
 
 
98
  print("list of newly added files")
 
99
  for filename in changes.added:
 
100
    print("%s has been added" % filename[0])
 
101
 
 
102
 
 
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.
 
106
 
 
107
For example::
 
108
 
 
109
  print("list of renamed files")
 
110
  for filename in changes.renamed:
 
111
    print("%s has been renamed to %s" % (filename[0], filename[1]))
 
112
 
 
113
 
 
114
Adding Files
 
115
------------
 
116
 
 
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::
 
120
 
 
121
  wt.smart_add(['dir1/filea.txt', 'fileb.txt',
 
122
                '/home/jebw/bzrtesttree/filec.txt'])
 
123
 
 
124
 
 
125
For more precise control over which files to add, use MutableTree.add::
 
126
 
 
127
  wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/bzrtesttree/filec.txt'])
 
128
 
 
129
 
 
130
Removing Files
 
131
--------------
 
132
 
 
133
You can remove multiple files at once.  The file paths need to be relative
 
134
to the workingtree::
 
135
 
 
136
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
 
137
 
 
138
 
 
139
By default, the files are not deleted, just removed from the inventory.
 
140
To delete them from the filesystem as well::
 
141
 
 
142
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
 
143
 
 
144
 
 
145
Renaming a File
 
146
---------------
 
147
 
 
148
You can rename one file to a different name using WorkingTree.rename_one.
 
149
You just provide the old and new names, eg::
 
150
 
 
151
  wt.rename_one('oldfile.txt','newfile.txt')
 
152
 
 
153
 
 
154
Moving Files
 
155
------------
 
156
 
 
157
You can move multiple files from one directory into another using
 
158
WorkingTree.move::
 
159
 
 
160
  wt.move(['olddir/file.txt'], 'newdir')
 
161
 
 
162
 
 
163
More complicated renames/moves can be done with transform.TreeTransform,
 
164
which is outside the scope of this document.
 
165
 
 
166
 
 
167
Committing Changes
 
168
------------------
 
169
 
 
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::
 
172
 
 
173
  wt.commit('this is my commit message')
 
174
 
 
175
 
 
176
To commit only certain files, we need to provide a list of filenames which we
 
177
want committing, eg::
 
178
 
 
179
  wt.commit(message='this is my commit message', specific_files=['fileA.txt',
 
180
            'dir2/fileB.txt', 'fileD.txt'])
 
181
 
 
182
 
 
183
Generating a Log for a File
 
184
===========================
 
185
 
 
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::
 
188
 
 
189
  from bzrlib import log
 
190
  from bzrlib import branch
 
191
 
 
192
  b = branch.Branch.open('/path/to/bazaar/branch')
 
193
  lf = log.LongLogFormatter(to_file=sys.stdout)
 
194
  log.show_log(b, lf)
 
195
 
 
196
 
 
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
 
200
very little code.
 
201
 
 
202
Annotating a File
 
203
=================
 
204
 
 
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.
 
208
 
 
209
First we get an annotation iterator for the file we are interested in::
 
210
 
 
211
  tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
 
212
  fileid = tree.path2id(relpath)
 
213
  annotation = list(tree.annotate_iter(fileid))
 
214
 
 
215
 
 
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
 
219
by the revision id::
 
220
 
 
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()
 
225
 
 
226
 
 
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::
 
229
 
 
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)
 
235
 
 
236
 
 
237
Working with branches
 
238
=====================
 
239
 
 
240
To work with a branch you need a branch object, created from your branch::
 
241
 
 
242
  from bzrlib import branch
 
243
 
 
244
  b = branch.Branch.open('/home/jebw/bzrtest')
 
245
 
 
246
 
 
247
Branching from an existing branch
 
248
---------------------------------
 
249
 
 
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
 
254
with no feedback::
 
255
 
 
256
  from bzrlib import branch
 
257
 
 
258
  b = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
259
  nb = b.bzrdir.sprout('/tmp/newBzrBranch').open_branch()
 
260
 
 
261
 
 
262
This provides no feedback, since Bazaar automatically uses the 'silent' UI.
 
263
 
 
264
 
 
265
Pushing and pulling branches
 
266
----------------------------
 
267
 
 
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::
 
270
 
 
271
  from bzrlib import branch
 
272
 
 
273
  b1 = branch.Branch.open('file:///home/user/mybranch')
 
274
  b2 = branch.Branch.open('http://bazaar.launchpad.net/~bzr-pqm/bzr/bzr.dev')
 
275
  b1.push(b2)
 
276
 
 
277
 
 
278
Pulling is much the same::
 
279
 
 
280
  b1.pull(b2)
 
281
 
 
282
 
 
283
If you have a working tree, as well as a branch, you should use
 
284
WorkingTree.pull, not Branch.pull.
 
285
 
 
286
This won't handle conflicts automatically though, so any conflicts will be
 
287
left in the working tree for the user to resolve.
 
288
 
 
289
 
 
290
Checkout from an existing branch
 
291
================================
 
292
 
 
293
This performs a Lightweight checkout from an existing Branch::
 
294
 
 
295
  from bzrlib import bzrdir
 
296
 
 
297
  accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
 
298
  source.create_checkout('/tmp/newBzrCheckout', None, True, accelerator_tree)
 
299
 
 
300
 
 
301
To make a heavyweight checkout, change the last line to::
 
302
 
 
303
  source.create_checkout('/tmp/newBzrCheckout', None, False, accelerator_tree
 
304
 
 
305
 
 
306
History Operations
 
307
==================
 
308
 
 
309
Finding the last revision number or id
 
310
--------------------------------------
 
311
 
 
312
To get the last revision number and id of a branch use::
 
313
 
 
314
  revision_number, revision_id = branch.last_revision_info()
 
315
 
 
316
 
 
317
If all you care about is the revision_id there is also the
 
318
method::
 
319
 
 
320
  revision_id = branch.last_revision()
 
321
 
 
322
 
 
323
Getting the list of revision ids that make up a branch
 
324
------------------------------------------------------
 
325
 
 
326
IMPORTANT: This should be avoided wherever possible, as it scales with the
 
327
length of history::
 
328
 
 
329
  revisions = branch.revision_history()
 
330
 
 
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.
 
335
 
 
336
 
 
337
Getting a Revision object from a revision id
 
338
--------------------------------------------
 
339
 
 
340
The Revision object has attributes like "message" to get the information
 
341
about the revision::
 
342
 
 
343
  repo = branch.repository
 
344
  revision = repo.get_revision(rev_id)
 
345
 
 
346
 
 
347
Accessing the files from a revision
 
348
-----------------------------------
 
349
 
 
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
 
352
revision id::
 
353
 
 
354
  revtree = repo.revision_tree(rev_id)
 
355
 
 
356
RevisionTrees, like all trees, can be compared as described in "Comparing
 
357
Trees" above.
 
358
 
 
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()``
 
362