##// END OF EJS Templates
diff: don't crash when merged-in addition was removed (issue4786)...
diff: don't crash when merged-in addition was removed (issue4786) During a merge, if the user removes a file that came from parent 2 and did not exist in parent 1, the file's status will be "removed". This surprises the diff code, which crashes because it expects removed files exist in parent 1. This has been broken since 377124ba6b10 (trydiff: use 'not in addedset' for symmetry with 'not in removedset', 2014-12-23). Fix by fixing up the list of removed file, similar to how we currently fix up the list of modified and added files during a merge.

File last commit:

r26185:e8f9dffc default
r27901:29c8e35d default
Show More
blackbox.py
164 lines | 5.5 KiB | text/x-python | PythonLexer
# blackbox.py - log repository events to a file for post-mortem debugging
#
# Copyright 2010 Nicolas Dumazet
# Copyright 2013 Facebook, Inc.
#
# This software may be used and distributed according to the terms of the
# GNU General Public License version 2 or any later version.
"""log repository events to a blackbox for debugging
Logs event information to .hg/blackbox.log to help debug and diagnose problems.
The events that get logged can be configured via the blackbox.track config key.
Examples::
[blackbox]
track = *
[blackbox]
track = command, commandfinish, commandexception, exthook, pythonhook
[blackbox]
track = incoming
[blackbox]
# limit the size of a log file
maxsize = 1.5 MB
# rotate up to N log files when the current one gets too big
maxfiles = 3
"""
from mercurial import util, cmdutil
from mercurial.i18n import _
import errno, os, re
cmdtable = {}
command = cmdutil.command(cmdtable)
# Note for extension authors: ONLY specify testedwith = 'internal' for
# extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
testedwith = 'internal'
lastblackbox = None
def wrapui(ui):
class blackboxui(ui.__class__):
@util.propertycache
def track(self):
return self.configlist('blackbox', 'track', ['*'])
def _openlogfile(self):
def rotate(oldpath, newpath):
try:
os.unlink(newpath)
except OSError as err:
if err.errno != errno.ENOENT:
self.debug("warning: cannot remove '%s': %s\n" %
(newpath, err.strerror))
try:
if newpath:
os.rename(oldpath, newpath)
except OSError as err:
if err.errno != errno.ENOENT:
self.debug("warning: cannot rename '%s' to '%s': %s\n" %
(newpath, oldpath, err.strerror))
fp = self._bbopener('blackbox.log', 'a')
maxsize = self.configbytes('blackbox', 'maxsize', 1048576)
if maxsize > 0:
st = os.fstat(fp.fileno())
if st.st_size >= maxsize:
path = fp.name
fp.close()
maxfiles = self.configint('blackbox', 'maxfiles', 7)
for i in xrange(maxfiles - 1, 1, -1):
rotate(oldpath='%s.%d' % (path, i - 1),
newpath='%s.%d' % (path, i))
rotate(oldpath=path,
newpath=maxfiles > 0 and path + '.1')
fp = self._bbopener('blackbox.log', 'a')
return fp
def log(self, event, *msg, **opts):
global lastblackbox
super(blackboxui, self).log(event, *msg, **opts)
if not '*' in self.track and not event in self.track:
return
if util.safehasattr(self, '_blackbox'):
blackbox = self._blackbox
elif util.safehasattr(self, '_bbopener'):
try:
self._blackbox = self._openlogfile()
except (IOError, OSError) as err:
self.debug('warning: cannot write to blackbox.log: %s\n' %
err.strerror)
del self._bbopener
self._blackbox = None
blackbox = self._blackbox
else:
# certain ui instances exist outside the context of
# a repo, so just default to the last blackbox that
# was seen.
blackbox = lastblackbox
if blackbox:
date = util.datestr(None, '%Y/%m/%d %H:%M:%S')
user = util.getuser()
pid = str(os.getpid())
formattedmsg = msg[0] % msg[1:]
try:
blackbox.write('%s %s (%s)> %s' %
(date, user, pid, formattedmsg))
except IOError as err:
self.debug('warning: cannot write to blackbox.log: %s\n' %
err.strerror)
lastblackbox = blackbox
def setrepo(self, repo):
self._bbopener = repo.vfs
ui.__class__ = blackboxui
def uisetup(ui):
wrapui(ui)
def reposetup(ui, repo):
# During 'hg pull' a httppeer repo is created to represent the remote repo.
# It doesn't have a .hg directory to put a blackbox in, so we don't do
# the blackbox setup for it.
if not repo.local():
return
if util.safehasattr(ui, 'setrepo'):
ui.setrepo(repo)
@command('^blackbox',
[('l', 'limit', 10, _('the number of events to show')),
],
_('hg blackbox [OPTION]...'))
def blackbox(ui, repo, *revs, **opts):
'''view the recent repository events
'''
if not os.path.exists(repo.join('blackbox.log')):
return
limit = opts.get('limit')
blackbox = repo.vfs('blackbox.log', 'r')
lines = blackbox.read().split('\n')
count = 0
output = []
for line in reversed(lines):
if count >= limit:
break
# count the commands by matching lines like: 2013/01/23 19:13:36 root>
if re.match('^\d{4}/\d{2}/\d{2} \d{2}:\d{2}:\d{2} .*> .*', line):
count += 1
output.append(line)
ui.status('\n'.join(reversed(output)))