40 command = registrar.command(cmdtable) |
40 command = registrar.command(cmdtable) |
41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
41 # Note for extension authors: ONLY specify testedwith = 'ships-with-hg-core' for |
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
42 # extensions which SHIP WITH MERCURIAL. Non-mainline extensions should |
43 # be specifying the version(s) of Mercurial they are tested with, or |
43 # be specifying the version(s) of Mercurial they are tested with, or |
44 # leave the attribute unspecified. |
44 # leave the attribute unspecified. |
45 testedwith = 'ships-with-hg-core' |
45 testedwith = b'ships-with-hg-core' |
46 |
46 |
47 |
47 |
48 @command( |
48 @command( |
49 'censor', |
49 b'censor', |
50 [ |
50 [ |
51 ('r', 'rev', '', _('censor file from specified revision'), _('REV')), |
51 ( |
52 ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT')), |
52 b'r', |
|
53 b'rev', |
|
54 b'', |
|
55 _(b'censor file from specified revision'), |
|
56 _(b'REV'), |
|
57 ), |
|
58 (b't', b'tombstone', b'', _(b'replacement tombstone data'), _(b'TEXT')), |
53 ], |
59 ], |
54 _('-r REV [-t TEXT] [FILE]'), |
60 _(b'-r REV [-t TEXT] [FILE]'), |
55 helpcategory=command.CATEGORY_MAINTENANCE, |
61 helpcategory=command.CATEGORY_MAINTENANCE, |
56 ) |
62 ) |
57 def censor(ui, repo, path, rev='', tombstone='', **opts): |
63 def censor(ui, repo, path, rev=b'', tombstone=b'', **opts): |
58 with repo.wlock(), repo.lock(): |
64 with repo.wlock(), repo.lock(): |
59 return _docensor(ui, repo, path, rev, tombstone, **opts) |
65 return _docensor(ui, repo, path, rev, tombstone, **opts) |
60 |
66 |
61 |
67 |
62 def _docensor(ui, repo, path, rev='', tombstone='', **opts): |
68 def _docensor(ui, repo, path, rev=b'', tombstone=b'', **opts): |
63 if not path: |
69 if not path: |
64 raise error.Abort(_('must specify file path to censor')) |
70 raise error.Abort(_(b'must specify file path to censor')) |
65 if not rev: |
71 if not rev: |
66 raise error.Abort(_('must specify revision to censor')) |
72 raise error.Abort(_(b'must specify revision to censor')) |
67 |
73 |
68 wctx = repo[None] |
74 wctx = repo[None] |
69 |
75 |
70 m = scmutil.match(wctx, (path,)) |
76 m = scmutil.match(wctx, (path,)) |
71 if m.anypats() or len(m.files()) != 1: |
77 if m.anypats() or len(m.files()) != 1: |
72 raise error.Abort(_('can only specify an explicit filename')) |
78 raise error.Abort(_(b'can only specify an explicit filename')) |
73 path = m.files()[0] |
79 path = m.files()[0] |
74 flog = repo.file(path) |
80 flog = repo.file(path) |
75 if not len(flog): |
81 if not len(flog): |
76 raise error.Abort(_('cannot censor file with no history')) |
82 raise error.Abort(_(b'cannot censor file with no history')) |
77 |
83 |
78 rev = scmutil.revsingle(repo, rev, rev).rev() |
84 rev = scmutil.revsingle(repo, rev, rev).rev() |
79 try: |
85 try: |
80 ctx = repo[rev] |
86 ctx = repo[rev] |
81 except KeyError: |
87 except KeyError: |
82 raise error.Abort(_('invalid revision identifier %s') % rev) |
88 raise error.Abort(_(b'invalid revision identifier %s') % rev) |
83 |
89 |
84 try: |
90 try: |
85 fctx = ctx.filectx(path) |
91 fctx = ctx.filectx(path) |
86 except error.LookupError: |
92 except error.LookupError: |
87 raise error.Abort(_('file does not exist at revision %s') % rev) |
93 raise error.Abort(_(b'file does not exist at revision %s') % rev) |
88 |
94 |
89 fnode = fctx.filenode() |
95 fnode = fctx.filenode() |
90 heads = [] |
96 heads = [] |
91 for headnode in repo.heads(): |
97 for headnode in repo.heads(): |
92 hc = repo[headnode] |
98 hc = repo[headnode] |
93 if path in hc and hc.filenode(path) == fnode: |
99 if path in hc and hc.filenode(path) == fnode: |
94 heads.append(hc) |
100 heads.append(hc) |
95 if heads: |
101 if heads: |
96 headlist = ', '.join([short(c.node()) for c in heads]) |
102 headlist = b', '.join([short(c.node()) for c in heads]) |
97 raise error.Abort( |
103 raise error.Abort( |
98 _('cannot censor file in heads (%s)') % headlist, |
104 _(b'cannot censor file in heads (%s)') % headlist, |
99 hint=_('clean/delete and commit first'), |
105 hint=_(b'clean/delete and commit first'), |
100 ) |
106 ) |
101 |
107 |
102 wp = wctx.parents() |
108 wp = wctx.parents() |
103 if ctx.node() in [p.node() for p in wp]: |
109 if ctx.node() in [p.node() for p in wp]: |
104 raise error.Abort( |
110 raise error.Abort( |
105 _('cannot censor working directory'), |
111 _(b'cannot censor working directory'), |
106 hint=_('clean/delete/update first'), |
112 hint=_(b'clean/delete/update first'), |
107 ) |
113 ) |
108 |
114 |
109 with repo.transaction(b'censor') as tr: |
115 with repo.transaction(b'censor') as tr: |
110 flog.censorrevision(tr, fnode, tombstone=tombstone) |
116 flog.censorrevision(tr, fnode, tombstone=tombstone) |