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, write to the Free Software
15 # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17 """Miscellaneous useful stuff."""
20 def single_plural(n
, single
, plural
):
21 """Return a single or plural form of a noun based on number."""
28 def defines_to_dict(defines
):
29 """Convert a list of definition strings to a dictionary."""
33 for define
in defines
:
34 kv
= define
.split('=', 1)
36 result
[define
.strip()] = 1
38 result
[kv
[0].strip()] = kv
[1].strip()
43 """Invert a dictionary with keys matching each value turned into a list."""
44 # Based on recipe from ASPN
46 for k
, v
in d
.iteritems():
47 keys
= result
.setdefault(v
, [])
52 def invert_dictset(d
):
53 """Invert a dictionary with keys matching a set of values, turned into lists."""
54 # Based on recipe from ASPN
56 for k
, c
in d
.iteritems():
58 keys
= result
.setdefault(v
, [])
63 def _common_path_and_rest(l1
, l2
, common
=[]):
64 # From http://code.activestate.com/recipes/208993/
65 if len(l1
) < 1: return (common
, l1
, l2
)
66 if len(l2
) < 1: return (common
, l1
, l2
)
67 if l1
[0] != l2
[0]: return (common
, l1
, l2
)
68 return _common_path_and_rest(l1
[1:], l2
[1:], common
+[l1
[0]])
71 def common_path(path1
, path2
):
72 """Find the common bit of 2 paths."""
73 return ''.join(_common_path_and_rest(path1
, path2
)[0])
76 def common_directory(paths
):
77 """Find the deepest common directory of a list of paths.
79 :return: if no paths are provided, None is returned;
80 if there is no common directory, '' is returned;
81 otherwise the common directory with a trailing / is returned.
83 from bzrlib
import osutils
84 def get_dir_with_slash(path
):
85 if path
== '' or path
.endswith('/'):
88 dirname
, basename
= osutils
.split(path
)
97 return get_dir_with_slash(paths
[0])
99 common
= common_path(paths
[0], paths
[1])
100 for path
in paths
[2:]:
101 common
= common_path(common
, path
)
102 return get_dir_with_slash(common
)
105 def escape_commit_message(message
):
106 """Replace xml-incompatible control characters."""
107 # This really ought to be provided by bzrlib.
108 # Code copied from bzrlib.commit.
110 # Python strings can include characters that can't be
111 # represented in well-formed XML; escape characters that
112 # aren't listed in the XML specification
113 # (http://www.w3.org/TR/REC-xml/#NT-Char).
115 message
, _
= re
.subn(
116 u
'[^\x09\x0A\x0D\u0020-\uD7FF\uE000-\uFFFD]+',
117 lambda match
: match
.group(0).encode('unicode_escape'),
122 def binary_stream(stream
):
123 """Ensure a stream is binary on Windows.
130 fileno
= getattr(stream
, 'fileno', None)
133 if no
>= 0: # -1 means we're working as subprocess
135 msvcrt
.setmode(no
, os
.O_BINARY
)
141 def best_format_for_objects_in_a_repository(repo
):
142 """Find the high-level format for branches and trees given a repository.
144 When creating branches and working trees within a repository, Bazaar
145 defaults to using the default format which may not be the best choice.
146 This routine does a reverse lookup of the high-level format registry
147 to find the high-level format that a shared repository was most likely
150 :return: the BzrDirFormat or None if no matches were found.
152 # Based on code from bzrlib/info.py ...
153 from bzrlib
import bzrdir
154 repo_format
= repo
._format
156 non_aliases
= set(bzrdir
.format_registry
.keys())
157 non_aliases
.difference_update(bzrdir
.format_registry
.aliases())
158 for key
in non_aliases
:
159 format
= bzrdir
.format_registry
.make_bzrdir(key
)
160 # LocalGitBzrDirFormat has no repository_format
161 if hasattr(format
, "repository_format"):
162 if format
.repository_format
== repo_format
:
163 candidates
.append((key
, format
))
165 # Assume the first one. Is there any reason not to do that?
166 name
, format
= candidates
[0]
172 def open_destination_directory(location
, format
=None, verbose
=True):
173 """Open a destination directory and return the BzrDir.
175 If destination has a control directory, it will be returned.
176 Otherwise, the destination should be empty or non-existent and
177 a shared repository will be created there.
179 :param location: the destination directory
180 :param format: the format to use or None for the default
181 :param verbose: display the format used if a repository is created.
182 :return: BzrDir for the destination
185 from bzrlib
import bzrdir
, errors
, trace
, transport
187 control
, relpath
= bzrdir
.BzrDir
.open_containing(location
)
188 # XXX: Check the relpath is None here?
190 except errors
.NotBranchError
:
193 # If the directory exists, check it is empty. Otherwise create it.
194 if os
.path
.exists(location
):
195 contents
= os
.listdir(location
)
197 errors
.BzrCommandError("Destination must have a .bzr directory, "
198 " not yet exist or be empty - files found in %s" % (location
,))
203 errors
.BzrCommandError("Unable to create %s: %s" %
206 # Create a repository for the nominated format.
207 trace
.note("Creating destination repository ...")
209 format
= bzrdir
.format_registry
.make_bzrdir('default')
210 to_transport
= transport
.get_transport(location
)
211 to_transport
.ensure_base()
212 control
= format
.initialize_on_transport(to_transport
)
213 repo
= control
.create_repository(shared
=True)
215 from bzrlib
.info
import show_bzrdir_info
216 show_bzrdir_info(repo
.bzrdir
, verbose
=0)