hgext/censor.py
changeset 51270 ceeb8fa23cc8
parent 51269 db121ddd171e
equal deleted inserted replaced
51269:db121ddd171e 51270:ceeb8fa23cc8
    34 from mercurial.i18n import _
    34 from mercurial.i18n import _
    35 from mercurial.node import short
    35 from mercurial.node import short
    36 
    36 
    37 from mercurial import (
    37 from mercurial import (
    38     error,
    38     error,
    39     logcmdutil,
       
    40     registrar,
    39     registrar,
    41     scmutil,
    40     scmutil,
    42 )
    41 )
    43 
    42 
    44 cmdtable = {}
    43 cmdtable = {}
    54     b'censor',
    53     b'censor',
    55     [
    54     [
    56         (
    55         (
    57             b'r',
    56             b'r',
    58             b'rev',
    57             b'rev',
    59             b'',
    58             [],
    60             _(b'censor file from specified revision'),
    59             _(b'censor file from specified revision'),
    61             _(b'REV'),
    60             _(b'REV'),
    62         ),
    61         ),
    63         (
    62         (
    64             b'',
    63             b'',
    69         (b't', b'tombstone', b'', _(b'replacement tombstone data'), _(b'TEXT')),
    68         (b't', b'tombstone', b'', _(b'replacement tombstone data'), _(b'TEXT')),
    70     ],
    69     ],
    71     _(b'-r REV [-t TEXT] [FILE]'),
    70     _(b'-r REV [-t TEXT] [FILE]'),
    72     helpcategory=command.CATEGORY_MAINTENANCE,
    71     helpcategory=command.CATEGORY_MAINTENANCE,
    73 )
    72 )
    74 def censor(ui, repo, path, rev=b'', tombstone=b'', check_heads=True, **opts):
    73 def censor(ui, repo, path, rev=(), tombstone=b'', check_heads=True, **opts):
    75     with repo.wlock(), repo.lock():
    74     with repo.wlock(), repo.lock():
    76         return _docensor(
    75         return _docensor(
    77             ui,
    76             ui,
    78             repo,
    77             repo,
    79             path,
    78             path,
    82             check_heads=check_heads,
    81             check_heads=check_heads,
    83             **opts,
    82             **opts,
    84         )
    83         )
    85 
    84 
    86 
    85 
    87 def _docensor(ui, repo, path, rev=b'', tombstone=b'', check_heads=True, **opts):
    86 def _docensor(ui, repo, path, revs=(), tombstone=b'', check_heads=True, **opts):
    88     if not path:
    87     if not path:
    89         raise error.Abort(_(b'must specify file path to censor'))
    88         raise error.Abort(_(b'must specify file path to censor'))
    90     if not rev:
    89     if not revs:
    91         raise error.Abort(_(b'must specify revision to censor'))
    90         raise error.Abort(_(b'must specify revisions to censor'))
    92 
    91 
    93     wctx = repo[None]
    92     wctx = repo[None]
    94 
    93 
    95     m = scmutil.match(wctx, (path,))
    94     m = scmutil.match(wctx, (path,))
    96     if m.anypats() or len(m.files()) != 1:
    95     if m.anypats() or len(m.files()) != 1:
    98     path = m.files()[0]
    97     path = m.files()[0]
    99     flog = repo.file(path)
    98     flog = repo.file(path)
   100     if not len(flog):
    99     if not len(flog):
   101         raise error.Abort(_(b'cannot censor file with no history'))
   100         raise error.Abort(_(b'cannot censor file with no history'))
   102 
   101 
   103     rev = logcmdutil.revsingle(repo, rev, rev).rev()
   102     revs = scmutil.revrange(repo, revs)
   104     try:
   103     if not revs:
   105         ctx = repo[rev]
   104         raise error.Abort(_(b'no matching revisions'))
   106     except KeyError:
   105     file_nodes = set()
   107         raise error.Abort(_(b'invalid revision identifier %s') % rev)
   106     for r in revs:
       
   107         try:
       
   108             ctx = repo[r]
       
   109             file_nodes.add(ctx.filectx(path).filenode())
       
   110         except error.LookupError:
       
   111             raise error.Abort(_(b'file does not exist at revision %s') % ctx)
   108 
   112 
   109     try:
       
   110         fctx = ctx.filectx(path)
       
   111     except error.LookupError:
       
   112         raise error.Abort(_(b'file does not exist at revision %s') % rev)
       
   113 
       
   114     fnode = fctx.filenode()
       
   115     if check_heads:
   113     if check_heads:
   116         heads = []
   114         heads = []
   117         repo_heads = repo.heads()
   115         repo_heads = repo.heads()
   118         msg = b'checking for the censored content in %d heads\n'
   116         msg = b'checking for the censored content in %d heads\n'
   119         msg %= len(repo_heads)
   117         msg %= len(repo_heads)
   120         ui.status(msg)
   118         ui.status(msg)
   121         for headnode in repo_heads:
   119         for headnode in repo_heads:
   122             hc = repo[headnode]
   120             hc = repo[headnode]
   123             if path in hc and hc.filenode(path) == fnode:
   121             if path in hc and hc.filenode(path) in file_nodes:
   124                 heads.append(hc)
   122                 heads.append(hc)
   125         if heads:
   123         if heads:
   126             headlist = b', '.join([short(c.node()) for c in heads])
   124             headlist = b', '.join([short(c.node()) for c in heads])
   127             raise error.Abort(
   125             raise error.Abort(
   128                 _(b'cannot censor file in heads (%s)') % headlist,
   126                 _(b'cannot censor file in heads (%s)') % headlist,
   136         raise error.Abort(
   134         raise error.Abort(
   137             _(b'cannot censor working directory'),
   135             _(b'cannot censor working directory'),
   138             hint=_(b'clean/delete/update first'),
   136             hint=_(b'clean/delete/update first'),
   139         )
   137         )
   140 
   138 
   141     msg = b'censoring 1 file revision\n'
   139     msg = b'censoring %d file revisions\n'
       
   140     msg %= len(file_nodes)
   142     ui.status(msg)
   141     ui.status(msg)
   143     with repo.transaction(b'censor') as tr:
   142     with repo.transaction(b'censor') as tr:
   144         flog.censorrevision(tr, fnode, tombstone=tombstone)
   143         flog.censorrevision(tr, file_nodes, tombstone=tombstone)