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