mirror of
https://github.com/facebook/sapling.git
synced 2024-10-11 01:07:15 +03:00
d36ae7f264
For the PyPy repo with 744 branches and 843 branch heads, this brings hg log -r default over NFS from: CallCount Recursive Total(ms) Inline(ms) module:lineno(function) 3249 0 1.3222 1.3222 <open> 3244 0 0.6211 0.6211 <method 'close' of 'file' objects> 3243 0 0.0800 0.0800 <method 'read' of 'file' objects> 3241 0 0.0660 0.0660 <method 'seek' of 'file' objects> 3905 0 0.0476 0.0476 <zlib.decompress> 3281 0 2.6756 0.0472 mercurial.changelog:182(read) +3281 0 2.5256 0.0453 +mercurial.revlog:881(revision) +3276 0 0.0389 0.0196 +mercurial.changelog:28(decodeextra) +6562 0 0.0123 0.0123 +<method 'split' of 'str' objects> +6562 0 0.0408 0.0073 +mercurial.encoding:61(tolocal) +3281 0 0.0054 0.0054 +<method 'index' of 'str' objects> 3241 0 2.2464 0.0456 mercurial.revlog:818(_loadchunk) +3241 0 0.6205 0.6205 +<method 'close' of 'file' objects> +3241 0 0.0765 0.0765 +<method 'read' of 'file' objects> +3241 0 0.0660 0.0660 +<method 'seek' of 'file' objects> +3241 0 1.4209 0.0135 +mercurial.store:374(__call__) +3241 0 0.0122 0.0107 +mercurial.revlog:810(_addchunk) 3281 0 2.5256 0.0453 mercurial.revlog:881(revision) +3280 0 0.0175 0.0175 +mercurial.revlog:305(rev) +3281 0 2.2819 0.0119 +mercurial.revlog:847(_chunkraw) +3281 0 0.0603 0.0083 +mercurial.revlog:945(_checkhash) +3281 0 0.0051 0.0051 +mercurial.revlog:349(flags) +3281 0 0.0040 0.0040 +<mercurial.mpatch.patches> 13682 0 0.0479 0.0248 <method 'decode' of 'str' objects> +7418 0 0.0228 0.0076 +encodings.utf_8:15(decode) +1 0 0.0003 0.0000 +encodings:71(search_function) 3248 0 1.3995 0.0246 mercurial.scmutil:218(__call__) +3248 0 1.3222 1.3222 +<open> +3248 0 0.0235 0.0184 +os.path:80(split) +3248 0 0.0084 0.0068 +mercurial.scmutil:92(__call__) Time: real 2.750 secs (user 0.680+0.000 sys 0.360+0.000) down to: CallCount Recursive Total(ms) Inline(ms) module:lineno(function) 55 31 0.0197 0.0163 <__import__> +1 0 0.0006 0.0002 +mercurial.context:8(<module>) +1 0 0.0042 0.0001 +mercurial.revlog:12(<module>) +1 0 0.0002 0.0001 +mercurial.match:8(<module>) +1 0 0.0003 0.0001 +mercurial.dirstate:7(<module>) +1 0 0.0057 0.0001 +mercurial.changelog:8(<module>) 1 0 0.0117 0.0032 mercurial.localrepo:525(_readbranchcache) +844 0 0.0015 0.0015 +<binascii.unhexlify> +845 0 0.0010 0.0010 +<method 'split' of 'str' objects> +843 0 0.0045 0.0009 +mercurial.encoding:61(tolocal) +843 0 0.0004 0.0004 +<method 'setdefault' of 'dict' objects> +1 0 0.0003 0.0003 +<method 'close' of 'file' objects> 3 0 0.0029 0.0029 <method 'read' of 'file' objects> 9 0 0.0018 0.0018 <open> 990 0 0.0017 0.0017 <binascii.unhexlify> 53 0 0.0016 0.0016 mercurial.demandimport:43(__init__) 862 0 0.0015 0.0015 <_codecs.utf_8_decode> 862 0 0.0037 0.0014 <method 'decode' of 'str' objects> +862 0 0.0023 0.0008 +encodings.utf_8:15(decode) 981 0 0.0011 0.0011 <method 'split' of 'str' objects> 861 0 0.0046 0.0009 mercurial.encoding:61(tolocal) +861 0 0.0037 0.0014 +<method 'decode' of 'str' objects> 862 0 0.0023 0.0008 encodings.utf_8:15(decode) +862 0 0.0015 0.0015 +<_codecs.utf_8_decode> 4 0 0.0008 0.0008 <method 'close' of 'file' objects> 179 154 0.0202 0.0004 mercurial.demandimport:83(__getattribute__) +36 11 0.0199 0.0003 +mercurial.demandimport:55(_load) +72 0 0.0001 0.0001 +mercurial.demandimport:83(__getattribute__) +36 0 0.0000 0.0000 +<getattr> 1 0 0.0015 0.0004 mercurial.tags:148(_readtagcache) Time: real 0.060 secs (user 0.030+0.000 sys 0.010+0.000)
255 lines
7.6 KiB
Python
255 lines
7.6 KiB
Python
# Mercurial bookmark support code
|
|
#
|
|
# Copyright 2008 David Soria Parra <dsp@php.net>
|
|
#
|
|
# This software may be used and distributed according to the terms of the
|
|
# GNU General Public License version 2 or any later version.
|
|
|
|
from mercurial.i18n import _
|
|
from mercurial.node import hex
|
|
from mercurial import encoding, error, util
|
|
import errno, os
|
|
|
|
def valid(mark):
|
|
for c in (':', '\0', '\n', '\r'):
|
|
if c in mark:
|
|
return False
|
|
return True
|
|
|
|
def read(repo):
|
|
'''Parse .hg/bookmarks file and return a dictionary
|
|
|
|
Bookmarks are stored as {HASH}\\s{NAME}\\n (localtags format) values
|
|
in the .hg/bookmarks file.
|
|
Read the file and return a (name=>nodeid) dictionary
|
|
'''
|
|
bookmarks = {}
|
|
try:
|
|
for line in repo.opener('bookmarks'):
|
|
line = line.strip()
|
|
if not line:
|
|
continue
|
|
if ' ' not in line:
|
|
repo.ui.warn(_('malformed line in .hg/bookmarks: %r\n') % line)
|
|
continue
|
|
sha, refspec = line.split(' ', 1)
|
|
refspec = encoding.tolocal(refspec)
|
|
try:
|
|
bookmarks[refspec] = repo.changelog.lookup(sha)
|
|
except LookupError:
|
|
pass
|
|
except IOError, inst:
|
|
if inst.errno != errno.ENOENT:
|
|
raise
|
|
return bookmarks
|
|
|
|
def readcurrent(repo):
|
|
'''Get the current bookmark
|
|
|
|
If we use gittishsh branches we have a current bookmark that
|
|
we are on. This function returns the name of the bookmark. It
|
|
is stored in .hg/bookmarks.current
|
|
'''
|
|
mark = None
|
|
try:
|
|
file = repo.opener('bookmarks.current')
|
|
except IOError, inst:
|
|
if inst.errno != errno.ENOENT:
|
|
raise
|
|
return None
|
|
try:
|
|
# No readline() in posixfile_nt, reading everything is cheap
|
|
mark = encoding.tolocal((file.readlines() or [''])[0])
|
|
if mark == '' or mark not in repo._bookmarks:
|
|
mark = None
|
|
finally:
|
|
file.close()
|
|
return mark
|
|
|
|
def write(repo):
|
|
'''Write bookmarks
|
|
|
|
Write the given bookmark => hash dictionary to the .hg/bookmarks file
|
|
in a format equal to those of localtags.
|
|
|
|
We also store a backup of the previous state in undo.bookmarks that
|
|
can be copied back on rollback.
|
|
'''
|
|
refs = repo._bookmarks
|
|
|
|
if repo._bookmarkcurrent not in refs:
|
|
setcurrent(repo, None)
|
|
for mark in refs.keys():
|
|
if not valid(mark):
|
|
raise util.Abort(_("bookmark '%s' contains illegal "
|
|
"character" % mark))
|
|
|
|
wlock = repo.wlock()
|
|
try:
|
|
|
|
file = repo.opener('bookmarks', 'w', atomictemp=True)
|
|
for refspec, node in refs.iteritems():
|
|
file.write("%s %s\n" % (hex(node), encoding.fromlocal(refspec)))
|
|
file.close()
|
|
|
|
# touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
|
|
try:
|
|
os.utime(repo.sjoin('00changelog.i'), None)
|
|
except OSError:
|
|
pass
|
|
|
|
finally:
|
|
wlock.release()
|
|
|
|
def setcurrent(repo, mark):
|
|
'''Set the name of the bookmark that we are currently on
|
|
|
|
Set the name of the bookmark that we are on (hg update <bookmark>).
|
|
The name is recorded in .hg/bookmarks.current
|
|
'''
|
|
current = repo._bookmarkcurrent
|
|
if current == mark:
|
|
return
|
|
|
|
if mark not in repo._bookmarks:
|
|
mark = ''
|
|
if not valid(mark):
|
|
raise util.Abort(_("bookmark '%s' contains illegal "
|
|
"character" % mark))
|
|
|
|
wlock = repo.wlock()
|
|
try:
|
|
file = repo.opener('bookmarks.current', 'w', atomictemp=True)
|
|
file.write(encoding.fromlocal(mark))
|
|
file.close()
|
|
finally:
|
|
wlock.release()
|
|
repo._bookmarkcurrent = mark
|
|
|
|
def unsetcurrent(repo):
|
|
wlock = repo.wlock()
|
|
try:
|
|
try:
|
|
util.unlink(repo.join('bookmarks.current'))
|
|
repo._bookmarkcurrent = None
|
|
except OSError, inst:
|
|
if inst.errno != errno.ENOENT:
|
|
raise
|
|
finally:
|
|
wlock.release()
|
|
|
|
def updatecurrentbookmark(repo, oldnode, curbranch):
|
|
try:
|
|
return update(repo, oldnode, repo.branchtip(curbranch))
|
|
except error.RepoLookupError:
|
|
if curbranch == "default": # no default branch!
|
|
return update(repo, oldnode, repo.lookup("tip"))
|
|
else:
|
|
raise util.Abort(_("branch %s not found") % curbranch)
|
|
|
|
def update(repo, parents, node):
|
|
marks = repo._bookmarks
|
|
update = False
|
|
cur = repo._bookmarkcurrent
|
|
if not cur:
|
|
return False
|
|
|
|
toupdate = [b for b in marks if b.split('@', 1)[0] == cur.split('@', 1)[0]]
|
|
for mark in toupdate:
|
|
if mark and marks[mark] in parents:
|
|
old = repo[marks[mark]]
|
|
new = repo[node]
|
|
if new in old.descendants() and mark == cur:
|
|
marks[cur] = new.node()
|
|
update = True
|
|
if mark != cur:
|
|
del marks[mark]
|
|
if update:
|
|
repo._writebookmarks(marks)
|
|
return update
|
|
|
|
def listbookmarks(repo):
|
|
# We may try to list bookmarks on a repo type that does not
|
|
# support it (e.g., statichttprepository).
|
|
marks = getattr(repo, '_bookmarks', {})
|
|
|
|
d = {}
|
|
for k, v in marks.iteritems():
|
|
# don't expose local divergent bookmarks
|
|
if '@' not in k or k.endswith('@'):
|
|
d[k] = hex(v)
|
|
return d
|
|
|
|
def pushbookmark(repo, key, old, new):
|
|
w = repo.wlock()
|
|
try:
|
|
marks = repo._bookmarks
|
|
if hex(marks.get(key, '')) != old:
|
|
return False
|
|
if new == '':
|
|
del marks[key]
|
|
else:
|
|
if new not in repo:
|
|
return False
|
|
marks[key] = repo[new].node()
|
|
write(repo)
|
|
return True
|
|
finally:
|
|
w.release()
|
|
|
|
def updatefromremote(ui, repo, remote, path):
|
|
ui.debug("checking for updated bookmarks\n")
|
|
rb = remote.listkeys('bookmarks')
|
|
changed = False
|
|
for k in rb.keys():
|
|
if k in repo._bookmarks:
|
|
nr, nl = rb[k], repo._bookmarks[k]
|
|
if nr in repo:
|
|
cr = repo[nr]
|
|
cl = repo[nl]
|
|
if cl.rev() >= cr.rev():
|
|
continue
|
|
if cr in cl.descendants():
|
|
repo._bookmarks[k] = cr.node()
|
|
changed = True
|
|
ui.status(_("updating bookmark %s\n") % k)
|
|
else:
|
|
# find a unique @ suffix
|
|
for x in range(1, 100):
|
|
n = '%s@%d' % (k, x)
|
|
if n not in repo._bookmarks:
|
|
break
|
|
# try to use an @pathalias suffix
|
|
# if an @pathalias already exists, we overwrite (update) it
|
|
for p, u in ui.configitems("paths"):
|
|
if path == u:
|
|
n = '%s@%s' % (k, p)
|
|
|
|
repo._bookmarks[n] = cr.node()
|
|
changed = True
|
|
ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
|
|
elif rb[k] in repo:
|
|
# add remote bookmarks for changes we already have
|
|
repo._bookmarks[k] = repo[rb[k]].node()
|
|
changed = True
|
|
ui.status(_("adding remote bookmark %s\n") % k)
|
|
|
|
if changed:
|
|
write(repo)
|
|
|
|
def diff(ui, repo, remote):
|
|
ui.status(_("searching for changed bookmarks\n"))
|
|
|
|
lmarks = repo.listkeys('bookmarks')
|
|
rmarks = remote.listkeys('bookmarks')
|
|
|
|
diff = sorted(set(rmarks) - set(lmarks))
|
|
for k in diff:
|
|
mark = ui.debugflag and rmarks[k] or rmarks[k][:12]
|
|
ui.write(" %-25s %s\n" % (k, mark))
|
|
|
|
if len(diff) <= 0:
|
|
ui.status(_("no changed bookmarks found\n"))
|
|
return 1
|
|
return 0
|