##// END OF EJS Templates
hgweb, spartan: link from manifest title to changeset page
hgweb, spartan: link from manifest title to changeset page

File last commit:

r18043:20459152 default
r18260:580274a1 default
Show More
bookmarks.py
283 lines | 8.9 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 _
Alexander Solovyov
remove unused imports and variables
r14064 from mercurial.node import hex
Pierre-Yves David
bookmark: simplify mutability check in `validdest`...
r17916 from mercurial import encoding, error, util, obsolete
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 import errno, os
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
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:
setcurrent(repo, None)
wlock = repo.wlock()
try:
file = repo.vfs('bookmarks', 'w', atomictemp=True)
for name, node in self.iteritems():
file.write("%s %s\n" % (hex(node), encoding.fromlocal(name)))
file.close()
# touch 00changelog.i so hgweb reloads bookmarks (no lock needed)
Benoit Boissinot
bookmarks: be more restrictive in our Exception catching
r14027 try:
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 os.utime(repo.sjoin('00changelog.i'), None)
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
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
'''
current = repo._bookmarkcurrent
if current == mark:
return
David Soria Parra
bookmarks: remove API limitation in setcurrent...
r13647 if mark not in repo._bookmarks:
Matt Mackall
bookmarks: move basic io to core
r13350 mark = ''
David Soria Parra
bookmarks: forbid \0 \r \n : in bookmark names (BC)...
r13425
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:
util.unlink(repo.join('bookmarks.current'))
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()
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)
Matt Mackall
bookmarks: move update into core
r13352 def update(repo, parents, node):
marks = repo._bookmarks
update = False
David Soria Parra
bookmarks: delete divergent bookmarks on merge
r16706 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]
FUJIWARA Katsunori
bookmarks: use "changectx.descendant()" for efficient descendant examination...
r17627 if old.descendant(new) and mark == cur:
David Soria Parra
bookmarks: delete divergent bookmarks on merge
r16706 marks[cur] = new.node()
update = True
if mark != cur:
del marks[mark]
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 = {}
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: clone non-divergent bookmarks with @ in them
r16276 if '@' 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):
Matt Mackall
bookmarks: backout locking change in 12dea4d998ec...
r15908 w = repo.wlock()
Matt Mackall
bookmarks: move pushkey functions into core
r13353 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()
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 marks.write()
Matt Mackall
bookmarks: move pushkey functions into core
r13353 return True
finally:
Matt Mackall
bookmarks: backout locking change in 12dea4d998ec...
r15908 w.release()
Matt Mackall
bookmarks: move diff to core
r13354
Matt Mackall
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
r15614 def updatefromremote(ui, repo, remote, path):
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 ui.debug("checking for updated bookmarks\n")
rb = remote.listkeys('bookmarks')
changed = False
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks = repo._bookmarks
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 for k in rb.keys():
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 if k in localmarks:
nr, nl = rb[k], localmarks[k]
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 if nr in repo:
cr = repo[nr]
cl = repo[nl]
if cl.rev() >= cr.rev():
continue
Pierre-Yves David
bookmarks: extract valid destination logic in a dedicated function...
r17550 if validdest(repo, cl, cr):
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks[k] = cr.node()
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 changed = True
ui.status(_("updating bookmark %s\n") % k)
else:
David M. Carr
bookmarks: when @ bookmark diverges, don't double the @ sign (BC)...
r17770 if k == '@':
kd = ''
else:
kd = k
Matt Mackall
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
r15614 # find a unique @ suffix
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613 for x in range(1, 100):
David M. Carr
bookmarks: when @ bookmark diverges, don't double the @ sign (BC)...
r17770 n = '%s@%d' % (kd, x)
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 if n not in localmarks:
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613 break
Matt Mackall
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
r15614 # 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:
David M. Carr
bookmarks: when @ bookmark diverges, don't double the @ sign (BC)...
r17770 n = '%s@%s' % (kd, p)
Matt Mackall
bookmarks: mark divergent bookmarks with book@pathalias when source in [paths]
r15614
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks[n] = cr.node()
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613 changed = True
ui.warn(_("divergent bookmark %s stored as %s\n") % (k, n))
Levi Bard
bookmarks: pull new bookmarks from remote by default (BC)
r16697 elif rb[k] in repo:
# add remote bookmarks for changes we already have
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks[k] = repo[rb[k]].node()
Levi Bard
bookmarks: pull new bookmarks from remote by default (BC)
r16697 changed = True
ui.status(_("adding remote bookmark %s\n") % k)
Matt Mackall
bookmarks: shadow divergent bookmarks of foo with foo@n
r15613
David Soria Parra
bookmarks: separate bookmarks update code from localrepo's pull....
r13646 if changed:
Augie Fackler
bookmarks: introduce a bmstore to manage bookmark persistence...
r17922 localmarks.write()
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:
# We only need this complicated logic if there is obsolescence
Mads Kiilerich
spelling: fix minor spell checker issues
r17738 # XXX will probably deserve an optimised revset.
Pierre-Yves David
bookmark: prevent crashing when a successor is unknown locally (issue3680)...
r17865 nm = repo.changelog.nodemap
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 validdests = set([old])
plen = -1
# compute the whole set of successors or descendants
while len(validdests) != plen:
plen = len(validdests)
succs = set(c.node() for c in validdests)
Pierre-Yves David
bookmark: issue a single call to `allsuccessors` per loop...
r17917 mutable = [c.node() for c in validdests if c.mutable()]
succs.update(obsolete.allsuccessors(repo.obsstore, mutable))
Thomas Arendsen Hein
bookmark: simplify nodemap check introduced in the previous changeset
r17866 known = (n for n in succs if n in nm)
Pierre-Yves David
bookmark: prevent crashing when a successor is unknown locally (issue3680)...
r17865 validdests = set(repo.set('%ln::', known))
FUJIWARA Katsunori
bookmarks: avoid redundant creation/assignment of "validdests" in "validdest()"
r17625 return new in validdests
Pierre-Yves David
bookmark: take successors into account when updating (issue3561)...
r17551 else:
FUJIWARA Katsunori
bookmarks: use "changectx.descendant()" for efficient descendant examination...
r17627 return old.descendant(new)