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
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):

Subscribers

People subscribed via source and target branches