##// END OF EJS Templates
branching: merge stable into default
branching: merge stable into default

File last commit:

r52163:ceeb8fa2 default
r52493:ee1b648e merge default
Show More
censor.py
143 lines | 4.7 KiB | text/x-python | PythonLexer
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 # Copyright (C) 2015 - Mike Edgar <adgar@google.com>
#
# This extension enables removal of file content at a given revision,
# rewriting the data/metadata of successive revisions to preserve revision log
# integrity.
"""erase file content at a given revision
The censor command instructs Mercurial to erase all content of a file at a given
revision *without updating the changeset hash.* This allows existing history to
remain valid while preventing future clones/pulls from receiving the erased
data.
Typical uses for censor are due to security or legal requirements, including::
Mads Kiilerich
spelling: trivial spell checking
r26781 * Passwords, private keys, cryptographic material
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 * Licensed data/code/libraries for which the license has expired
* Personally Identifiable Information or other private data
Censored nodes can interrupt mercurial's typical operation whenever the excised
data needs to be materialized. Some commands, like ``hg cat``/``hg revert``,
simply fail when asked to produce censored data. Others, like ``hg verify`` and
``hg update``, must be capable of tolerating censored data to continue to
function in a meaningful way. Such commands only tolerate censored file
censor: mention that we check the heads in the help...
r52159 As having a censored version in a checkout is impractical. The current head
revisions of the repository are checked. If the revision to be censored is in
any of them the command will abort.
Jordi Gutiérrez Hermoso
censor: document that some commands simply ignore censored data...
r43623
A few informative commands such as ``hg grep`` will unconditionally
ignore censored data and merely report that it was encountered.
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 """
Gregory Szorc
censor: use absolute_import
r28092
from mercurial.i18n import _
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 from mercurial.node import short
Gregory Szorc
censor: use absolute_import
r28092
from mercurial import (
error,
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 registrar,
Gregory Szorc
censor: use absolute_import
r28092 scmutil,
)
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
cmdtable = {}
Yuya Nishihara
registrar: move cmdutil.command to registrar module (API)...
r32337 command = registrar.command(cmdtable)
Augie Fackler
extensions: change magic "shipped with hg" string...
r29841 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for
Augie Fackler
extensions: document that `testedwith = 'internal'` is special...
r25186 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should
# be specifying the version(s) of Mercurial they are tested with, or
# leave the attribute unspecified.
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 testedwith = b'ships-with-hg-core'
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
Augie Fackler
formatting: blacken the codebase...
r43346
@command(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 b'censor',
Augie Fackler
formatting: blacken the codebase...
r43346 [
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (
b'r',
b'rev',
censor: accept multiple revision in a single call...
r52163 [],
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'censor file from specified revision'),
_(b'REV'),
),
censor: add a command flag to skip the head checks...
r52161 (
b'',
b'check-heads',
True,
_(b'check that repository heads are not affected'),
),
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 (b't', b'tombstone', b'', _(b'replacement tombstone data'), _(b'TEXT')),
Augie Fackler
formatting: blacken the codebase...
r43346 ],
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'-r REV [-t TEXT] [FILE]'),
Augie Fackler
formatting: blacken the codebase...
r43346 helpcategory=command.CATEGORY_MAINTENANCE,
)
censor: accept multiple revision in a single call...
r52163 def censor(ui, repo, path, rev=(), tombstone=b'', check_heads=True, **opts):
Matt Harbison
censor: use context manager for lock management
r38460 with repo.wlock(), repo.lock():
censor: add a command flag to skip the head checks...
r52161 return _docensor(
ui,
repo,
path,
rev,
tombstone,
check_heads=check_heads,
**opts,
)
FUJIWARA Katsunori
censor: make censor acquire locks before processing...
r27290
Augie Fackler
formatting: blacken the codebase...
r43346
censor: accept multiple revision in a single call...
r52163 def _docensor(ui, repo, path, revs=(), tombstone=b'', check_heads=True, **opts):
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 if not path:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'must specify file path to censor'))
censor: accept multiple revision in a single call...
r52163 if not revs:
raise error.Abort(_(b'must specify revisions to censor'))
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
FUJIWARA Katsunori
censor: make various path forms available like other Mercurial commands...
r25806 wctx = repo[None]
m = scmutil.match(wctx, (path,))
if m.anypats() or len(m.files()) != 1:
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'can only specify an explicit filename'))
FUJIWARA Katsunori
censor: make various path forms available like other Mercurial commands...
r25806 path = m.files()[0]
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 flog = repo.file(path)
if not len(flog):
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 raise error.Abort(_(b'cannot censor file with no history'))
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
censor: accept multiple revision in a single call...
r52163 revs = scmutil.revrange(repo, revs)
if not revs:
raise error.Abort(_(b'no matching revisions'))
file_nodes = set()
for r in revs:
try:
ctx = repo[r]
file_nodes.add(ctx.filectx(path).filenode())
except error.LookupError:
raise error.Abort(_(b'file does not exist at revision %s') % ctx)
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
censor: add a command flag to skip the head checks...
r52161 if check_heads:
heads = []
repo_heads = repo.heads()
msg = b'checking for the censored content in %d heads\n'
msg %= len(repo_heads)
ui.status(msg)
for headnode in repo_heads:
hc = repo[headnode]
censor: accept multiple revision in a single call...
r52163 if path in hc and hc.filenode(path) in file_nodes:
censor: add a command flag to skip the head checks...
r52161 heads.append(hc)
if heads:
headlist = b', '.join([short(c.node()) for c in heads])
raise error.Abort(
_(b'cannot censor file in heads (%s)') % headlist,
hint=_(b'clean/delete and commit first'),
)
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
censor: be more verbose about the other steps too...
r52162 msg = b'checking for the censored content in the working directory\n'
ui.status(msg)
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347 wp = wctx.parents()
if ctx.node() in [p.node() for p in wp]:
Augie Fackler
formatting: blacken the codebase...
r43346 raise error.Abort(
Augie Fackler
formatting: byteify all mercurial/ and hgext/ string literals...
r43347 _(b'cannot censor working directory'),
hint=_(b'clean/delete/update first'),
Augie Fackler
formatting: blacken the codebase...
r43346 )
Mike Edgar
censor: add censor command to hgext with basic client-side tests...
r24347
censor: accept multiple revision in a single call...
r52163 msg = b'censoring %d file revisions\n'
msg %= len(file_nodes)
censor: be more verbose about the other steps too...
r52162 ui.status(msg)
Gregory Szorc
revlog: move censor logic out of censor extension...
r39814 with repo.transaction(b'censor') as tr:
censor: accept multiple revision in a single call...
r52163 flog.censorrevision(tr, file_nodes, tombstone=tombstone)