Fix bzr-fastimport when used with newer versions of python-fastimport.(Jelmer Vernooij)
[bzr-fastimport.git] / helpers.py
blob6bba6fd04e81b663a5b4388de941c1400cead54e
1 # Copyright (C) 2008 Canonical Ltd
3 # This program is free software; you can redistribute it and/or modify
4 # it under the terms of the GNU General Public License as published by
5 # the Free Software Foundation; either version 2 of the License, or
6 # (at your option) any later version.
8 # This program is distributed in the hope that it will be useful,
9 # but WITHOUT ANY WARRANTY; without even the implied warranty of
10 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 # GNU General Public License for more details.
13 # You should have received a copy of the GNU General Public License
14 # along with this program. If not, see <http://www.gnu.org/licenses/>.
16 """Miscellaneous useful stuff."""
18 import stat
21 def escape_commit_message(message):
22 """Replace xml-incompatible control characters."""
23 # This really ought to be provided by bzrlib.
24 # Code copied from bzrlib.commit.
26 # Python strings can include characters that can't be
27 # represented in well-formed XML; escape characters that
28 # aren't listed in the XML specification
29 # (http://www.w3.org/TR/REC-xml/#NT-Char).
30 import re
31 message, _ = re.subn(
32 u'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
33 lambda match: match.group(0).encode('unicode_escape'),
34 message)
35 return message
38 def best_format_for_objects_in_a_repository(repo):
39 """Find the high-level format for branches and trees given a repository.
41 When creating branches and working trees within a repository, Bazaar
42 defaults to using the default format which may not be the best choice.
43 This routine does a reverse lookup of the high-level format registry
44 to find the high-level format that a shared repository was most likely
45 created via.
47 :return: the BzrDirFormat or None if no matches were found.
48 """
49 # Based on code from bzrlib/info.py ...
50 from bzrlib import bzrdir
51 repo_format = repo._format
52 candidates = []
53 non_aliases = set(bzrdir.format_registry.keys())
54 non_aliases.difference_update(bzrdir.format_registry.aliases())
55 for key in non_aliases:
56 format = bzrdir.format_registry.make_bzrdir(key)
57 # LocalGitBzrDirFormat has no repository_format
58 if hasattr(format, "repository_format"):
59 if format.repository_format == repo_format:
60 candidates.append((key, format))
61 if len(candidates):
62 # Assume the first one. Is there any reason not to do that?
63 name, format = candidates[0]
64 return format
65 else:
66 return None
69 def open_destination_directory(location, format=None, verbose=True):
70 """Open a destination directory and return the BzrDir.
72 If destination has a control directory, it will be returned.
73 Otherwise, the destination should be empty or non-existent and
74 a shared repository will be created there.
76 :param location: the destination directory
77 :param format: the format to use or None for the default
78 :param verbose: display the format used if a repository is created.
79 :return: BzrDir for the destination
80 """
81 import os
82 from bzrlib import bzrdir, errors, trace, transport
83 try:
84 control, relpath = bzrdir.BzrDir.open_containing(location)
85 # XXX: Check the relpath is None here?
86 return control
87 except errors.NotBranchError:
88 pass
90 # If the directory exists, check it is empty. Otherwise create it.
91 if os.path.exists(location):
92 contents = os.listdir(location)
93 if contents:
94 errors.BzrCommandError("Destination must have a .bzr directory, "
95 " not yet exist or be empty - files found in %s" % (location,))
96 else:
97 try:
98 os.mkdir(location)
99 except IOError, ex:
100 errors.BzrCommandError("Unable to create %s: %s" %
101 (location, ex))
103 # Create a repository for the nominated format.
104 trace.note("Creating destination repository ...")
105 if format is None:
106 format = bzrdir.format_registry.make_bzrdir('default')
107 to_transport = transport.get_transport(location)
108 to_transport.ensure_base()
109 control = format.initialize_on_transport(to_transport)
110 repo = control.create_repository(shared=True)
111 if verbose:
112 from bzrlib.info import show_bzrdir_info
113 show_bzrdir_info(repo.bzrdir, verbose=0)
114 return control
117 def kind_to_mode(kind, executable):
118 if kind == "file":
119 if executable == True:
120 return stat.S_IFREG | 0755
121 elif executable == False:
122 return stat.S_IFREG | 0644
123 else:
124 raise AssertionError("Executable %r invalid" % executable)
125 elif kind == "symlink":
126 return stat.S_IFLNK
127 elif kind == "directory":
128 return stat.S_IFDIR
129 elif kind == "tree-reference":
130 return 0160000
131 else:
132 raise AssertionError("Unknown file kind '%s'" % kind)
135 def mode_to_kind(mode):
136 # Note: Output from git-fast-export slightly different to spec
137 if mode in (0644, 0100644):
138 return 'file', False
139 elif mode in (0755, 0100755):
140 return 'file', True
141 elif mode == 0040000:
142 return 'directory', False
143 elif mode == 0120000:
144 return 'symlink', False
145 elif mode == 0160000:
146 return 'tree-reference', False
147 else:
148 raise AssertionError("invalid mode %o" % mode)
151 def binary_stream(stream):
152 """Ensure a stream is binary on Windows.
154 :return: the stream
156 try:
157 import os
158 if os.name == 'nt':
159 fileno = getattr(stream, 'fileno', None)
160 if fileno:
161 no = fileno()
162 if no >= 0: # -1 means we're working as subprocess
163 import msvcrt
164 msvcrt.setmode(no, os.O_BINARY)
165 except ImportError:
166 pass
167 return stream
170 def single_plural(n, single, plural):
171 """Return a single or plural form of a noun based on number."""
172 if n == 1:
173 return single
174 else:
175 return plural
178 def invert_dictset(d):
179 """Invert a dictionary with keys matching a set of values, turned into lists."""
180 # Based on recipe from ASPN
181 result = {}
182 for k, c in d.iteritems():
183 for v in c:
184 keys = result.setdefault(v, [])
185 keys.append(k)
186 return result
189 def invert_dict(d):
190 """Invert a dictionary with keys matching each value turned into a list."""
191 # Based on recipe from ASPN
192 result = {}
193 for k, v in d.iteritems():
194 keys = result.setdefault(v, [])
195 keys.append(k)
196 return result