2017-07-13 21:04:56 +03:00
|
|
|
# hiddenoverride.py - lightweight hidden-ness override
|
|
|
|
#
|
|
|
|
# Copyright 2017 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.
|
|
|
|
|
|
|
|
from __future__ import absolute_import
|
|
|
|
|
2017-07-19 02:07:33 +03:00
|
|
|
import contextlib
|
|
|
|
import os
|
|
|
|
|
2017-07-21 20:50:30 +03:00
|
|
|
from mercurial.node import short
|
2017-07-13 21:04:56 +03:00
|
|
|
from mercurial import (
|
|
|
|
dispatch,
|
|
|
|
error,
|
|
|
|
extensions,
|
2017-07-19 02:07:33 +03:00
|
|
|
lock as lockmod,
|
2017-07-13 21:04:56 +03:00
|
|
|
obsolete,
|
|
|
|
repoview,
|
2017-07-19 02:07:33 +03:00
|
|
|
util,
|
|
|
|
vfs as vfsmod,
|
2017-07-13 21:04:56 +03:00
|
|
|
)
|
|
|
|
|
|
|
|
def uisetup(ui):
|
|
|
|
extensions.wrapfunction(repoview, 'pinnedrevs', pinnedrevs)
|
|
|
|
extensions.wrapfunction(dispatch, 'runcommand', runcommand)
|
|
|
|
extensions.wrapfunction(obsolete, 'createmarkers', createmarkers)
|
|
|
|
|
|
|
|
def pinnedrevs(orig, repo):
|
|
|
|
revs = orig(repo)
|
|
|
|
nodemap = repo.changelog.nodemap
|
2017-07-20 03:49:21 +03:00
|
|
|
pinnednodes = set(loadpinnednodes(repo))
|
|
|
|
tounpin = getattr(repo, '_tounpinnodes', set())
|
|
|
|
pinnednodes -= tounpin
|
|
|
|
revs.update(nodemap[n] for n in pinnednodes)
|
2017-07-13 21:04:56 +03:00
|
|
|
return revs
|
|
|
|
|
|
|
|
def loadpinnednodes(repo):
|
2017-07-21 08:22:38 +03:00
|
|
|
"""yield pinned nodes that are obsoleted and should be visible"""
|
2017-07-13 21:04:56 +03:00
|
|
|
if repo is None or not repo.local():
|
|
|
|
return
|
|
|
|
# the "pinned nodes" file name is "obsinhibit" for compatibility reason
|
|
|
|
content = repo.svfs.tryread('obsinhibit') or ''
|
2017-07-21 08:22:38 +03:00
|
|
|
unfi = repo.unfiltered()
|
|
|
|
nodemap = unfi.changelog.nodemap
|
2017-07-13 21:04:56 +03:00
|
|
|
offset = 0
|
2017-07-21 20:50:30 +03:00
|
|
|
result = []
|
2017-07-13 21:04:56 +03:00
|
|
|
while True:
|
|
|
|
node = content[offset:offset + 20]
|
|
|
|
if not node:
|
|
|
|
break
|
2017-07-21 08:22:38 +03:00
|
|
|
# remove unnecessary (non-obsoleted) nodes since pinnedrevs should only
|
|
|
|
# affect obsoleted revs.
|
|
|
|
if node in nodemap and unfi[node].obsolete():
|
2017-07-21 20:50:30 +03:00
|
|
|
result.append(node)
|
2017-07-13 21:04:56 +03:00
|
|
|
offset += 20
|
2017-07-21 20:50:30 +03:00
|
|
|
return result
|
2017-07-13 21:04:56 +03:00
|
|
|
|
2017-07-19 02:07:33 +03:00
|
|
|
def shouldpinnodes(repo):
|
|
|
|
"""get nodes that should be pinned: working parent + bookmarks for now"""
|
|
|
|
result = set()
|
2017-07-13 21:04:56 +03:00
|
|
|
if repo and repo.local():
|
2017-07-19 02:07:33 +03:00
|
|
|
# working copy parent
|
2017-07-13 21:04:56 +03:00
|
|
|
try:
|
|
|
|
wnode = repo.vfs('dirstate').read(20)
|
2017-07-19 02:07:33 +03:00
|
|
|
result.add(wnode)
|
2017-07-13 21:04:56 +03:00
|
|
|
except Exception:
|
|
|
|
pass
|
2017-07-19 02:07:33 +03:00
|
|
|
# bookmarks
|
|
|
|
result.update(repo._bookmarks.values())
|
|
|
|
return result
|
|
|
|
|
|
|
|
@contextlib.contextmanager
|
|
|
|
def flock(lockpath):
|
|
|
|
# best effort lightweight lock
|
|
|
|
try:
|
|
|
|
import fcntl
|
|
|
|
fcntl.flock
|
|
|
|
except ImportError:
|
|
|
|
# fallback to Mercurial lock
|
|
|
|
vfs = vfsmod.vfs(os.path.dirname(lockpath))
|
|
|
|
with lockmod.lock(vfs, os.path.basename(lockpath)):
|
|
|
|
yield
|
|
|
|
return
|
|
|
|
# make sure lock file exists
|
|
|
|
util.makedirs(os.path.dirname(lockpath))
|
|
|
|
with open(lockpath, 'a'):
|
|
|
|
pass
|
|
|
|
lockfd = os.open(lockpath, os.O_RDONLY | os.O_CREAT, 0o664)
|
|
|
|
fcntl.flock(lockfd, fcntl.LOCK_EX)
|
|
|
|
try:
|
|
|
|
yield
|
|
|
|
finally:
|
|
|
|
fcntl.flock(lockfd, fcntl.LOCK_UN)
|
|
|
|
os.close(lockfd)
|
|
|
|
|
2017-07-21 20:50:30 +03:00
|
|
|
def savepinnednodes(repo, newpin, newunpin, fullargs):
|
2017-07-19 02:07:33 +03:00
|
|
|
# take a narrowed lock so it does not affect repo lock
|
|
|
|
with flock(repo.svfs.join('obsinhibit.lock')):
|
2017-07-21 20:50:30 +03:00
|
|
|
orignodes = loadpinnednodes(repo)
|
|
|
|
nodes = set(orignodes)
|
2017-07-19 02:07:33 +03:00
|
|
|
nodes |= set(newpin)
|
|
|
|
nodes -= set(newunpin)
|
|
|
|
with util.atomictempfile(repo.svfs.join('obsinhibit')) as f:
|
|
|
|
f.write(''.join(nodes))
|
|
|
|
|
2017-07-21 20:50:30 +03:00
|
|
|
desc = lambda s: [short(n) for n in s]
|
|
|
|
repo.ui.log('pinnednodes', 'pinnednodes: %r newpin=%r newunpin=%r '
|
|
|
|
'before=%r after=%r\n', fullargs, desc(newpin),
|
|
|
|
desc(newunpin), desc(orignodes), desc(nodes))
|
|
|
|
|
2017-07-19 02:07:33 +03:00
|
|
|
def runcommand(orig, lui, repo, cmd, fullargs, *args):
|
2017-07-21 08:22:38 +03:00
|
|
|
# return directly for non-repo command
|
|
|
|
if not repo:
|
|
|
|
return orig(lui, repo, cmd, fullargs, *args)
|
|
|
|
|
|
|
|
shouldpinbefore = shouldpinnodes(repo) | set(loadpinnednodes(repo))
|
2017-07-19 02:07:33 +03:00
|
|
|
result = orig(lui, repo, cmd, fullargs, *args)
|
|
|
|
# after a command completes, make sure working copy parent and all
|
|
|
|
# bookmarks get "pinned".
|
|
|
|
newpin = shouldpinnodes(repo) - shouldpinbefore
|
2017-07-21 08:22:38 +03:00
|
|
|
newunpin = getattr(repo.unfiltered(), '_tounpinnodes', set())
|
|
|
|
# filter newpin by obsolte - ex. if newpin is on a non-obsoleted commit,
|
|
|
|
# ignore it.
|
|
|
|
unfi = repo.unfiltered()
|
|
|
|
newpin = set(n for n in newpin if unfi[n].obsolete())
|
2017-07-19 02:07:33 +03:00
|
|
|
# only do a write if something has changed
|
|
|
|
if newpin or newunpin:
|
2017-07-21 20:50:30 +03:00
|
|
|
savepinnednodes(repo, newpin, newunpin, fullargs)
|
2017-07-13 21:04:56 +03:00
|
|
|
return result
|
|
|
|
|
|
|
|
def createmarkers(orig, repo, rels, *args, **kwargs):
|
|
|
|
# this is a way to unpin revs - precursors are unpinned
|
2017-07-21 08:22:38 +03:00
|
|
|
# note: hg debugobsolete does not call this function
|
2017-07-13 21:04:56 +03:00
|
|
|
unfi = repo.unfiltered()
|
|
|
|
tounpin = getattr(unfi, '_tounpinnodes', set())
|
|
|
|
for r in rels:
|
|
|
|
try:
|
|
|
|
tounpin.add(r[0].node())
|
|
|
|
except error.RepoLookupError:
|
|
|
|
pass
|
|
|
|
unfi._tounpinnodes = tounpin
|
|
|
|
return orig(repo, rels, *args, **kwargs)
|