Merge lp:~maxb/bzr-builddeb/bzrtools-code-disentangle into lp:bzr-builddeb
- bzrtools-code-disentangle
- Merge into trunk
Status: | Merged |
---|---|
Merged at revision: | not available |
Proposed branch: | lp:~maxb/bzr-builddeb/bzrtools-code-disentangle |
Merge into: | lp:bzr-builddeb |
Diff against target: |
687 lines (+374/-229) 5 files modified
README (+1/-3) bzrtools_bzrtools.py (+30/-0) bzrtools_import.py (+340/-0) debian/control (+2/-2) import_dsc.py (+1/-224) |
To merge this branch: | bzr merge lp:~maxb/bzr-builddeb/bzrtools-code-disentangle |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Jelmer Vernooij | Approve | ||
Review via email:
|
Commit message
Description of the change
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Max Bowsher (maxb) wrote : | # |
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Max Bowsher (maxb) wrote : | # |
Owing to the nature of this change (moving content from one file to another), it is likely to be more practical to review individual changeset diffs, rather than the entire merge diff.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jelmer Vernooij (jelmer) wrote : | # |
I haven't done a full review, but it seems like there is still a dependency on bzrtools remaining: open_from_url is imported from bzrtools.
In general, disentangling the dependency on bzrtools seems like a good idea to me.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Max Bowsher (maxb) wrote : | # |
Apparently I fail at using grep... I'll fix it up.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Max Bowsher (maxb) wrote : | # |
OK, sorry for that, I was so focussed on sorting out the congruence with upstream_import.py that I failed to spot the use of another bzrtools file.
This should now be ready for reconsideration, and this time I've taken care to purge bzrtools from my system before running the builddeb tests.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Jelmer Vernooij (jelmer) wrote : | # |
For what it's worth, this seems ok to me. Eventually, I prefer to see more of this code move into bzrlib.
E.g. open_from_url() could perfectly well live in bzrlib.transport.
The import code seems generic enough to be in bzrlib as well.
There's conflicts in debian/control.
- 416. By Max Bowsher
-
Merge trunk, resolving conflicts.
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
Max Bowsher (maxb) wrote : | # |
Conflicts resolved. Ready for merge?
![](/+icing/build/overlay/assets/skins/sam/images/close.gif)
James Westby (james-w) wrote : | # |
Thanks Max, very helpful to have it split out, I should have done
it from the start.
Merged,
Thanks,
James
Preview Diff
1 | === modified file 'README' |
2 | --- README 2009-03-02 22:06:20 +0000 |
3 | +++ README 2010-02-11 20:48:23 +0000 |
4 | @@ -18,13 +18,11 @@ |
5 | ------------ |
6 | |
7 | This plugin requires `python-debian`_ (at least version 0.1.11), |
8 | -and a version of bzr at least 1.2. It also requires the |
9 | -`bzrtools`_ plugin to be installed. These are available in Debian |
10 | +and a version of bzr at least 1.2. These are available in Debian |
11 | (though maybe not at the required versions for a development version |
12 | of builddeb). |
13 | |
14 | .. _python-debian: http://bzr.debian.org/pkg-python-debian/trunk/ |
15 | -.. _bzrtools: https://launchpad.net/bzrtools |
16 | |
17 | This plugin can be installed in two ways. As you are probably using a Debian |
18 | system you can probably just use the debian packages. The other way is to |
19 | |
20 | === added file 'bzrtools_bzrtools.py' |
21 | --- bzrtools_bzrtools.py 1970-01-01 00:00:00 +0000 |
22 | +++ bzrtools_bzrtools.py 2010-02-11 20:48:23 +0000 |
23 | @@ -0,0 +1,30 @@ |
24 | +# This file is a small part of bzrtools' own bzrtools.py |
25 | +# The parts copied last changed in bzrtools 1.13.0. |
26 | + |
27 | +# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com> |
28 | +# Copyright (C) 2007 John Arbash Meinel |
29 | +# |
30 | +# This program is free software; you can redistribute it and/or modify |
31 | +# it under the terms of the GNU General Public License as published by |
32 | +# the Free Software Foundation; either version 2 of the License, or |
33 | +# (at your option) any later version. |
34 | +# |
35 | +# This program is distributed in the hope that it will be useful, |
36 | +# but WITHOUT ANY WARRANTY; without even the implied warranty of |
37 | +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
38 | +# GNU General Public License for more details. |
39 | +# |
40 | +# You should have received a copy of the GNU General Public License |
41 | +# along with this program; if not, write to the Free Software |
42 | +# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
43 | + |
44 | +from bzrlib import urlutils |
45 | +from bzrlib.transport import get_transport |
46 | + |
47 | + |
48 | +def open_from_url(location): |
49 | + location = urlutils.normalize_url(location) |
50 | + dirname, basename = urlutils.split(location) |
51 | + if location.endswith('/') and not basename.endswith('/'): |
52 | + basename += '/' |
53 | + return get_transport(dirname).get(basename) |
54 | |
55 | === added file 'bzrtools_import.py' |
56 | --- bzrtools_import.py 1970-01-01 00:00:00 +0000 |
57 | +++ bzrtools_import.py 2010-02-11 20:48:23 +0000 |
58 | @@ -0,0 +1,340 @@ |
59 | +# This file is a modified copy of bzrtools' upstream_import.py, last changed in |
60 | +# bzrtools 1.14.0. |
61 | + |
62 | +"""Import upstream source into a branch""" |
63 | + |
64 | +from bz2 import BZ2File |
65 | +import errno |
66 | +import os |
67 | +from StringIO import StringIO |
68 | +import stat |
69 | +import tarfile |
70 | +import zipfile |
71 | + |
72 | +from bzrlib import generate_ids |
73 | +from bzrlib.bzrdir import BzrDir |
74 | +from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError |
75 | +from bzrlib.osutils import (pathjoin, isdir, file_iterator, basename, |
76 | + file_kind, splitpath, normpath) |
77 | +from bzrlib.trace import warning |
78 | +from bzrlib.transform import TreeTransform, resolve_conflicts, cook_conflicts |
79 | +from bzrlib.workingtree import WorkingTree |
80 | +from bzrlib.plugins.builddeb.bzrtools_bzrtools import open_from_url |
81 | +from bzrlib.plugins.builddeb.errors import UnknownType |
82 | + |
83 | +class ZipFileWrapper(object): |
84 | + |
85 | + def __init__(self, fileobj, mode): |
86 | + self.zipfile = zipfile.ZipFile(fileobj, mode) |
87 | + |
88 | + def getmembers(self): |
89 | + for info in self.zipfile.infolist(): |
90 | + yield ZipInfoWrapper(self.zipfile, info) |
91 | + |
92 | + def extractfile(self, infowrapper): |
93 | + return StringIO(self.zipfile.read(infowrapper.name)) |
94 | + |
95 | + def add(self, filename): |
96 | + if isdir(filename): |
97 | + self.zipfile.writestr(filename+'/', '') |
98 | + else: |
99 | + self.zipfile.write(filename) |
100 | + |
101 | + def close(self): |
102 | + self.zipfile.close() |
103 | + |
104 | + |
105 | +class ZipInfoWrapper(object): |
106 | + |
107 | + def __init__(self, zipfile, info): |
108 | + self.info = info |
109 | + self.type = None |
110 | + self.name = info.filename |
111 | + self.zipfile = zipfile |
112 | + self.mode = 0666 |
113 | + |
114 | + def isdir(self): |
115 | + # Really? Eeeew! |
116 | + return bool(self.name.endswith('/')) |
117 | + |
118 | + def isreg(self): |
119 | + # Really? Eeeew! |
120 | + return not self.isdir() |
121 | + |
122 | + |
123 | +files_to_ignore = set( |
124 | + ['.bzrignore', '.shelf', '.bzr', '.bzr.backup', '.bzrtags', |
125 | + '.bzr-builddeb']) |
126 | + |
127 | + |
128 | +class DirWrapper(object): |
129 | + def __init__(self, fileobj, mode='r'): |
130 | + assert mode == 'r', mode |
131 | + self.root = os.path.realpath(fileobj.read()) |
132 | + |
133 | + def __repr__(self): |
134 | + return 'DirWrapper(%r)' % self.root |
135 | + |
136 | + def getmembers(self, subdir=None): |
137 | + if subdir is not None: |
138 | + mydir = pathjoin(self.root, subdir) |
139 | + else: |
140 | + mydir = self.root |
141 | + for child in os.listdir(mydir): |
142 | + if subdir is not None: |
143 | + child = pathjoin(subdir, child) |
144 | + fi = FileInfo(self.root, child) |
145 | + yield fi |
146 | + if fi.isdir(): |
147 | + for v in self.getmembers(child): |
148 | + yield v |
149 | + |
150 | + def extractfile(self, member): |
151 | + return open(member.fullpath) |
152 | + |
153 | + |
154 | +class FileInfo(object): |
155 | + |
156 | + def __init__(self, root, filepath): |
157 | + self.fullpath = pathjoin(root, filepath) |
158 | + self.root = root |
159 | + if filepath != '': |
160 | + self.name = pathjoin(basename(root), filepath) |
161 | + else: |
162 | + self.name = basename(root) |
163 | + self.type = None |
164 | + stat = os.lstat(self.fullpath) |
165 | + self.mode = stat.st_mode |
166 | + if self.isdir(): |
167 | + self.name += '/' |
168 | + |
169 | + def __repr__(self): |
170 | + return 'FileInfo(%r)' % self.name |
171 | + |
172 | + def isreg(self): |
173 | + return stat.S_ISREG(self.mode) |
174 | + |
175 | + def isdir(self): |
176 | + return stat.S_ISDIR(self.mode) |
177 | + |
178 | + def issym(self): |
179 | + if stat.S_ISLNK(self.mode): |
180 | + self.linkname = os.readlink(self.fullpath) |
181 | + return True |
182 | + else: |
183 | + return False |
184 | + |
185 | + |
186 | +def top_path(path): |
187 | + """Return the top directory given in a path.""" |
188 | + components = splitpath(normpath(path)) |
189 | + if len(components) > 0: |
190 | + return components[0] |
191 | + else: |
192 | + return '' |
193 | + |
194 | + |
195 | +def common_directory(names): |
196 | + """Determine a single directory prefix from a list of names""" |
197 | + prefixes = set() |
198 | + prefixes.update(map(top_path, names)) |
199 | + if '' in prefixes: |
200 | + prefixes.remove('') |
201 | + if len(prefixes) != 1: |
202 | + return None |
203 | + prefix = prefixes.pop() |
204 | + if prefix == '': |
205 | + return None |
206 | + return prefix |
207 | + |
208 | + |
209 | +def do_directory(tt, trans_id, tree, relative_path, path): |
210 | + if isdir(path) and tree.path2id(relative_path) is not None: |
211 | + tt.cancel_deletion(trans_id) |
212 | + else: |
213 | + tt.create_directory(trans_id) |
214 | + |
215 | + |
216 | +def add_implied_parents(implied_parents, path): |
217 | + """Update the set of implied parents from a path""" |
218 | + parent = os.path.dirname(path) |
219 | + if parent in implied_parents: |
220 | + return |
221 | + implied_parents.add(parent) |
222 | + add_implied_parents(implied_parents, parent) |
223 | + |
224 | + |
225 | +def names_of_files(tar_file): |
226 | + for member in tar_file.getmembers(): |
227 | + if member.type != "g": |
228 | + yield member.name |
229 | + |
230 | + |
231 | +def should_ignore(relative_path): |
232 | + parts = splitpath(relative_path) |
233 | + if not parts: |
234 | + return False |
235 | + for part in parts: |
236 | + if part in files_to_ignore: |
237 | + return True |
238 | + if part.endswith(',v'): |
239 | + return True |
240 | + |
241 | + |
242 | +def import_tar(tree, tar_input, file_ids_from=None): |
243 | + """Replace the contents of a working directory with tarfile contents. |
244 | + The tarfile may be a gzipped stream. File ids will be updated. |
245 | + """ |
246 | + tar_file = tarfile.open('lala', 'r', tar_input) |
247 | + import_archive(tree, tar_file, file_ids_from=file_ids_from) |
248 | + |
249 | +def import_zip(tree, zip_input, file_ids_from=None): |
250 | + zip_file = ZipFileWrapper(zip_input, 'r') |
251 | + import_archive(tree, zip_file, file_ids_from=file_ids_from) |
252 | + |
253 | +def import_dir(tree, dir, file_ids_from=None): |
254 | + dir_input = StringIO(dir) |
255 | + dir_file = DirWrapper(dir_input) |
256 | + import_archive(tree, dir_file, file_ids_from=file_ids_from) |
257 | + |
258 | +def import_archive(tree, archive_file, file_ids_from=None): |
259 | + prefix = common_directory(names_of_files(archive_file)) |
260 | + tt = TreeTransform(tree) |
261 | + |
262 | + if file_ids_from is None: |
263 | + file_ids_from = [] |
264 | + |
265 | + removed = set() |
266 | + for path, entry in tree.inventory.iter_entries(): |
267 | + if entry.parent_id is None: |
268 | + continue |
269 | + trans_id = tt.trans_id_tree_path(path) |
270 | + tt.delete_contents(trans_id) |
271 | + removed.add(path) |
272 | + |
273 | + added = set() |
274 | + implied_parents = set() |
275 | + seen = set() |
276 | + for member in archive_file.getmembers(): |
277 | + if member.type == 'g': |
278 | + # type 'g' is a header |
279 | + continue |
280 | + relative_path = member.name |
281 | + relative_path = normpath(relative_path) |
282 | + relative_path = relative_path.lstrip('/') |
283 | + if prefix is not None: |
284 | + relative_path = relative_path[len(prefix)+1:] |
285 | + relative_path = relative_path.rstrip('/') |
286 | + if relative_path == '' or relative_path == '.': |
287 | + continue |
288 | + if should_ignore(relative_path): |
289 | + continue |
290 | + add_implied_parents(implied_parents, relative_path) |
291 | + trans_id = tt.trans_id_tree_path(relative_path) |
292 | + added.add(relative_path.rstrip('/')) |
293 | + path = tree.abspath(relative_path) |
294 | + if member.name in seen: |
295 | + if tt.final_kind(trans_id) == 'file': |
296 | + tt.set_executability(None, trans_id) |
297 | + tt.cancel_creation(trans_id) |
298 | + seen.add(member.name) |
299 | + if member.isreg(): |
300 | + tt.create_file(file_iterator(archive_file.extractfile(member)), |
301 | + trans_id) |
302 | + executable = (member.mode & 0111) != 0 |
303 | + tt.set_executability(executable, trans_id) |
304 | + elif member.isdir(): |
305 | + do_directory(tt, trans_id, tree, relative_path, path) |
306 | + elif member.issym(): |
307 | + tt.create_symlink(member.linkname, trans_id) |
308 | + else: |
309 | + raise UnknownType(relative_path) |
310 | + if tt.tree_file_id(trans_id) is None: |
311 | + found = False |
312 | + for other_tree in file_ids_from: |
313 | + other_tree.lock_read() |
314 | + try: |
315 | + if other_tree.has_filename(relative_path): |
316 | + file_id = other_tree.path2id(relative_path) |
317 | + if file_id is not None: |
318 | + tt.version_file(file_id, trans_id) |
319 | + found = True |
320 | + break |
321 | + finally: |
322 | + other_tree.unlock() |
323 | + if not found: |
324 | + name = basename(member.name.rstrip('/')) |
325 | + file_id = generate_ids.gen_file_id(name) |
326 | + tt.version_file(file_id, trans_id) |
327 | + |
328 | + for relative_path in implied_parents.difference(added): |
329 | + if relative_path == "": |
330 | + continue |
331 | + trans_id = tt.trans_id_tree_path(relative_path) |
332 | + path = tree.abspath(relative_path) |
333 | + do_directory(tt, trans_id, tree, relative_path, path) |
334 | + if tt.tree_file_id(trans_id) is None: |
335 | + found = False |
336 | + for other_tree in file_ids_from: |
337 | + other_tree.lock_read() |
338 | + try: |
339 | + if other_tree.has_filename(relative_path): |
340 | + file_id = other_tree.path2id(relative_path) |
341 | + if file_id is not None: |
342 | + tt.version_file(file_id, trans_id) |
343 | + found = True |
344 | + break |
345 | + finally: |
346 | + other_tree.unlock() |
347 | + if not found: |
348 | + tt.version_file(trans_id, trans_id) |
349 | + added.add(relative_path) |
350 | + |
351 | + for path in removed.difference(added): |
352 | + tt.unversion_file(tt.trans_id_tree_path(path)) |
353 | + |
354 | + for conflict in cook_conflicts(resolve_conflicts(tt), tt): |
355 | + warning(conflict) |
356 | + tt.apply() |
357 | + |
358 | + |
359 | +def do_import(source, tree_directory=None): |
360 | + """Implementation of import command. Intended for UI only""" |
361 | + if tree_directory is not None: |
362 | + try: |
363 | + tree = WorkingTree.open(tree_directory) |
364 | + except NotBranchError: |
365 | + if not os.path.exists(tree_directory): |
366 | + os.mkdir(tree_directory) |
367 | + branch = BzrDir.create_branch_convenience(tree_directory) |
368 | + tree = branch.bzrdir.open_workingtree() |
369 | + else: |
370 | + tree = WorkingTree.open_containing('.')[0] |
371 | + tree.lock_write() |
372 | + try: |
373 | + if tree.changes_from(tree.basis_tree()).has_changed(): |
374 | + raise BzrCommandError("Working tree has uncommitted changes.") |
375 | + |
376 | + if (source.endswith('.tar') or source.endswith('.tar.gz') or |
377 | + source.endswith('.tar.bz2')) or source.endswith('.tgz'): |
378 | + try: |
379 | + tar_input = open_from_url(source) |
380 | + if source.endswith('.bz2'): |
381 | + tar_input = StringIO(tar_input.read().decode('bz2')) |
382 | + except IOError, e: |
383 | + if e.errno == errno.ENOENT: |
384 | + raise NoSuchFile(source) |
385 | + try: |
386 | + import_tar(tree, tar_input) |
387 | + finally: |
388 | + tar_input.close() |
389 | + elif source.endswith('.zip'): |
390 | + import_zip(tree, open_from_url(source)) |
391 | + elif file_kind(source) == 'directory': |
392 | + s = StringIO(source) |
393 | + s.seek(0) |
394 | + import_dir(tree, s) |
395 | + else: |
396 | + raise BzrCommandError('Unhandled import source') |
397 | + finally: |
398 | + tree.unlock() |
399 | |
400 | === modified file 'debian/control' |
401 | --- debian/control 2010-02-02 02:44:53 +0000 |
402 | +++ debian/control 2010-02-11 20:48:23 +0000 |
403 | @@ -4,7 +4,7 @@ |
404 | Maintainer: Debian Bazaar Maintainers <pkg-bazaar-maint@lists.alioth.debian.org> |
405 | Uploaders: Reinhard Tartler <siretart@tauware.de>, James Westby <james.westby@ubuntu.com>, Jelmer Vernooij <jelmer@debian.org> |
406 | Build-Depends: debhelper (>= 5.0.37.2), python-all (>= 2.3.5-11) |
407 | -Build-Depends-Indep: bzr (>= 2.1~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, bzrtools (>= 1.2~), patchutils |
408 | +Build-Depends-Indep: bzr (>= 2.1~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, patchutils |
409 | Vcs-Bzr: http://bzr.debian.org/pkg-bazaar/bzr-builddeb/unstable |
410 | Vcs-Browser: http://bzr.debian.org/loggerhead/pkg-bazaar/bzr-builddeb/unstable |
411 | XS-Python-Version: >= 2.4 |
412 | @@ -12,7 +12,7 @@ |
413 | |
414 | Package: bzr-builddeb |
415 | Architecture: all |
416 | -Depends: bzr (>= 2.1~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, bzrtools (>= 1.2), devscripts, patchutils, pristine-tar |
417 | +Depends: bzr (>= 2.1~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, devscripts, patchutils, pristine-tar |
418 | Recommends: python-launchpadlib |
419 | Suggests: bzr-svn (>= 0.4.10) |
420 | Provides: bzr-buildpackage |
421 | |
422 | === modified file 'import_dsc.py' |
423 | --- import_dsc.py 2010-02-09 19:52:28 +0000 |
424 | +++ import_dsc.py 2010-02-11 20:48:23 +0000 |
425 | @@ -38,7 +38,6 @@ |
426 | import shutil |
427 | import stat |
428 | from subprocess import Popen, PIPE, STDOUT |
429 | -from StringIO import StringIO |
430 | import tempfile |
431 | |
432 | from debian_bundle import deb822 |
433 | @@ -46,7 +45,6 @@ |
434 | |
435 | from bzrlib import ( |
436 | bzrdir, |
437 | - generate_ids, |
438 | osutils, |
439 | ) |
440 | from bzrlib.config import ConfigObj |
441 | @@ -57,24 +55,17 @@ |
442 | NoWorkingTree, |
443 | ) |
444 | from bzrlib.export import export |
445 | -from bzrlib.osutils import file_iterator, isdir, basename, splitpath |
446 | from bzrlib.revisionspec import RevisionSpec |
447 | from bzrlib.revision import NULL_REVISION |
448 | from bzrlib.trace import warning, mutter, note |
449 | -from bzrlib.transform import TreeTransform, cook_conflicts, resolve_conflicts |
450 | from bzrlib.transport import ( |
451 | get_transport, |
452 | ) |
453 | |
454 | -from bzrlib.plugins.bzrtools.upstream_import import ( |
455 | - names_of_files, |
456 | - add_implied_parents, |
457 | - ) |
458 | - |
459 | +from bzrlib.plugins.builddeb.bzrtools_import import import_dir |
460 | from bzrlib.plugins.builddeb.errors import ( |
461 | PristineTarError, |
462 | TarFailed, |
463 | - UnknownType, |
464 | UpstreamAlreadyImported, |
465 | UpstreamBranchAlreadyMerged, |
466 | ) |
467 | @@ -87,220 +78,6 @@ |
468 | ) |
469 | |
470 | |
471 | -files_to_ignore = set( |
472 | - ['.bzrignore', '.shelf', '.bzr', '.bzr.backup', '.bzrtags', |
473 | - '.bzr-builddeb']) |
474 | - |
475 | - |
476 | -class DirWrapper(object): |
477 | - def __init__(self, fileobj, mode='r'): |
478 | - assert mode == 'r', mode |
479 | - self.root = os.path.realpath(fileobj.read()) |
480 | - |
481 | - def __repr__(self): |
482 | - return 'DirWrapper(%r)' % self.root |
483 | - |
484 | - def getmembers(self, subdir=None): |
485 | - if subdir is not None: |
486 | - mydir = os.path.join(self.root, subdir) |
487 | - else: |
488 | - mydir = self.root |
489 | - for child in os.listdir(mydir): |
490 | - if subdir is not None: |
491 | - child = os.path.join(subdir, child) |
492 | - fi = FileInfo(self.root, child) |
493 | - yield fi |
494 | - if fi.isdir(): |
495 | - for v in self.getmembers(child): |
496 | - yield v |
497 | - |
498 | - def extractfile(self, member): |
499 | - return open(member.fullpath) |
500 | - |
501 | - |
502 | -class FileInfo(object): |
503 | - |
504 | - def __init__(self, root, filepath): |
505 | - self.fullpath = os.path.join(root, filepath) |
506 | - self.root = root |
507 | - if filepath != '': |
508 | - self.name = os.path.join(basename(root), filepath) |
509 | - else: |
510 | - self.name = basename(root) |
511 | - self.type = None |
512 | - stat = os.lstat(self.fullpath) |
513 | - self.mode = stat.st_mode |
514 | - if self.isdir(): |
515 | - self.name += '/' |
516 | - |
517 | - def __repr__(self): |
518 | - return 'FileInfo(%r)' % self.name |
519 | - |
520 | - def isreg(self): |
521 | - return stat.S_ISREG(self.mode) |
522 | - |
523 | - def isdir(self): |
524 | - return stat.S_ISDIR(self.mode) |
525 | - |
526 | - def issym(self): |
527 | - if stat.S_ISLNK(self.mode): |
528 | - self.linkname = os.readlink(self.fullpath) |
529 | - return True |
530 | - else: |
531 | - return False |
532 | - |
533 | - def islnk(self): |
534 | - # This could be accurate, but the use below seems like |
535 | - # it wouldn't really care |
536 | - return False |
537 | - |
538 | - |
539 | -def import_dir(tree, dir, file_ids_from=None): |
540 | - dir_input = StringIO(dir) |
541 | - dir_file = DirWrapper(dir_input) |
542 | - import_archive(tree, dir_file, file_ids_from=file_ids_from) |
543 | - |
544 | - |
545 | -def do_directory(tt, trans_id, tree, relative_path, path): |
546 | - if isdir(path) and tree.path2id(relative_path) is not None: |
547 | - tt.cancel_deletion(trans_id) |
548 | - else: |
549 | - tt.create_directory(trans_id) |
550 | - |
551 | - |
552 | -def should_ignore(relative_path): |
553 | - parts = splitpath(relative_path) |
554 | - if not parts: |
555 | - return False |
556 | - for part in parts: |
557 | - if part in files_to_ignore: |
558 | - return True |
559 | - if part.endswith(',v'): |
560 | - return True |
561 | - |
562 | - |
563 | -def top_directory(path): |
564 | - """Return the top directory given in a path.""" |
565 | - parts = osutils.splitpath(osutils.normpath(path)) |
566 | - if len(parts) > 0: |
567 | - return parts[0] |
568 | - return '' |
569 | - |
570 | - |
571 | -def common_directory(names): |
572 | - """Determine a single directory prefix from a list of names""" |
573 | - prefixes = set() |
574 | - prefixes.update(map(top_directory, names)) |
575 | - if '' in prefixes: |
576 | - prefixes.remove('') |
577 | - if len(prefixes) != 1: |
578 | - return None |
579 | - prefix = prefixes.pop() |
580 | - if prefix == '': |
581 | - return None |
582 | - return prefix |
583 | - |
584 | - |
585 | -def import_archive(tree, archive_file, file_ids_from=None): |
586 | - prefix = common_directory(names_of_files(archive_file)) |
587 | - tt = TreeTransform(tree) |
588 | - |
589 | - if file_ids_from is None: |
590 | - file_ids_from = [] |
591 | - |
592 | - removed = set() |
593 | - for path, entry in tree.inventory.iter_entries(): |
594 | - if entry.parent_id is None: |
595 | - continue |
596 | - trans_id = tt.trans_id_tree_path(path) |
597 | - tt.delete_contents(trans_id) |
598 | - removed.add(path) |
599 | - |
600 | - added = set() |
601 | - implied_parents = set() |
602 | - seen = set() |
603 | - for member in archive_file.getmembers(): |
604 | - if member.type == 'g': |
605 | - # type 'g' is a header |
606 | - continue |
607 | - relative_path = member.name |
608 | - relative_path = osutils.normpath(relative_path) |
609 | - relative_path = relative_path.lstrip('/') |
610 | - if prefix is not None: |
611 | - relative_path = relative_path[len(prefix)+1:] |
612 | - if relative_path == '' or relative_path == '.': |
613 | - continue |
614 | - if should_ignore(relative_path): |
615 | - continue |
616 | - add_implied_parents(implied_parents, relative_path) |
617 | - trans_id = tt.trans_id_tree_path(relative_path) |
618 | - added.add(relative_path.rstrip('/')) |
619 | - path = tree.abspath(relative_path) |
620 | - if member.name in seen: |
621 | - if tt.final_kind(trans_id) == 'file': |
622 | - tt.set_executability(None, trans_id) |
623 | - tt.cancel_creation(trans_id) |
624 | - seen.add(member.name) |
625 | - if member.isreg() or member.islnk(): |
626 | - tt.create_file(file_iterator(archive_file.extractfile(member)), |
627 | - trans_id) |
628 | - executable = (member.mode & 0111) != 0 |
629 | - tt.set_executability(executable, trans_id) |
630 | - elif member.isdir(): |
631 | - do_directory(tt, trans_id, tree, relative_path, path) |
632 | - elif member.issym(): |
633 | - tt.create_symlink(member.linkname, trans_id) |
634 | - else: |
635 | - raise UnknownType(relative_path) |
636 | - if tt.tree_file_id(trans_id) is None: |
637 | - found = False |
638 | - for other_tree in file_ids_from: |
639 | - other_tree.lock_read() |
640 | - try: |
641 | - if other_tree.has_filename(relative_path): |
642 | - file_id = other_tree.path2id(relative_path) |
643 | - if file_id is not None: |
644 | - tt.version_file(file_id, trans_id) |
645 | - found = True |
646 | - break |
647 | - finally: |
648 | - other_tree.unlock() |
649 | - if not found: |
650 | - name = basename(member.name.rstrip('/')) |
651 | - file_id = generate_ids.gen_file_id(name) |
652 | - tt.version_file(file_id, trans_id) |
653 | - |
654 | - for relative_path in implied_parents.difference(added): |
655 | - if relative_path == "": |
656 | - continue |
657 | - trans_id = tt.trans_id_tree_path(relative_path) |
658 | - path = tree.abspath(relative_path) |
659 | - do_directory(tt, trans_id, tree, relative_path, path) |
660 | - if tt.tree_file_id(trans_id) is None: |
661 | - found = False |
662 | - for other_tree in file_ids_from: |
663 | - other_tree.lock_read() |
664 | - try: |
665 | - if other_tree.has_filename(relative_path): |
666 | - file_id = other_tree.path2id(relative_path) |
667 | - if file_id is not None: |
668 | - tt.version_file(file_id, trans_id) |
669 | - found = True |
670 | - break |
671 | - finally: |
672 | - other_tree.unlock() |
673 | - if not found: |
674 | - tt.version_file(trans_id, trans_id) |
675 | - added.add(relative_path) |
676 | - |
677 | - for path in removed.difference(added): |
678 | - tt.unversion_file(tt.trans_id_tree_path(path)) |
679 | - |
680 | - for conflict in cook_conflicts(resolve_conflicts(tt), tt): |
681 | - warning(conflict) |
682 | - tt.apply() |
683 | - |
684 | - |
685 | class DscCache(object): |
686 | |
687 | def __init__(self, transport=None): |
Currently bzr-builddeb incorporates a copied version of the code for "bzr import", which has diverged from its upstream. The copied code is included in import_dsc.py along with a large amount of not very related code. Furthermore, bzr-builddeb has a vestigial dependency on bzrtools just for 11 lines of code which didn't get copied.
This branch sets out to rectify this by:
1. Performing several tweaks to import_dsc.py to make the code closer to the version in bzrtools.
2. Importing the remaining 11 lines of code from bzrtools, and severing the dependency to bzrtools.
3. Importing a copy of bzrtools' current upstream_import.py into the bzr-builddeb tree as bzrtools_import.py. (The rationale for the rename is that the term "upstream" is already used a lot in bzr-builddeb, and its use here would be confusingly in conflict.)
4. Applying onto the added bzrtools_import.py all the changes to the relevant code, feature by feature.
5. Having attained the situation where the relevant sections of code in import_dsc.py exactly match the relevant sections in bzrtools_import.py by the previous steps, delete the code from import_dsc.py and switch imports to use the new one.
All this should leave us with the relevant code now isolated to a file separate from the rest of bzr-builddeb's code, which is easily diffable against the version in bzrtools. This will (a) make it easy to import any future upstream changes, and (b) make it easy to see what needs pushing back upstream.
Hopefully in the longer term, the modifications can be sent upstream to bzrtools, and in the even longer term, bzr-builddeb could drop the incorporated copy of the code, and depend on bzrtools for it.