##// END OF EJS Templates
transactions: fix hg recover with fncache backups...
transactions: fix hg recover with fncache backups The transaction backupfiles logic was broken for 'hg recover'. The file format is XXX\0XXX\0YYY\0YYY\0 but the parser did a couple things wrong. 1) It went one step beyond the final \0 and tried to read past the end of the array. 2) array[i:i+1] returns a single item, instead of two items as intended. Added a test to catch it, which turns out to be the first actual 'hg recover' test.

File last commit:

r22941:da2758c0 default
r23063:cd86a670 stable
Show More
bookmarks.py
441 lines | 14.2 KiB | text/x-python | PythonLexer
Matt Mackall
bookmarks: move basic io to core
r13350 # 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 _
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 from mercurial.node import hex, bin
Pierre-Yves David
push: update bookmarks (on server) within a transaction...
r22667 from mercurial import encoding, error, util, obsolete, lock as lockmod
FUJIWARA Katsunori
bookmarks: use "vfs.utime()" instead of "os.utime()"
r19896 import errno
Matt Mackall
bookmarks: move basic io to core
r13350
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 class bmstore(dict):
"""Storage for bookmarks.
This object should do all bookmark reads and writes, so that it's
fairly simple to replace the storage underlying bookmarks without
having to clone the logic surrounding bookmarks.
This particular bmstore implementation stores bookmarks as
{hash}\s{name}\n (the same format as localtags) in
.hg/bookmarks. The mapping is stored as {name: nodeid}.
This class does NOT handle the "current" bookmark state at this
time.
"""
Matt Mackall
bookmarks: move read methods to core
r13351
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 def __init__(self, repo):
dict.__init__(self)
self._repo = repo
try:
for line in repo.vfs('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:
self[refspec] = repo.changelog.lookup(sha)
except LookupError:
pass
except IOError, inst:
if inst.errno != errno.ENOENT:
raise
Pierre-Yves David
bookmark: add a `bmstore.recordupdate` to plug bookmarks into the transaction...
r22665 def recordchange(self, tr):
"""record that bookmarks have been changed in a transaction
The transaction is then responsible for updating the file content."""
tr.addfilegenerator('bookmarks', ('bookmarks',), self._write,
vfs=self._repo.vfs)
Pierre-Yves David
bookmarks: inform transaction-related hooks that some bookmarks were moved...
r22941 tr.hookargs['bookmark_moved'] = '1'
Pierre-Yves David
bookmark: add a `bmstore.recordupdate` to plug bookmarks into the transaction...
r22665
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 def write(self):
'''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.
'''
repo = self._repo
if repo._bookmarkcurrent not in self:
Siddharth Agarwal
bmstore.write: use unsetcurrent instead of setcurrent with None
r20098 unsetcurrent(repo)
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922
wlock = repo.wlock()
try:
file = repo.vfs('bookmarks', 'w', atomictemp=True)
Pierre-Yves David
bookmarks: split bookmark serialization and file handling...
r22664 self._write(file)
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 file.close()
# touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 try:
FUJIWARA Katsunori
bookmarks: use "vfs.utime()" instead of "os.utime()"
r19896 repo.svfs.utime('00changelog.i', None)
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 except OSError:
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 pass
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922
finally:
wlock.release()
Matt Mackall
bookmarks: move read methods to core
r13351
Pierre-Yves David
bookmarks: split bookmark serialization and file handling...
r22664 def _write(self, fp):
for name, node in self.iteritems():
fp.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
Matt Mackall
bookmarks: move read methods to core
r13351 def readcurrent(repo):
'''Get the current bookmark
Kevin Bullock
bookmarks: spelling correction in docstring
r18043 If we use gittish branches we have a current bookmark that
Matt Mackall
bookmarks: move read methods to core
r13351 we are on. This function returns the name of the bookmark. It
is stored in .hg/bookmarks.current
'''
mark = None
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 try:
Matt Mackall
bookmarks: move read methods to core
r13351 file = repo.opener('bookmarks.current')
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 except IOError, inst:
if inst.errno != errno.ENOENT:
raise
return None
try:
Mads Kiilerich
fix wording and not-completely-trivial spelling errors and bad docstrings
r17425 # No readline() in osutil.posixfile, reading everything is cheap
David Soria Parra
bookmarks: read current bookmark as utf-8 and convert it to local
r13381 mark = encoding.tolocal((file.readlines() or [''])[0])
Benoit Boissinot
bookmarks: discard current bookmark if absent from the bookmarks (issue2692)...
r13627 if mark == '' or mark not in repo._bookmarks:
Matt Mackall
bookmarks: move read methods to core
r13351 mark = None
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 finally:
Matt Mackall
bookmarks: move read methods to core
r13351 file.close()
return mark
Matt Mackall
bookmarks: move basic io to core
r13350 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
'''
Siddharth Agarwal
bookmarks: make setcurrent with None an error
r20100 if mark not in repo._bookmarks:
raise AssertionError('bookmark %s does not exist!' % mark)
Matt Mackall
bookmarks: move basic io to core
r13350 current = repo._bookmarkcurrent
if current == mark:
return
Matt Mackall
bookmarks: backout locking change in 12dea4d998ec...
r15908 wlock = repo.wlock()
Matt Mackall
bookmarks: move basic io to core
r13350 try:
file = repo.opener('bookmarks.current', 'w', atomictemp=True)
LUO Zheng
bookmarks: recognize the current bookmark when the local encoding isn't UTF-8...
r14559 file.write(encoding.fromlocal(mark))
Greg Ward
atomictempfile: make close() consistent with other file-like objects....
r15057 file.close()
Matt Mackall
bookmarks: move basic io to core
r13350 finally:
Matt Mackall
bookmarks: backout locking change in 12dea4d998ec...
r15908 wlock.release()
Matt Mackall
bookmarks: move basic io to core
r13350 repo._bookmarkcurrent = mark
Matt Mackall
bookmarks: move update into core
r13352
Idan Kamara
update: delete bookmarks.current when explicitly updating to a rev (issue3276)
r16191 def unsetcurrent(repo):
wlock = repo.wlock()
try:
Gilles Moris
bookmarks: restore python 2.4 compatibility...
r16194 try:
FUJIWARA Katsunori
bookmarks: use "vfs.unlink()" instead of "util.unlink()"
r19895 repo.vfs.unlink('bookmarks.current')
Gilles Moris
bookmarks: restore python 2.4 compatibility...
r16194 repo._bookmarkcurrent = None
except OSError, inst:
if inst.errno != errno.ENOENT:
raise
Idan Kamara
update: delete bookmarks.current when explicitly updating to a rev (issue3276)
r16191 finally:
wlock.release()
Kevin Bullock
update: update to current bookmark if it moved out from under us (issue3682)...
r18471 def iscurrent(repo, mark=None, parents=None):
'''Tell whether the current bookmark is also active
I.e., the bookmark listed in .hg/bookmarks.current also points to a
parent of the working directory.
'''
if not mark:
mark = repo._bookmarkcurrent
if not parents:
parents = [p.node() for p in repo[None].parents()]
marks = repo._bookmarks
return (mark in marks and marks[mark] in parents)
David Soria Parra
bundle: update current bookmark to most recent revision on current branch...
r13663 def updatecurrentbookmark(repo, oldnode, curbranch):
try:
Brodie Rao
localrepo: add branchtip() method for faster single-branch lookups...
r16719 return update(repo, oldnode, repo.branchtip(curbranch))
except error.RepoLookupError:
David Soria Parra
bundle: update current bookmark to most recent revision on current branch...
r13663 if curbranch == "default": # no default branch!
Kevin Bullock
bookmarks: update and updatecurrentbookmark return status...
r15621 return update(repo, oldnode, repo.lookup("tip"))
David Soria Parra
bundle: update current bookmark to most recent revision on current branch...
r13663 else:
raise util.Abort(_("branch %s not found") % curbranch)
Siddharth Agarwal
bookmarks: factor out delete divergent code...
r18513 def deletedivergent(repo, deletefrom, bm):
'''Delete divergent versions of bm on nodes in deletefrom.
Return True if at least one bookmark was deleted, False otherwise.'''
deleted = False
marks = repo._bookmarks
divergent = [b for b in marks if b.split('@', 1)[0] == bm.split('@', 1)[0]]
for mark in divergent:
Matt Mackall
bookmarks: avoid deleting primary bookmarks on rebase...
r21843 if mark == '@' or '@' not in mark:
# can't be divergent by definition
continue
Siddharth Agarwal
bookmarks: factor out delete divergent code...
r18513 if mark and marks[mark] in deletefrom:
if mark != bm:
del marks[mark]
deleted = True
return deleted
Kevin Bullock
bookmarks: pull --update updates to active bookmark if it moved (issue4007)...
r19523 def calculateupdate(ui, repo, checkout):
'''Return a tuple (targetrev, movemarkfrom) indicating the rev to
check out and where to move the active bookmark from, if needed.'''
movemarkfrom = None
if checkout is None:
curmark = repo._bookmarkcurrent
if iscurrent(repo):
movemarkfrom = repo['.'].node()
elif curmark:
ui.status(_("updating to active bookmark %s\n") % curmark)
checkout = curmark
return (checkout, movemarkfrom)
Matt Mackall
bookmarks: move update into core
r13352 def update(repo, parents, node):
Sean Farley
bookmarks: resolve divergent bookmarks when moving active bookmark forward...
r19110 deletefrom = parents
Matt Mackall
bookmarks: move update into core
r13352 marks = repo._bookmarks
update = False
David Soria Parra
bookmarks: delete divergent bookmarks on merge
r16706 cur = repo._bookmarkcurrent
if not cur:
return False
Siddharth Agarwal
bookmarks: factor out delete divergent code...
r18513 if marks[cur] in parents:
new = repo[node]
Sean Farley
bookmarks: resolve divergent bookmarks when moving active bookmark forward...
r19110 divs = [repo[b] for b in marks
if b.split('@', 1)[0] == cur.split('@', 1)[0]]
anc = repo.changelog.ancestors([new.rev()])
deletefrom = [b.node() for b in divs if b.rev() in anc or b == new]
Sean Farley
update: consider successor changesets when moving active bookmark...
r20281 if validdest(repo, repo[marks[cur]], new):
Siddharth Agarwal
bookmarks: factor out delete divergent code...
r18513 marks[cur] = new.node()
update = True
Sean Farley
bookmarks: resolve divergent bookmarks when moving active bookmark forward...
r19110 if deletedivergent(repo, deletefrom, cur):
Siddharth Agarwal
bookmarks: factor out delete divergent code...
r18513 update = True
Matt Mackall
bookmarks: move update into core
r13352 if update:
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 marks.write()
Kevin Bullock
bookmarks: update and updatecurrentbookmark return status...
r15621 return update
Matt Mackall
bookmarks: move pushkey functions into core
r13353
def listbookmarks(repo):
# We may try to list bookmarks on a repo type that does not
# support it (e.g., statichttprepository).
Augie Fackler
bookmarks: use getattr instead of hasattr
r14946 marks = getattr(repo, '_bookmarks', {})
Matt Mackall
bookmarks: move pushkey functions into core
r13353
d = {}
Kevin Bullock
bookmarks: hide bookmarks on filtered revs from listkeys...
r18496 hasnode = repo.changelog.hasnode
Augie Fackler
bookmarks: use getattr instead of hasattr
r14946 for k, v in marks.iteritems():
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613 # don't expose local divergent bookmarks
Kevin Bullock
bookmarks: hide bookmarks on filtered revs from listkeys...
r18496 if hasnode(v) and ('@' not in k or k.endswith('@')):
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613 d[k] = hex(v)
Matt Mackall
bookmarks: move pushkey functions into core
r13353 return d
def pushbookmark(repo, key, old, new):
Pierre-Yves David
push: update bookmarks (on server) within a transaction...
r22667 w = l = tr = None
Matt Mackall
bookmarks: move pushkey functions into core
r13353 try:
Pierre-Yves David
push: update bookmarks (on server) within a transaction...
r22667 w = repo.wlock()
l = repo.lock()
tr = repo.transaction('bookmarks')
Matt Mackall
bookmarks: move pushkey functions into core
r13353 marks = repo._bookmarks
Durham Goode
bookmarks: allow pushkey if new equals current...
r22364 existing = hex(marks.get(key, ''))
if existing != old and existing != new:
Matt Mackall
bookmarks: move pushkey functions into core
r13353 return False
if new == '':
del marks[key]
else:
if new not in repo:
return False
marks[key] = repo[new].node()
Pierre-Yves David
push: update bookmarks (on server) within a transaction...
r22667 marks.recordchange(tr)
tr.close()
Matt Mackall
bookmarks: move pushkey functions into core
r13353 return True
finally:
Pierre-Yves David
push: update bookmarks (on server) within a transaction...
r22667 lockmod.release(tr, l, w)
Matt Mackall
bookmarks: move diff to core
r13354
FUJIWARA Katsunori
bookmarks: add function to centralize the logic to compare bookmarks...
r20024 def compare(repo, srcmarks, dstmarks,
srchex=None, dsthex=None, targets=None):
'''Compare bookmarks between srcmarks and dstmarks
This returns tuple "(addsrc, adddst, advsrc, advdst, diverge,
differ, invalid)", each are list of bookmarks below:
:addsrc: added on src side (removed on dst side, perhaps)
:adddst: added on dst side (removed on src side, perhaps)
:advsrc: advanced on src side
:advdst: advanced on dst side
:diverge: diverge
:differ: changed, but changeset referred on src is unknown on dst
:invalid: unknown on both side
Each elements of lists in result tuple is tuple "(bookmark name,
changeset ID on source side, changeset ID on destination
side)". Each changeset IDs are 40 hexadecimal digit string or
None.
Changeset IDs of tuples in "addsrc", "adddst", "differ" or
"invalid" list may be unknown for repo.
This function expects that "srcmarks" and "dstmarks" return
changeset ID in 40 hexadecimal digit string for specified
bookmark. If not so (e.g. bmstore "repo._bookmarks" returning
binary value), "srchex" or "dsthex" should be specified to convert
into such form.
If "targets" is specified, only bookmarks listed in it are
examined.
'''
if not srchex:
srchex = lambda x: x
if not dsthex:
dsthex = lambda x: x
if targets:
bset = set(targets)
else:
srcmarkset = set(srcmarks)
dstmarkset = set(dstmarks)
bset = srcmarkset ^ dstmarkset
for b in srcmarkset & dstmarkset:
if srchex(srcmarks[b]) != dsthex(dstmarks[b]):
bset.add(b)
results = ([], [], [], [], [], [], [])
addsrc = results[0].append
adddst = results[1].append
advsrc = results[2].append
advdst = results[3].append
diverge = results[4].append
differ = results[5].append
invalid = results[6].append
for b in sorted(bset):
if b not in srcmarks:
if b in dstmarks:
adddst((b, None, dsthex(dstmarks[b])))
else:
invalid((b, None, None))
elif b not in dstmarks:
addsrc((b, srchex(srcmarks[b]), None))
else:
scid = srchex(srcmarks[b])
dcid = dsthex(dstmarks[b])
if scid in repo and dcid in repo:
sctx = repo[scid]
dctx = repo[dcid]
if sctx.rev() < dctx.rev():
if validdest(repo, sctx, dctx):
advdst((b, scid, dcid))
else:
diverge((b, scid, dcid))
else:
if validdest(repo, dctx, sctx):
advsrc((b, scid, dcid))
else:
diverge((b, scid, dcid))
else:
# it is too expensive to examine in detail, in this case
differ((b, scid, dcid))
return results
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 def _diverge(ui, b, path, localmarks):
if b == '@':
b = ''
# find a unique @ suffix
for x in range(1, 100):
n = '%s@%d' % (b, x)
if n not in localmarks:
break
# try to use an @pathalias suffix
# if an @pathalias already exists, we overwrite (update) it
Matt Mackall
bookmarks: fix divergent bookmark path normalization
r22629 if path.startswith("file:"):
path = util.url(path).path
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 for p, u in ui.configitems("paths"):
Matt Mackall
bookmarks: fix divergent bookmark path normalization
r22629 if u.startswith("file:"):
u = util.url(u).path
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 if path == u:
n = '%s@%s' % (b, p)
return n
Pierre-Yves David
pull: perform bookmark updates in the transaction
r22666 def updatefromremote(ui, repo, remotemarks, path, trfunc, explicit=()):
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 ui.debug("checking for updated bookmarks\n")
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks = repo._bookmarks
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 (addsrc, adddst, advsrc, advdst, diverge, differ, invalid
) = compare(repo, remotemarks, localmarks, dsthex=hex)
Matt Mackall
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
r15614
Pierre-Yves David
bookmarks: allow `updatefromremote` to be quiet...
r22644 status = ui.status
warn = ui.warn
if ui.configbool('ui', 'quietbookmarkmove', False):
status = warn = ui.debug
Pierre-Yves David
pull: merge bookmark updates and imports...
r22659 explicit = set(explicit)
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 changed = []
for b, scid, dcid in addsrc:
if scid in repo: # add remote bookmarks for changes we already have
Pierre-Yves David
bookmarks: allow `updatefromremote` to be quiet...
r22644 changed.append((b, bin(scid), status,
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 _("adding remote bookmark %s\n") % (b)))
for b, scid, dcid in advsrc:
Pierre-Yves David
bookmarks: allow `updatefromremote` to be quiet...
r22644 changed.append((b, bin(scid), status,
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 _("updating bookmark %s\n") % (b)))
Pierre-Yves David
pull: merge bookmark updates and imports...
r22659 # remove normal movement from explicit set
explicit.difference_update(d[0] for d in changed)
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 for b, scid, dcid in diverge:
Pierre-Yves David
pull: merge bookmark updates and imports...
r22659 if b in explicit:
explicit.discard(b)
changed.append((b, bin(scid), status,
_("importing bookmark %s\n") % (b, b)))
else:
db = _diverge(ui, b, path, localmarks)
changed.append((db, bin(scid), warn,
_("divergent bookmark %s stored as %s\n")
% (b, db)))
for b, scid, dcid in adddst + advdst:
if b in explicit:
explicit.discard(b)
changed.append((b, bin(scid), status,
_("importing bookmark %s\n") % (b, b)))
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 if changed:
Pierre-Yves David
pull: perform bookmark updates in the transaction
r22666 tr = trfunc()
FUJIWARA Katsunori
bookmarks: rewrite "updatefromremote()" by "compare()"...
r20025 for b, node, writer, msg in sorted(changed):
localmarks[b] = node
writer(msg)
Pierre-Yves David
pull: perform bookmark updates in the transaction
r22666 localmarks.recordchange(tr)
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646
FUJIWARA Katsunori
bookmarks: rename arguments/variables for source code readability...
r17667 def diff(ui, dst, src):
Matt Mackall
bookmarks: move diff to core
r13354 ui.status(_("searching for changed bookmarks\n"))
FUJIWARA Katsunori
bookmarks: rename arguments/variables for source code readability...
r17667 smarks = src.listkeys('bookmarks')
dmarks = dst.listkeys('bookmarks')
Matt Mackall
bookmarks: move diff to core
r13354
FUJIWARA Katsunori
bookmarks: rename arguments/variables for source code readability...
r17667 diff = sorted(set(smarks) - set(dmarks))
Matt Mackall
bookmarks: move diff to core
r13354 for k in diff:
FUJIWARA Katsunori
bookmarks: rename arguments/variables for source code readability...
r17667 mark = ui.debugflag and smarks[k] or smarks[k][:12]
David Soria Parra
bookmarks: respect --debug during incoming/outgoing
r15984 ui.write(" %-25s %s\n" % (k, mark))
Matt Mackall
bookmarks: move diff to core
r13354
if len(diff) <= 0:
ui.status(_("no changed bookmarks found\n"))
return 1
return 0
Pierre-Yves David
bookmarks: extract valid destination logic in a dedicated function...
r17550
def validdest(repo, old, new):
"""Is the new bookmark destination a valid update from the old one"""
Pierre-Yves David
clfilter: `bookmark.validdest` should run on unfiltered repo...
r18008 repo = repo.unfiltered()
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 if old == new:
# Old == new -> nothing to update.
FUJIWARA Katsunori
bookmarks: avoid redundant creation/assignment of "validdests" in "validdest()"
r17625 return False
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 elif not old:
# old is nullrev, anything is valid.
# (new != nullrev has been excluded by the previous check)
FUJIWARA Katsunori
bookmarks: avoid redundant creation/assignment of "validdests" in "validdest()"
r17625 return True
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 elif repo.obsstore:
Pierre-Yves David
obsolete: extract foreground computation from bookmark.validdest...
r18984 return new.node() in obsolete.foreground(repo, [old.node()])
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 else:
Mads Kiilerich
spelling: random spell checker fixes
r19951 # still an independent clause as it is lazyer (and therefore faster)
FUJIWARA Katsunori
bookmarks: use "changectx.descendant()" for efficient descendant examination...
r17627 return old.descendant(new)