1
# Copyright (C) 2005, 2006 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
18
"""Commit message editor support."""
23
from subprocess import call
27
import bzrlib.config as config
28
from bzrlib.errors import BzrError
29
from bzrlib.trace import warning, mutter
33
"""Return a sequence of possible editor binaries for the current platform"""
35
yield os.environ["BZR_EDITOR"]
39
e = config.GlobalConfig().get_editor()
43
for varname in 'VISUAL', 'EDITOR':
44
if varname in os.environ:
45
yield os.environ[varname]
47
if sys.platform == 'win32':
48
for editor in 'wordpad.exe', 'notepad.exe':
51
for editor in ['/usr/bin/editor', 'vi', 'pico', 'nano', 'joe']:
55
def _run_editor(filename):
56
"""Try to execute an editor to edit the commit message."""
57
for e in _get_editor():
60
## mutter("trying editor: %r", (edargs +[filename]))
61
x = call(edargs + [filename])
63
# We're searching for an editor, so catch safe errors and continue
64
if e.errno in (errno.ENOENT, ):
73
raise BzrError("Could not start any editor.\nPlease specify one with:\n"
74
" - $BZR_EDITOR\n - editor=/some/path in %s\n"
75
" - $VISUAL\n - $EDITOR" % \
76
config.config_filename())
79
DEFAULT_IGNORE_LINE = "%(bar)s %(msg)s %(bar)s" % \
80
{ 'bar' : '-' * 14, 'msg' : 'This line and the following will be ignored' }
83
def edit_commit_message(infotext, ignoreline=DEFAULT_IGNORE_LINE,
85
"""Let the user edit a commit message in a temp file.
87
This is run if they don't give a message or
88
message-containing file on the command line.
91
Text to be displayed at bottom of message for
92
the user's reference; currently similar to
96
The separator to use above the infotext.
99
The text to place above the separator, if any. This will not be
100
removed from the message after the user has edited it.
106
tmp_fileno, msgfilename = tempfile.mkstemp(prefix='bzr_log.', dir=u'.')
107
msgfile = os.close(tmp_fileno)
109
if start_message is not None:
111
msgfile = file(msgfilename, "w")
112
msgfile.write("%s\n" % start_message.encode(
113
bzrlib.user_encoding, 'replace'))
114
if infotext is not None and infotext != "":
117
msgfile = file(msgfilename, "w")
119
msgfile.write("\n\n%s\n\n%s" % (ignoreline,
120
infotext.encode(bzrlib.user_encoding, 'replace')))
127
if not _run_editor(msgfilename):
132
lastline, nlines = 0, 0
133
for line in codecs.open(msgfilename, 'r', bzrlib.user_encoding):
134
stripped_line = line.strip()
135
# strip empty line before the log message starts
137
if stripped_line != "":
141
# check for the ignore line only if there
142
# is additional information at the end
143
if hasinfo and stripped_line == ignoreline:
146
# keep track of the last line that had some content
147
if stripped_line != "":
153
# delete empty lines at the end
155
# add a newline at the end, if needed
156
if not msg[-1].endswith("\n"):
157
return "%s%s" % ("".join(msg), "\n")
161
# delete the msg file in any case
162
if msgfilename is not None:
164
os.unlink(msgfilename)
166
warning("failed to unlink %s: %s; ignored", msgfilename, e)
169
def make_commit_message_template(working_tree, specific_files):
170
"""Prepare a template file for a commit into a branch.
172
Returns a unicode string containing the template.
174
# TODO: Should probably be given the WorkingTree not the branch
176
# TODO: make provision for this to be overridden or modified by a hook
178
# TODO: Rather than running the status command, should prepare a draft of
179
# the revision to be committed, then pause and ask the user to
180
# confirm/write a message.
181
from StringIO import StringIO # must be unicode-safe
182
from bzrlib.status import show_tree_status
183
status_tmp = StringIO()
184
show_tree_status(working_tree, specific_files=specific_files,
186
return status_tmp.getvalue()