/breezy/unstable

To get this branch, use:
bzr branch https://code.breezy-vcs.org/breezy/unstable

« back to all changes in this revision

Viewing changes to breezy/plugins/fastimport/helpers.py

  • Committer: Jelmer Vernooij
  • Date: 2017-05-24 01:39:33 UTC
  • mfrom: (3815.3776.6)
  • Revision ID: jelmer@jelmer.uk-20170524013933-ir4y4tqtrsiz2ka2
New upstream snapshot.

Show diffs side-by-side

added added

removed removed

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