~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Martin Pool
  • Date: 2005-05-05 07:00:55 UTC
  • Revision ID: mbp@sourcefrog.net-20050505070055-e1ef8f7dd14b48b1
- Fix up bzr log command

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.launchpad.net/~bzr-pqm/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.launchpad.net/~bzr-pqm/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