~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Martin Pool
  • Date: 2005-06-17 07:34:04 UTC
  • Revision ID: mbp@sourcefrog.net-20050617073404-7c914dc1bd15f38a
- don't display progress bars on really dumb terminals

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