Merge lp:~maxb/bzr-builddeb/bzrtools-code-disentangle into lp:bzr-builddeb

Proposed by Max Bowsher
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
Reviewer Review Type Date Requested Status
Jelmer Vernooij Approve
Review via email: mp+16596@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Max Bowsher (maxb) wrote :

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.

Revision history for this message
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.

Revision history for this message
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.

review: Needs Fixing
Revision history for this message
Max Bowsher (maxb) wrote :

Apparently I fail at using grep... I'll fix it up.

Revision history for this message
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.

Revision history for this message
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.

review: Approve
416. By Max Bowsher

Merge trunk, resolving conflicts.

Revision history for this message
Max Bowsher (maxb) wrote :

Conflicts resolved. Ready for merge?

Revision history for this message
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

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
=== modified file 'README'
--- README 2009-03-02 22:06:20 +0000
+++ README 2010-02-11 20:48:23 +0000
@@ -18,13 +18,11 @@
18------------18------------
1919
20This plugin requires `python-debian`_ (at least version 0.1.11),20This plugin requires `python-debian`_ (at least version 0.1.11),
21and a version of bzr at least 1.2. It also requires the21and a version of bzr at least 1.2. These are available in Debian
22`bzrtools`_ plugin to be installed. These are available in Debian
23(though maybe not at the required versions for a development version22(though maybe not at the required versions for a development version
24 of builddeb).23 of builddeb).
2524
26.. _python-debian: http://bzr.debian.org/pkg-python-debian/trunk/25.. _python-debian: http://bzr.debian.org/pkg-python-debian/trunk/
27.. _bzrtools: https://launchpad.net/bzrtools
2826
29This plugin can be installed in two ways. As you are probably using a Debian27This plugin can be installed in two ways. As you are probably using a Debian
30system you can probably just use the debian packages. The other way is to 28system you can probably just use the debian packages. The other way is to
3129
=== added file 'bzrtools_bzrtools.py'
--- bzrtools_bzrtools.py 1970-01-01 00:00:00 +0000
+++ bzrtools_bzrtools.py 2010-02-11 20:48:23 +0000
@@ -0,0 +1,30 @@
1# This file is a small part of bzrtools' own bzrtools.py
2# The parts copied last changed in bzrtools 1.13.0.
3
4# Copyright (C) 2005, 2006, 2007 Aaron Bentley <aaron@aaronbentley.com>
5# Copyright (C) 2007 John Arbash Meinel
6#
7# This program is free software; you can redistribute it and/or modify
8# it under the terms of the GNU General Public License as published by
9# the Free Software Foundation; either version 2 of the License, or
10# (at your option) any later version.
11#
12# This program is distributed in the hope that it will be useful,
13# but WITHOUT ANY WARRANTY; without even the implied warranty of
14# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15# GNU General Public License for more details.
16#
17# You should have received a copy of the GNU General Public License
18# along with this program; if not, write to the Free Software
19# Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
21from bzrlib import urlutils
22from bzrlib.transport import get_transport
23
24
25def open_from_url(location):
26 location = urlutils.normalize_url(location)
27 dirname, basename = urlutils.split(location)
28 if location.endswith('/') and not basename.endswith('/'):
29 basename += '/'
30 return get_transport(dirname).get(basename)
031
=== added file 'bzrtools_import.py'
--- bzrtools_import.py 1970-01-01 00:00:00 +0000
+++ bzrtools_import.py 2010-02-11 20:48:23 +0000
@@ -0,0 +1,340 @@
1# This file is a modified copy of bzrtools' upstream_import.py, last changed in
2# bzrtools 1.14.0.
3
4"""Import upstream source into a branch"""
5
6from bz2 import BZ2File
7import errno
8import os
9from StringIO import StringIO
10import stat
11import tarfile
12import zipfile
13
14from bzrlib import generate_ids
15from bzrlib.bzrdir import BzrDir
16from bzrlib.errors import NoSuchFile, BzrCommandError, NotBranchError
17from bzrlib.osutils import (pathjoin, isdir, file_iterator, basename,
18 file_kind, splitpath, normpath)
19from bzrlib.trace import warning
20from bzrlib.transform import TreeTransform, resolve_conflicts, cook_conflicts
21from bzrlib.workingtree import WorkingTree
22from bzrlib.plugins.builddeb.bzrtools_bzrtools import open_from_url
23from bzrlib.plugins.builddeb.errors import UnknownType
24
25class ZipFileWrapper(object):
26
27 def __init__(self, fileobj, mode):
28 self.zipfile = zipfile.ZipFile(fileobj, mode)
29
30 def getmembers(self):
31 for info in self.zipfile.infolist():
32 yield ZipInfoWrapper(self.zipfile, info)
33
34 def extractfile(self, infowrapper):
35 return StringIO(self.zipfile.read(infowrapper.name))
36
37 def add(self, filename):
38 if isdir(filename):
39 self.zipfile.writestr(filename+'/', '')
40 else:
41 self.zipfile.write(filename)
42
43 def close(self):
44 self.zipfile.close()
45
46
47class ZipInfoWrapper(object):
48
49 def __init__(self, zipfile, info):
50 self.info = info
51 self.type = None
52 self.name = info.filename
53 self.zipfile = zipfile
54 self.mode = 0666
55
56 def isdir(self):
57 # Really? Eeeew!
58 return bool(self.name.endswith('/'))
59
60 def isreg(self):
61 # Really? Eeeew!
62 return not self.isdir()
63
64
65files_to_ignore = set(
66 ['.bzrignore', '.shelf', '.bzr', '.bzr.backup', '.bzrtags',
67 '.bzr-builddeb'])
68
69
70class DirWrapper(object):
71 def __init__(self, fileobj, mode='r'):
72 assert mode == 'r', mode
73 self.root = os.path.realpath(fileobj.read())
74
75 def __repr__(self):
76 return 'DirWrapper(%r)' % self.root
77
78 def getmembers(self, subdir=None):
79 if subdir is not None:
80 mydir = pathjoin(self.root, subdir)
81 else:
82 mydir = self.root
83 for child in os.listdir(mydir):
84 if subdir is not None:
85 child = pathjoin(subdir, child)
86 fi = FileInfo(self.root, child)
87 yield fi
88 if fi.isdir():
89 for v in self.getmembers(child):
90 yield v
91
92 def extractfile(self, member):
93 return open(member.fullpath)
94
95
96class FileInfo(object):
97
98 def __init__(self, root, filepath):
99 self.fullpath = pathjoin(root, filepath)
100 self.root = root
101 if filepath != '':
102 self.name = pathjoin(basename(root), filepath)
103 else:
104 self.name = basename(root)
105 self.type = None
106 stat = os.lstat(self.fullpath)
107 self.mode = stat.st_mode
108 if self.isdir():
109 self.name += '/'
110
111 def __repr__(self):
112 return 'FileInfo(%r)' % self.name
113
114 def isreg(self):
115 return stat.S_ISREG(self.mode)
116
117 def isdir(self):
118 return stat.S_ISDIR(self.mode)
119
120 def issym(self):
121 if stat.S_ISLNK(self.mode):
122 self.linkname = os.readlink(self.fullpath)
123 return True
124 else:
125 return False
126
127
128def top_path(path):
129 """Return the top directory given in a path."""
130 components = splitpath(normpath(path))
131 if len(components) > 0:
132 return components[0]
133 else:
134 return ''
135
136
137def common_directory(names):
138 """Determine a single directory prefix from a list of names"""
139 prefixes = set()
140 prefixes.update(map(top_path, names))
141 if '' in prefixes:
142 prefixes.remove('')
143 if len(prefixes) != 1:
144 return None
145 prefix = prefixes.pop()
146 if prefix == '':
147 return None
148 return prefix
149
150
151def do_directory(tt, trans_id, tree, relative_path, path):
152 if isdir(path) and tree.path2id(relative_path) is not None:
153 tt.cancel_deletion(trans_id)
154 else:
155 tt.create_directory(trans_id)
156
157
158def add_implied_parents(implied_parents, path):
159 """Update the set of implied parents from a path"""
160 parent = os.path.dirname(path)
161 if parent in implied_parents:
162 return
163 implied_parents.add(parent)
164 add_implied_parents(implied_parents, parent)
165
166
167def names_of_files(tar_file):
168 for member in tar_file.getmembers():
169 if member.type != "g":
170 yield member.name
171
172
173def should_ignore(relative_path):
174 parts = splitpath(relative_path)
175 if not parts:
176 return False
177 for part in parts:
178 if part in files_to_ignore:
179 return True
180 if part.endswith(',v'):
181 return True
182
183
184def import_tar(tree, tar_input, file_ids_from=None):
185 """Replace the contents of a working directory with tarfile contents.
186 The tarfile may be a gzipped stream. File ids will be updated.
187 """
188 tar_file = tarfile.open('lala', 'r', tar_input)
189 import_archive(tree, tar_file, file_ids_from=file_ids_from)
190
191def import_zip(tree, zip_input, file_ids_from=None):
192 zip_file = ZipFileWrapper(zip_input, 'r')
193 import_archive(tree, zip_file, file_ids_from=file_ids_from)
194
195def import_dir(tree, dir, file_ids_from=None):
196 dir_input = StringIO(dir)
197 dir_file = DirWrapper(dir_input)
198 import_archive(tree, dir_file, file_ids_from=file_ids_from)
199
200def import_archive(tree, archive_file, file_ids_from=None):
201 prefix = common_directory(names_of_files(archive_file))
202 tt = TreeTransform(tree)
203
204 if file_ids_from is None:
205 file_ids_from = []
206
207 removed = set()
208 for path, entry in tree.inventory.iter_entries():
209 if entry.parent_id is None:
210 continue
211 trans_id = tt.trans_id_tree_path(path)
212 tt.delete_contents(trans_id)
213 removed.add(path)
214
215 added = set()
216 implied_parents = set()
217 seen = set()
218 for member in archive_file.getmembers():
219 if member.type == 'g':
220 # type 'g' is a header
221 continue
222 relative_path = member.name
223 relative_path = normpath(relative_path)
224 relative_path = relative_path.lstrip('/')
225 if prefix is not None:
226 relative_path = relative_path[len(prefix)+1:]
227 relative_path = relative_path.rstrip('/')
228 if relative_path == '' or relative_path == '.':
229 continue
230 if should_ignore(relative_path):
231 continue
232 add_implied_parents(implied_parents, relative_path)
233 trans_id = tt.trans_id_tree_path(relative_path)
234 added.add(relative_path.rstrip('/'))
235 path = tree.abspath(relative_path)
236 if member.name in seen:
237 if tt.final_kind(trans_id) == 'file':
238 tt.set_executability(None, trans_id)
239 tt.cancel_creation(trans_id)
240 seen.add(member.name)
241 if member.isreg():
242 tt.create_file(file_iterator(archive_file.extractfile(member)),
243 trans_id)
244 executable = (member.mode & 0111) != 0
245 tt.set_executability(executable, trans_id)
246 elif member.isdir():
247 do_directory(tt, trans_id, tree, relative_path, path)
248 elif member.issym():
249 tt.create_symlink(member.linkname, trans_id)
250 else:
251 raise UnknownType(relative_path)
252 if tt.tree_file_id(trans_id) is None:
253 found = False
254 for other_tree in file_ids_from:
255 other_tree.lock_read()
256 try:
257 if other_tree.has_filename(relative_path):
258 file_id = other_tree.path2id(relative_path)
259 if file_id is not None:
260 tt.version_file(file_id, trans_id)
261 found = True
262 break
263 finally:
264 other_tree.unlock()
265 if not found:
266 name = basename(member.name.rstrip('/'))
267 file_id = generate_ids.gen_file_id(name)
268 tt.version_file(file_id, trans_id)
269
270 for relative_path in implied_parents.difference(added):
271 if relative_path == "":
272 continue
273 trans_id = tt.trans_id_tree_path(relative_path)
274 path = tree.abspath(relative_path)
275 do_directory(tt, trans_id, tree, relative_path, path)
276 if tt.tree_file_id(trans_id) is None:
277 found = False
278 for other_tree in file_ids_from:
279 other_tree.lock_read()
280 try:
281 if other_tree.has_filename(relative_path):
282 file_id = other_tree.path2id(relative_path)
283 if file_id is not None:
284 tt.version_file(file_id, trans_id)
285 found = True
286 break
287 finally:
288 other_tree.unlock()
289 if not found:
290 tt.version_file(trans_id, trans_id)
291 added.add(relative_path)
292
293 for path in removed.difference(added):
294 tt.unversion_file(tt.trans_id_tree_path(path))
295
296 for conflict in cook_conflicts(resolve_conflicts(tt), tt):
297 warning(conflict)
298 tt.apply()
299
300
301def do_import(source, tree_directory=None):
302 """Implementation of import command. Intended for UI only"""
303 if tree_directory is not None:
304 try:
305 tree = WorkingTree.open(tree_directory)
306 except NotBranchError:
307 if not os.path.exists(tree_directory):
308 os.mkdir(tree_directory)
309 branch = BzrDir.create_branch_convenience(tree_directory)
310 tree = branch.bzrdir.open_workingtree()
311 else:
312 tree = WorkingTree.open_containing('.')[0]
313 tree.lock_write()
314 try:
315 if tree.changes_from(tree.basis_tree()).has_changed():
316 raise BzrCommandError("Working tree has uncommitted changes.")
317
318 if (source.endswith('.tar') or source.endswith('.tar.gz') or
319 source.endswith('.tar.bz2')) or source.endswith('.tgz'):
320 try:
321 tar_input = open_from_url(source)
322 if source.endswith('.bz2'):
323 tar_input = StringIO(tar_input.read().decode('bz2'))
324 except IOError, e:
325 if e.errno == errno.ENOENT:
326 raise NoSuchFile(source)
327 try:
328 import_tar(tree, tar_input)
329 finally:
330 tar_input.close()
331 elif source.endswith('.zip'):
332 import_zip(tree, open_from_url(source))
333 elif file_kind(source) == 'directory':
334 s = StringIO(source)
335 s.seek(0)
336 import_dir(tree, s)
337 else:
338 raise BzrCommandError('Unhandled import source')
339 finally:
340 tree.unlock()
0341
=== modified file 'debian/control'
--- debian/control 2010-02-02 02:44:53 +0000
+++ debian/control 2010-02-11 20:48:23 +0000
@@ -4,7 +4,7 @@
4Maintainer: Debian Bazaar Maintainers <pkg-bazaar-maint@lists.alioth.debian.org>4Maintainer: Debian Bazaar Maintainers <pkg-bazaar-maint@lists.alioth.debian.org>
5Uploaders: Reinhard Tartler <siretart@tauware.de>, James Westby <james.westby@ubuntu.com>, Jelmer Vernooij <jelmer@debian.org>5Uploaders: Reinhard Tartler <siretart@tauware.de>, James Westby <james.westby@ubuntu.com>, Jelmer Vernooij <jelmer@debian.org>
6Build-Depends: debhelper (>= 5.0.37.2), python-all (>= 2.3.5-11)6Build-Depends: debhelper (>= 5.0.37.2), python-all (>= 2.3.5-11)
7Build-Depends-Indep: bzr (>= 2.1~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, bzrtools (>= 1.2~), patchutils7Build-Depends-Indep: bzr (>= 2.1~), python-central (>= 0.5.8), python-docutils, python-debian (>= 0.1.11), python-apt, patchutils
8Vcs-Bzr: http://bzr.debian.org/pkg-bazaar/bzr-builddeb/unstable8Vcs-Bzr: http://bzr.debian.org/pkg-bazaar/bzr-builddeb/unstable
9Vcs-Browser: http://bzr.debian.org/loggerhead/pkg-bazaar/bzr-builddeb/unstable9Vcs-Browser: http://bzr.debian.org/loggerhead/pkg-bazaar/bzr-builddeb/unstable
10XS-Python-Version: >= 2.410XS-Python-Version: >= 2.4
@@ -12,7 +12,7 @@
1212
13Package: bzr-builddeb13Package: bzr-builddeb
14Architecture: all14Architecture: all
15Depends: bzr (>= 2.1~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, bzrtools (>= 1.2), devscripts, patchutils, pristine-tar15Depends: bzr (>= 2.1~), python-debian (>= 0.1.11), python-apt, ${python:Depends}, dpkg-dev, fakeroot, devscripts, patchutils, pristine-tar
16Recommends: python-launchpadlib16Recommends: python-launchpadlib
17Suggests: bzr-svn (>= 0.4.10)17Suggests: bzr-svn (>= 0.4.10)
18Provides: bzr-buildpackage18Provides: bzr-buildpackage
1919
=== modified file 'import_dsc.py'
--- import_dsc.py 2010-02-09 19:52:28 +0000
+++ import_dsc.py 2010-02-11 20:48:23 +0000
@@ -38,7 +38,6 @@
38import shutil38import shutil
39import stat39import stat
40from subprocess import Popen, PIPE, STDOUT40from subprocess import Popen, PIPE, STDOUT
41from StringIO import StringIO
42import tempfile41import tempfile
4342
44from debian_bundle import deb82243from debian_bundle import deb822
@@ -46,7 +45,6 @@
4645
47from bzrlib import (46from bzrlib import (
48 bzrdir,47 bzrdir,
49 generate_ids,
50 osutils,48 osutils,
51 )49 )
52from bzrlib.config import ConfigObj50from bzrlib.config import ConfigObj
@@ -57,24 +55,17 @@
57 NoWorkingTree,55 NoWorkingTree,
58 )56 )
59from bzrlib.export import export57from bzrlib.export import export
60from bzrlib.osutils import file_iterator, isdir, basename, splitpath
61from bzrlib.revisionspec import RevisionSpec58from bzrlib.revisionspec import RevisionSpec
62from bzrlib.revision import NULL_REVISION59from bzrlib.revision import NULL_REVISION
63from bzrlib.trace import warning, mutter, note60from bzrlib.trace import warning, mutter, note
64from bzrlib.transform import TreeTransform, cook_conflicts, resolve_conflicts
65from bzrlib.transport import (61from bzrlib.transport import (
66 get_transport,62 get_transport,
67 )63 )
6864
69from bzrlib.plugins.bzrtools.upstream_import import (65from bzrlib.plugins.builddeb.bzrtools_import import import_dir
70 names_of_files,
71 add_implied_parents,
72 )
73
74from bzrlib.plugins.builddeb.errors import (66from bzrlib.plugins.builddeb.errors import (
75 PristineTarError,67 PristineTarError,
76 TarFailed,68 TarFailed,
77 UnknownType,
78 UpstreamAlreadyImported,69 UpstreamAlreadyImported,
79 UpstreamBranchAlreadyMerged,70 UpstreamBranchAlreadyMerged,
80 )71 )
@@ -87,220 +78,6 @@
87 )78 )
8879
8980
90files_to_ignore = set(
91 ['.bzrignore', '.shelf', '.bzr', '.bzr.backup', '.bzrtags',
92 '.bzr-builddeb'])
93
94
95class DirWrapper(object):
96 def __init__(self, fileobj, mode='r'):
97 assert mode == 'r', mode
98 self.root = os.path.realpath(fileobj.read())
99
100 def __repr__(self):
101 return 'DirWrapper(%r)' % self.root
102
103 def getmembers(self, subdir=None):
104 if subdir is not None:
105 mydir = os.path.join(self.root, subdir)
106 else:
107 mydir = self.root
108 for child in os.listdir(mydir):
109 if subdir is not None:
110 child = os.path.join(subdir, child)
111 fi = FileInfo(self.root, child)
112 yield fi
113 if fi.isdir():
114 for v in self.getmembers(child):
115 yield v
116
117 def extractfile(self, member):
118 return open(member.fullpath)
119
120
121class FileInfo(object):
122
123 def __init__(self, root, filepath):
124 self.fullpath = os.path.join(root, filepath)
125 self.root = root
126 if filepath != '':
127 self.name = os.path.join(basename(root), filepath)
128 else:
129 self.name = basename(root)
130 self.type = None
131 stat = os.lstat(self.fullpath)
132 self.mode = stat.st_mode
133 if self.isdir():
134 self.name += '/'
135
136 def __repr__(self):
137 return 'FileInfo(%r)' % self.name
138
139 def isreg(self):
140 return stat.S_ISREG(self.mode)
141
142 def isdir(self):
143 return stat.S_ISDIR(self.mode)
144
145 def issym(self):
146 if stat.S_ISLNK(self.mode):
147 self.linkname = os.readlink(self.fullpath)
148 return True
149 else:
150 return False
151
152 def islnk(self):
153 # This could be accurate, but the use below seems like
154 # it wouldn't really care
155 return False
156
157
158def import_dir(tree, dir, file_ids_from=None):
159 dir_input = StringIO(dir)
160 dir_file = DirWrapper(dir_input)
161 import_archive(tree, dir_file, file_ids_from=file_ids_from)
162
163
164def do_directory(tt, trans_id, tree, relative_path, path):
165 if isdir(path) and tree.path2id(relative_path) is not None:
166 tt.cancel_deletion(trans_id)
167 else:
168 tt.create_directory(trans_id)
169
170
171def should_ignore(relative_path):
172 parts = splitpath(relative_path)
173 if not parts:
174 return False
175 for part in parts:
176 if part in files_to_ignore:
177 return True
178 if part.endswith(',v'):
179 return True
180
181
182def top_directory(path):
183 """Return the top directory given in a path."""
184 parts = osutils.splitpath(osutils.normpath(path))
185 if len(parts) > 0:
186 return parts[0]
187 return ''
188
189
190def common_directory(names):
191 """Determine a single directory prefix from a list of names"""
192 prefixes = set()
193 prefixes.update(map(top_directory, names))
194 if '' in prefixes:
195 prefixes.remove('')
196 if len(prefixes) != 1:
197 return None
198 prefix = prefixes.pop()
199 if prefix == '':
200 return None
201 return prefix
202
203
204def import_archive(tree, archive_file, file_ids_from=None):
205 prefix = common_directory(names_of_files(archive_file))
206 tt = TreeTransform(tree)
207
208 if file_ids_from is None:
209 file_ids_from = []
210
211 removed = set()
212 for path, entry in tree.inventory.iter_entries():
213 if entry.parent_id is None:
214 continue
215 trans_id = tt.trans_id_tree_path(path)
216 tt.delete_contents(trans_id)
217 removed.add(path)
218
219 added = set()
220 implied_parents = set()
221 seen = set()
222 for member in archive_file.getmembers():
223 if member.type == 'g':
224 # type 'g' is a header
225 continue
226 relative_path = member.name
227 relative_path = osutils.normpath(relative_path)
228 relative_path = relative_path.lstrip('/')
229 if prefix is not None:
230 relative_path = relative_path[len(prefix)+1:]
231 if relative_path == '' or relative_path == '.':
232 continue
233 if should_ignore(relative_path):
234 continue
235 add_implied_parents(implied_parents, relative_path)
236 trans_id = tt.trans_id_tree_path(relative_path)
237 added.add(relative_path.rstrip('/'))
238 path = tree.abspath(relative_path)
239 if member.name in seen:
240 if tt.final_kind(trans_id) == 'file':
241 tt.set_executability(None, trans_id)
242 tt.cancel_creation(trans_id)
243 seen.add(member.name)
244 if member.isreg() or member.islnk():
245 tt.create_file(file_iterator(archive_file.extractfile(member)),
246 trans_id)
247 executable = (member.mode & 0111) != 0
248 tt.set_executability(executable, trans_id)
249 elif member.isdir():
250 do_directory(tt, trans_id, tree, relative_path, path)
251 elif member.issym():
252 tt.create_symlink(member.linkname, trans_id)
253 else:
254 raise UnknownType(relative_path)
255 if tt.tree_file_id(trans_id) is None:
256 found = False
257 for other_tree in file_ids_from:
258 other_tree.lock_read()
259 try:
260 if other_tree.has_filename(relative_path):
261 file_id = other_tree.path2id(relative_path)
262 if file_id is not None:
263 tt.version_file(file_id, trans_id)
264 found = True
265 break
266 finally:
267 other_tree.unlock()
268 if not found:
269 name = basename(member.name.rstrip('/'))
270 file_id = generate_ids.gen_file_id(name)
271 tt.version_file(file_id, trans_id)
272
273 for relative_path in implied_parents.difference(added):
274 if relative_path == "":
275 continue
276 trans_id = tt.trans_id_tree_path(relative_path)
277 path = tree.abspath(relative_path)
278 do_directory(tt, trans_id, tree, relative_path, path)
279 if tt.tree_file_id(trans_id) is None:
280 found = False
281 for other_tree in file_ids_from:
282 other_tree.lock_read()
283 try:
284 if other_tree.has_filename(relative_path):
285 file_id = other_tree.path2id(relative_path)
286 if file_id is not None:
287 tt.version_file(file_id, trans_id)
288 found = True
289 break
290 finally:
291 other_tree.unlock()
292 if not found:
293 tt.version_file(trans_id, trans_id)
294 added.add(relative_path)
295
296 for path in removed.difference(added):
297 tt.unversion_file(tt.trans_id_tree_path(path))
298
299 for conflict in cook_conflicts(resolve_conflicts(tt), tt):
300 warning(conflict)
301 tt.apply()
302
303
304class DscCache(object):81class DscCache(object):
30582
306 def __init__(self, transport=None):83 def __init__(self, transport=None):

Subscribers

People subscribed via source and target branches