/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/msgeditor.py

  • Committer: Andrew Bennetts
  • Date: 2011-06-09 07:38:32 UTC
  • mto: This revision was merged to the branch mainline in revision 5964.
  • Revision ID: andrew.bennetts@canonical.com-20110609073832-dt6oww033iexli4l
Fix thinko in wording regarding stacking invariants and revisions with multiple parents.

Show diffs side-by-side

added added

removed removed

Lines of Context:
14
14
# along with this program; if not, write to the Free Software
15
15
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
16
 
 
17
 
17
18
"""Commit message editor support."""
18
19
 
19
20
import codecs
20
 
from io import (
21
 
    BytesIO,
22
 
    StringIO,
23
 
    )
24
21
import os
25
22
from subprocess import call
26
23
import sys
27
24
 
28
 
from . import (
29
 
    bedding,
 
25
from bzrlib import (
30
26
    cmdline,
31
27
    config,
32
28
    osutils,
34
30
    transport,
35
31
    ui,
36
32
    )
37
 
from .errors import BzrError
38
 
from .hooks import Hooks
39
 
 
40
 
 
41
 
class BadCommitMessageEncoding(BzrError):
42
 
 
43
 
    _fmt = 'The specified commit message contains characters unsupported by '\
44
 
        'the current encoding.'
 
33
from bzrlib.errors import BzrError, BadCommitMessageEncoding
 
34
from bzrlib.hooks import Hooks
45
35
 
46
36
 
47
37
def _get_editor():
48
 
    """Return sequence of possible editor binaries for the current platform"""
 
38
    """Return a sequence of possible editor binaries for the current platform"""
49
39
    try:
50
 
        yield os.environ["BRZ_EDITOR"], '$BRZ_EDITOR'
 
40
        yield os.environ["BZR_EDITOR"], '$BZR_EDITOR'
51
41
    except KeyError:
52
42
        pass
53
43
 
54
44
    e = config.GlobalStack().get('editor')
55
45
    if e is not None:
56
 
        yield e, bedding.config_path()
 
46
        yield e, config.config_filename()
57
47
 
58
48
    for varname in 'VISUAL', 'EDITOR':
59
49
        if varname in os.environ:
72
62
    for candidate, candidate_source in _get_editor():
73
63
        edargs = cmdline.split(candidate)
74
64
        try:
 
65
            ## mutter("trying editor: %r", (edargs +[filename]))
75
66
            x = call(edargs + [filename])
76
 
        except OSError as e:
 
67
        except OSError, e:
77
68
            if candidate_source is not None:
78
69
                # We tried this editor because some user configuration (an
79
70
                # environment variable or config file) said to try it.  Let
90
81
        else:
91
82
            break
92
83
    raise BzrError("Could not start any editor.\nPlease specify one with:\n"
93
 
                   " - $BRZ_EDITOR\n - editor=/some/path in %s\n"
94
 
                   " - $VISUAL\n - $EDITOR" %
95
 
                   bedding.config_path())
 
84
                   " - $BZR_EDITOR\n - editor=/some/path in %s\n"
 
85
                   " - $VISUAL\n - $EDITOR" % \
 
86
                    config.config_filename())
96
87
 
97
88
 
98
89
DEFAULT_IGNORE_LINE = "%(bar)s %(msg)s %(bar)s" % \
99
 
    {'bar': '-' * 14, 'msg': 'This line and the following will be ignored'}
 
90
    { 'bar' : '-' * 14, 'msg' : 'This line and the following will be ignored' }
100
91
 
101
92
 
102
93
def edit_commit_message(infotext, ignoreline=DEFAULT_IGNORE_LINE,
119
110
    :return:    commit message or None.
120
111
    """
121
112
 
122
 
    if start_message is not None:
 
113
    if not start_message is None:
123
114
        start_message = start_message.encode(osutils.get_user_encoding())
124
115
    infotext = infotext.encode(osutils.get_user_encoding(), 'replace')
125
116
    return edit_commit_message_encoded(infotext, ignoreline, start_message)
149
140
    msgfilename = None
150
141
    try:
151
142
        msgfilename, hasinfo = _create_temp_file_with_commit_template(
152
 
            infotext, ignoreline, start_message)
 
143
                                    infotext, ignoreline, start_message)
153
144
        if not msgfilename:
154
145
            return None
155
146
        basename = osutils.basename(msgfilename)
156
 
        msg_transport = transport.get_transport_from_path(
157
 
            osutils.dirname(msgfilename))
 
147
        msg_transport = transport.get_transport(osutils.dirname(msgfilename))
158
148
        reference_content = msg_transport.get_bytes(basename)
159
149
        if not _run_editor(msgfilename):
160
150
            return None
162
152
        if edited_content == reference_content:
163
153
            if not ui.ui_factory.confirm_action(
164
154
                u"Commit message was not edited, use anyway",
165
 
                "breezy.msgeditor.unchanged",
166
 
                    {}):
 
155
                "bzrlib.msgeditor.unchanged",
 
156
                {}):
167
157
                # Returning "" makes cmd_commit raise 'empty commit message
168
158
                # specified' which is a reasonable error, given the user has
169
159
                # rejected using the unedited template.
171
161
        started = False
172
162
        msg = []
173
163
        lastline, nlines = 0, 0
174
 
        with codecs.open(msgfilename, mode='rb', encoding=osutils.get_user_encoding()) as f:
 
164
        # codecs.open() ALWAYS opens file in binary mode but we need text mode
 
165
        # 'rU' mode useful when bzr.exe used on Cygwin (bialix 20070430)
 
166
        f = file(msgfilename, 'rU')
 
167
        try:
175
168
            try:
176
 
                for line in f:
 
169
                for line in codecs.getreader(osutils.get_user_encoding())(f):
177
170
                    stripped_line = line.strip()
178
171
                    # strip empty line before the log message starts
179
172
                    if not started:
192
185
                    msg.append(line)
193
186
            except UnicodeDecodeError:
194
187
                raise BadCommitMessageEncoding()
 
188
        finally:
 
189
            f.close()
195
190
 
196
191
        if len(msg) == 0:
197
192
            return ""
207
202
        if msgfilename is not None:
208
203
            try:
209
204
                os.unlink(msgfilename)
210
 
            except IOError as e:
 
205
            except IOError, e:
211
206
                trace.warning(
212
207
                    "failed to unlink %s: %s; ignored", msgfilename, e)
213
208
 
233
228
    import tempfile
234
229
    tmp_fileno, msgfilename = tempfile.mkstemp(prefix='bzr_log.',
235
230
                                               dir=tmpdir, text=True)
236
 
    with os.fdopen(tmp_fileno, 'wb') as msgfile:
 
231
    msgfile = os.fdopen(tmp_fileno, 'w')
 
232
    try:
237
233
        if start_message is not None:
238
 
            msgfile.write(b"%s\n" % start_message)
 
234
            msgfile.write("%s\n" % start_message)
239
235
 
240
236
        if infotext is not None and infotext != "":
241
237
            hasinfo = True
242
 
            trailer = b"\n\n%s\n\n%s" % (
243
 
                ignoreline.encode(osutils.get_user_encoding()), infotext)
244
 
            msgfile.write(trailer)
 
238
            msgfile.write("\n\n%s\n\n%s" %(ignoreline, infotext))
245
239
        else:
246
240
            hasinfo = False
 
241
    finally:
 
242
        msgfile.close()
247
243
 
248
244
    return (msgfilename, hasinfo)
249
245
 
258
254
    # TODO: Rather than running the status command, should prepare a draft of
259
255
    # the revision to be committed, then pause and ask the user to
260
256
    # confirm/write a message.
261
 
    from .status import show_tree_status
 
257
    from StringIO import StringIO       # must be unicode-safe
 
258
    from bzrlib.status import show_tree_status
262
259
    status_tmp = StringIO()
263
260
    show_tree_status(working_tree, specific_files=specific_files,
264
261
                     to_file=status_tmp, verbose=True)
276
273
    # TODO: Rather than running the status command, should prepare a draft of
277
274
    # the revision to be committed, then pause and ask the user to
278
275
    # confirm/write a message.
279
 
    from .diff import show_diff_trees
 
276
    from StringIO import StringIO       # must be unicode-safe
 
277
    from bzrlib.diff import show_diff_trees
280
278
 
281
279
    template = make_commit_message_template(working_tree, specific_files)
282
280
    template = template.encode(output_encoding, "replace")
283
281
 
284
282
    if diff:
285
 
        stream = BytesIO()
 
283
        stream = StringIO()
286
284
        show_diff_trees(working_tree.basis_tree(),
287
285
                        working_tree, stream, specific_files,
288
286
                        path_encoding=output_encoding)
289
 
        template = template + b'\n' + stream.getvalue()
 
287
        template = template + '\n' + stream.getvalue()
290
288
 
291
289
    return template
292
290
 
304
302
 
305
303
        These are all empty initially.
306
304
        """
307
 
        Hooks.__init__(self, "breezy.msgeditor", "hooks")
308
 
        self.add_hook(
309
 
            'set_commit_message',
310
 
            "Set a fixed commit message. "
311
 
            "set_commit_message is called with the "
312
 
            "breezy.commit.Commit object (so you can also change e.g. "
313
 
            "revision properties by editing commit.builder._revprops) and the "
314
 
            "message so far. set_commit_message must return the message to "
315
 
            "use or None if it should use the message editor as normal.",
316
 
            (2, 4))
317
 
        self.add_hook(
318
 
            'commit_message_template',
 
305
        Hooks.__init__(self, "bzrlib.msgeditor", "hooks")
 
306
        self.add_hook('commit_message_template',
319
307
            "Called when a commit message is being generated. "
320
 
            "commit_message_template is called with the breezy.commit.Commit "
 
308
            "commit_message_template is called with the bzrlib.commit.Commit "
321
309
            "object and the message that is known so far. "
322
310
            "commit_message_template must return a new message to use (which "
323
311
            "could be the same as it was given). When there are multiple "
329
317
hooks = MessageEditorHooks()
330
318
 
331
319
 
332
 
def set_commit_message(commit, start_message=None):
333
 
    """Sets the commit message.
334
 
    :param commit: Commit object for the active commit.
335
 
    :return: The commit message or None to continue using the message editor
336
 
    """
337
 
    start_message = None
338
 
    for hook in hooks['set_commit_message']:
339
 
        start_message = hook(commit, start_message)
340
 
    return start_message
341
 
 
342
 
 
343
320
def generate_commit_message_template(commit, start_message=None):
344
321
    """Generate a commit message template.
345
322