~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Canonical.com Patch Queue Manager
  • Date: 2010-09-29 22:03:03 UTC
  • mfrom: (5416.2.6 jam-integration)
  • Revision ID: pqm@pqm.ubuntu.com-20100929220303-cr95h8iwtggco721
(mbp) Add 'break-lock --force'

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