Skip to content

Instantly share code, notes, and snippets.

@ehiggs
Created May 3, 2010 14:49
Show Gist options
  • Save ehiggs/388161 to your computer and use it in GitHub Desktop.
Save ehiggs/388161 to your computer and use it in GitHub Desktop.
Bazaar message editing.
def _get_editor():
"""Return a sequence of possible editor binaries for the current platform"""
try:
yield os.environ["BZR_EDITOR"], '$BZR_EDITOR'
except KeyError:
pass
e = config.GlobalConfig().get_editor()
if e is not None:
yield e, config.config_filename()
for varname in 'VISUAL', 'EDITOR':
if varname in os.environ:
yield os.environ[varname], '$' + varname
if sys.platform == 'win32':
for editor in 'wordpad.exe', 'notepad.exe':
yield editor, None
else:
for editor in ['/usr/bin/editor', 'vi', 'pico', 'nano', 'joe']:
yield editor, None
def _run_editor(filename):
"""Try to execute an editor to edit the commit message."""
for candidate, candidate_source in _get_editor():
edargs = candidate.split(' ')
try:
## mutter("trying editor: %r", (edargs +[filename]))
x = call(edargs + [filename])
except OSError, e:
if candidate_source is not None:
# We tried this editor because some user configuration (an
# environment variable or config file) said to try it. Let
# the user know their configuration is broken.
trace.warning(
'Could not start editor "%s" (specified by %s): %s\n'
% (candidate, candidate_source, str(e)))
continue
raise
if x == 0:
return True
elif x == 127:
continue
else:
break
raise BzrError("Could not start any editor.\nPlease specify one with:\n"
" - $BZR_EDITOR\n - editor=/some/path in %s\n"
" - $VISUAL\n - $EDITOR" % \
config.config_filename())
DEFAULT_IGNORE_LINE = "%(bar)s %(msg)s %(bar)s" % \
{ 'bar' : '-' * 14, 'msg' : 'This line and the following will be ignored' }
def edit_commit_message(infotext, ignoreline=DEFAULT_IGNORE_LINE,
start_message=None):
"""Let the user edit a commit message in a temp file.
This is run if they don't give a message or
message-containing file on the command line.
:param infotext: Text to be displayed at bottom of message
for the user's reference;
currently similar to 'bzr status'.
:param ignoreline: The separator to use above the infotext.
:param start_message: The text to place above the separator, if any.
This will not be removed from the message
after the user has edited it.
:return: commit message or None.
"""
if not start_message is None:
start_message = start_message.encode(osutils.get_user_encoding())
infotext = infotext.encode(osutils.get_user_encoding(), 'replace')
return edit_commit_message_encoded(infotext, ignoreline, start_message)
def edit_commit_message_encoded(infotext, ignoreline=DEFAULT_IGNORE_LINE,
start_message=None):
"""Let the user edit a commit message in a temp file.
This is run if they don't give a message or
message-containing file on the command line.
:param infotext: Text to be displayed at bottom of message
for the user's reference;
currently similar to 'bzr status'.
The string is already encoded
:param ignoreline: The separator to use above the infotext.
:param start_message: The text to place above the separator, if any.
This will not be removed from the message
after the user has edited it.
The string is already encoded
:return: commit message or None.
"""
msgfilename = None
try:
msgfilename, hasinfo = _create_temp_file_with_commit_template(
infotext, ignoreline, start_message)
if not msgfilename:
return None
basename = osutils.basename(msgfilename)
msg_transport = transport.get_transport(osutils.dirname(msgfilename))
reference_content = msg_transport.get_bytes(basename)
if not _run_editor(msgfilename):
return None
edited_content = msg_transport.get_bytes(basename)
if edited_content == reference_content:
if not ui.ui_factory.get_boolean(
"Commit message was not edited, use anyway"):
# Returning "" makes cmd_commit raise 'empty commit message
# specified' which is a reasonable error, given the user has
# rejected using the unedited template.
return ""
started = False
msg = []
lastline, nlines = 0, 0
# codecs.open() ALWAYS opens file in binary mode but we need text mode
# 'rU' mode useful when bzr.exe used on Cygwin (bialix 20070430)
f = file(msgfilename, 'rU')
try:
try:
for line in codecs.getreader(osutils.get_user_encoding())(f):
stripped_line = line.strip()
# strip empty line before the log message starts
if not started:
if stripped_line != "":
started = True
else:
continue
# check for the ignore line only if there
# is additional information at the end
if hasinfo and stripped_line == ignoreline:
break
nlines += 1
# keep track of the last line that had some content
if stripped_line != "":
lastline = nlines
msg.append(line)
except UnicodeDecodeError:
raise BadCommitMessageEncoding()
finally:
f.close()
if len(msg) == 0:
return ""
# delete empty lines at the end
del msg[lastline:]
# add a newline at the end, if needed
if not msg[-1].endswith("\n"):
return "%s%s" % ("".join(msg), "\n")
else:
return "".join(msg)
finally:
# delete the msg file in any case
if msgfilename is not None:
try:
os.unlink(msgfilename)
except IOError, e:
trace.warning(
"failed to unlink %s: %s; ignored", msgfilename, e)
def _create_temp_file_with_commit_template(infotext,
ignoreline=DEFAULT_IGNORE_LINE,
start_message=None):
"""Create temp file and write commit template in it.
:param infotext: Text to be displayed at bottom of message
for the user's reference;
currently similar to 'bzr status'.
The text is already encoded.
:param ignoreline: The separator to use above the infotext.
:param start_message: The text to place above the separator, if any.
This will not be removed from the message
after the user has edited it.
The string is already encoded
:return: 2-tuple (temp file name, hasinfo)
"""
import tempfile
tmp_fileno, msgfilename = tempfile.mkstemp(prefix='bzr_log.',
dir='.',
text=True)
msgfilename = osutils.basename(msgfilename)
msgfile = os.fdopen(tmp_fileno, 'w')
try:
if start_message is not None:
msgfile.write("%s\n" % start_message)
if infotext is not None and infotext != "":
hasinfo = True
msgfile.write("\n\n%s\n\n%s" %(ignoreline, infotext))
else:
hasinfo = False
finally:
msgfile.close()
return (msgfilename, hasinfo)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment