##// END OF EJS Templates
localrepo.commit: normalize commit message even for rawcommit....
localrepo.commit: normalize commit message even for rawcommit. This normalization consists of: - stripping trailing whitespace - always using "\n" as the line separator I think the main reason rawcommit was skipping this normalization was an attempt to preserve hashes during an hg->hg conversion. While this is a nice goal, it's not particularly interesting in practice. Since SHA-1 is so strong, the only safe way to do it is to have absolutely identical revisions. But: - if the original revision was created with a recent version of hg, the commit message will be the same, with or without that normalization - if it was created with an ancient version of hg that didn't do any normalization, even if the commit message is identical, the file list in the changelog is likely to be different (e.g. no removed files), and there were some old issues with e.g. extra file merging, which will end up changing the hash anyway - in any case, if one *really* has to preserve hashes, it's easier (and faster) to fake a partial conversion using something like: hg clone -U -r rev orig-repo new-repo hg -R new-repo log --template '#node# #node#\n' > new-repo/.hg/shamap Additionally, we've had some reports of problems arising from this lack of normalization - e.g. issue871, and a user that was wondering why hg export/hg import was not preserving hashes when there was nothing unusual going on (it was just import doing the normalization that had been skipped). This also means that it's even more unlikely to get identical revisions when going $VCS->hg->$VCS.

File last commit:

r4959:8933b8ea default
r6254:3667b6e4 default
Show More
lock.py
120 lines | 3.7 KiB | text/x-python | PythonLexer
# lock.py - simple locking scheme for mercurial
#
# Copyright 2005, 2006 Matt Mackall <mpm@selenic.com>
#
# This software may be used and distributed according to the terms
# of the GNU General Public License, incorporated herein by reference.
import errno, os, socket, time, util
class LockException(IOError):
def __init__(self, errno, strerror, filename, desc):
IOError.__init__(self, errno, strerror, filename)
self.desc = desc
class LockHeld(LockException):
def __init__(self, errno, filename, desc, locker):
LockException.__init__(self, errno, 'Lock held', filename, desc)
self.locker = locker
class LockUnavailable(LockException):
pass
class lock(object):
# lock is symlink on platforms that support it, file on others.
# symlink is used because create of directory entry and contents
# are atomic even over nfs.
# old-style lock: symlink to pid
# new-style lock: symlink to hostname:pid
_host = None
def __init__(self, file, timeout=-1, releasefn=None, desc=None):
self.f = file
self.held = 0
self.timeout = timeout
self.releasefn = releasefn
self.desc = desc
self.lock()
def __del__(self):
self.release()
def lock(self):
timeout = self.timeout
while 1:
try:
self.trylock()
return 1
except LockHeld, inst:
if timeout != 0:
time.sleep(1)
if timeout > 0:
timeout -= 1
continue
raise LockHeld(errno.ETIMEDOUT, inst.filename, self.desc,
inst.locker)
def trylock(self):
if lock._host is None:
lock._host = socket.gethostname()
lockname = '%s:%s' % (lock._host, os.getpid())
while not self.held:
try:
util.makelock(lockname, self.f)
self.held = 1
except (OSError, IOError), why:
if why.errno == errno.EEXIST:
locker = self.testlock()
if locker is not None:
raise LockHeld(errno.EAGAIN, self.f, self.desc,
locker)
else:
raise LockUnavailable(why.errno, why.strerror,
why.filename, self.desc)
def testlock(self):
"""return id of locker if lock is valid, else None.
If old-style lock, we cannot tell what machine locker is on.
with new-style lock, if locker is on this machine, we can
see if locker is alive. If locker is on this machine but
not alive, we can safely break lock.
The lock file is only deleted when None is returned.
"""
locker = util.readlock(self.f)
try:
host, pid = locker.split(":", 1)
except ValueError:
return locker
if host != lock._host:
return locker
try:
pid = int(pid)
except:
return locker
if util.testpid(pid):
return locker
# if locker dead, break lock. must do this with another lock
# held, or can race and break valid lock.
try:
l = lock(self.f + '.break')
l.trylock()
os.unlink(self.f)
l.release()
except (LockHeld, LockUnavailable):
return locker
def release(self):
if self.held:
self.held = 0
if self.releasefn:
self.releasefn()
try:
os.unlink(self.f)
except: pass