/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to doc/developers/integration.txt

  • Committer: Jelmer Vernooij
  • Date: 2020-05-24 00:39:50 UTC
  • mto: This revision was merged to the branch mainline in revision 7504.
  • Revision ID: jelmer@jelmer.uk-20200524003950-bbc545r76vc5yajg
Add github action.

Show diffs side-by-side

added added

removed removed

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