~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Martin Pool
  • Date: 2009-06-19 06:21:13 UTC
  • mto: This revision was merged to the branch mainline in revision 4558.
  • Revision ID: mbp@sourcefrog.net-20090619062113-019bp0a3bl2y4nkx
Un-soft-deprecate _supports_progress - still useful

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
=======================
 
2
Integrating with Bazaar
 
3
=======================
 
4
 
 
5
This page should hopefully become a quick guide to integrating other
 
6
(Python-based) software with Bazaar.
 
7
 
 
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::
 
13
 
 
14
  from bzrlib import workingtree
 
15
  wt = workingtree.WorkingTree.open('/home/jebw/bzrtest')
 
16
 
 
17
 
 
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.
 
22
 
 
23
Compare trees
 
24
===============
 
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.
 
28
 
 
29
``changes_from`` creates a Delta object showing changes::
 
30
 
 
31
  from bzrlib import delta
 
32
  changes = wt.changes_from(wt.basis_tree())
 
33
 
 
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::
 
39
 
 
40
  print("list of newly added files")
 
41
  for filename in changes.added:
 
42
    print("%s has been added" % filename[0])
 
43
 
 
44
 
 
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.
 
48
 
 
49
For example::
 
50
 
 
51
  print("list of renamed files")
 
52
  for filename in changes.renamed:
 
53
    print("%s has been renamed to %s" % (filename[0], filename[1]))
 
54
 
 
55
 
 
56
Adding Files
 
57
============
 
58
 
 
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::
 
62
 
 
63
  wt.smart_add(['dir1/filea.txt', 'fileb.txt',
 
64
                '/home/jebw/bzrtesttree/filec.txt'])
 
65
 
 
66
 
 
67
For more precise control over which files to add, use MutableTree.add::
 
68
 
 
69
  wt.add(['dir1/filea.txt', 'fileb.txt', '/home/jebw/bzrtesttree/filec.txt'])
 
70
 
 
71
 
 
72
Removing Files
 
73
==============
 
74
 
 
75
You can remove multiple files at once.  The file paths need to be relative
 
76
to the workingtree::
 
77
 
 
78
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'])
 
79
 
 
80
 
 
81
By default, the files are not deleted, just removed from the inventory.
 
82
To delete them from the filesystem as well::
 
83
 
 
84
  wt.remove(['filea.txt', 'fileb.txt', 'dir1'], keep_files=False)
 
85
 
 
86
 
 
87
Renaming a File
 
88
===============
 
89
 
 
90
You can rename one file to a different name using WorkingTree.rename_one.
 
91
You just provide the old and new names, eg::
 
92
 
 
93
  wt.rename_one('oldfile.txt','newfile.txt')
 
94
 
 
95
 
 
96
Moving Files
 
97
============
 
98
 
 
99
You can move multiple files from one directory into another using
 
100
WorkingTree.move::
 
101
 
 
102
  wt.move(['olddir/file.txt'], 'newdir')
 
103
 
 
104
 
 
105
More complicated renames/moves can be done with transform.TreeTransform,
 
106
which is outside the scope of this document.
 
107
 
 
108
 
 
109
Committing Changes
 
110
==================
 
111
 
 
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::
 
114
 
 
115
  wt.commit('this is my commit message')
 
116
 
 
117
 
 
118
To commit only certain files, we need to provide a list of filenames which we
 
119
want committing, eg::
 
120
 
 
121
  wt.commit(message='this is my commit message', specific_files=['fileA.txt',
 
122
            'dir2/fileB.txt', 'fileD.txt'])
 
123
 
 
124
 
 
125
Generating a Log for a File
 
126
===========================
 
127
 
 
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::
 
130
 
 
131
  from bzrlib import log
 
132
  from bzrlib import branch
 
133
 
 
134
  b = branch.Branch.open('/path/to/bazaar/branch')
 
135
  lf = log.LongLogFormatter(to_file=sys.stdout)
 
136
  log.show_log(b, lf)
 
137
 
 
138
 
 
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
 
142
very little code.
 
143
 
 
144
Annotating a File
 
145
=================
 
146
 
 
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.
 
150
 
 
151
First we get an annotation iterator for the file we are interested in::
 
152
 
 
153
  tree, relpath = workingtree.WorkingTree.open_containing('/path/to/file.txt')
 
154
  fileid = tree.path2id(relpath)
 
155
  annotation = list(tree.annotate_iter(fileid))
 
156
 
 
157
 
 
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
 
161
by the revision id::
 
162
 
 
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()
 
167
 
 
168
 
 
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::
 
171
 
 
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)
 
177
 
 
178
 
 
179
Working with branches
 
180
=====================
 
181
 
 
182
To work with a branch you need a branch object, created from your branch::
 
183
 
 
184
  from bzrlib import branch
 
185
 
 
186
  b = branch.Branch.open('/home/jebw/bzrtest')
 
187
 
 
188
 
 
189
Branching from an existing branch
 
190
=================================
 
191
 
 
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
 
196
with no feedback::
 
197
 
 
198
  from bzrlib import branch
 
199
 
 
200
  b = branch.Branch.open('http://bazaar-vcs.org/bzr/bzr.dev')
 
201
  nb = b.bzrdir.sprout('/tmp/newBzrBranch').open_branch()
 
202
 
 
203
 
 
204
This provides no feedback, since Bazaar automatically uses the 'silent' UI.
 
205
 
 
206
 
 
207
Pushing and pulling branches
 
208
============================
 
209
 
 
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::
 
212
 
 
213
  from bzrlib import branch
 
214
 
 
215
  b1 = branch.Branch.open('file:///home/user/mybranch')
 
216
  b2 = branch.Branch.open('http://bazaar-vcs.org/bzr/bzr.dev')
 
217
  b1.push(b2)
 
218
 
 
219
 
 
220
Pulling is much the same::
 
221
 
 
222
  b1.pull(b2)
 
223
 
 
224
 
 
225
If you have a working tree, as well as a branch, you should use
 
226
WorkingTree.pull, not Branch.pull.
 
227
 
 
228
This won't handle conflicts automatically though, so any conflicts will be
 
229
left in the working tree for the user to resolve.
 
230
 
 
231
 
 
232
Checkout from an existing branch
 
233
================================
 
234
 
 
235
This performs a Lightweight checkout from an existing Branch::
 
236
 
 
237
  from bzrlib import bzrdir
 
238
 
 
239
  accelerator_tree, source = bzrdir.BzrDir.open_tree_or_branch('http:URL')
 
240
  source.create_checkout('/tmp/newBzrCheckout', None, True, accelerator_tree)
 
241
 
 
242
 
 
243
To make a heavyweight checkout, change the last line to::
 
244
 
 
245
  source.create_checkout('/tmp/newBzrCheckout', None, False, accelerator_tree
 
246
 
 
247
==================
 
248
History Operations
 
249
==================
 
250
 
 
251
Finding the last revision number or id
 
252
======================================
 
253
 
 
254
To get the last revision number and id of a branch use::
 
255
 
 
256
  revision_number, revision_id = branch.last_revision_info()
 
257
 
 
258
 
 
259
If all you care about is the revision_id there is also the
 
260
method::
 
261
 
 
262
  revision_id = branch.last_revision()
 
263
 
 
264
 
 
265
Getting the list of revision ids that make up a branch
 
266
======================================================
 
267
 
 
268
IMPORTANT: This should be avoided wherever possible, as it scales with the
 
269
length of history::
 
270
 
 
271
  revisions = branch.revision_history()
 
272
 
 
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.
 
277
 
 
278
 
 
279
Getting a Revision object from a revision id
 
280
============================================
 
281
 
 
282
The Revision object has attributes like "message" to get the information
 
283
about the revision::
 
284
 
 
285
  repo = branch.repository
 
286
  revision = repo.get_revision(rev_id)
 
287
 
 
288
 
 
289
Accessing the files from a revision
 
290
===================================
 
291
 
 
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
 
294
revision id::
 
295
 
 
296
  revtree = repo.revision_tree(rev_id)
 
297
 
 
298
RevisionTrees, like all trees, can be compared as described in "Comparing
 
299
Trees" above.
 
300
 
 
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()``
 
304