41 [('r', 'rev', '', _('censor file from specified revision'), _('REV')), |
41 [('r', 'rev', '', _('censor file from specified revision'), _('REV')), |
42 ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT'))], |
42 ('t', 'tombstone', '', _('replacement tombstone data'), _('TEXT'))], |
43 _('-r REV [-t TEXT] [FILE]')) |
43 _('-r REV [-t TEXT] [FILE]')) |
44 def censor(ui, repo, path, rev='', tombstone='', **opts): |
44 def censor(ui, repo, path, rev='', tombstone='', **opts): |
45 if not path: |
45 if not path: |
46 raise util.Abort(_('must specify file path to censor')) |
46 raise error.Abort(_('must specify file path to censor')) |
47 if not rev: |
47 if not rev: |
48 raise util.Abort(_('must specify revision to censor')) |
48 raise error.Abort(_('must specify revision to censor')) |
49 |
49 |
50 wctx = repo[None] |
50 wctx = repo[None] |
51 |
51 |
52 m = scmutil.match(wctx, (path,)) |
52 m = scmutil.match(wctx, (path,)) |
53 if m.anypats() or len(m.files()) != 1: |
53 if m.anypats() or len(m.files()) != 1: |
54 raise util.Abort(_('can only specify an explicit filename')) |
54 raise error.Abort(_('can only specify an explicit filename')) |
55 path = m.files()[0] |
55 path = m.files()[0] |
56 flog = repo.file(path) |
56 flog = repo.file(path) |
57 if not len(flog): |
57 if not len(flog): |
58 raise util.Abort(_('cannot censor file with no history')) |
58 raise error.Abort(_('cannot censor file with no history')) |
59 |
59 |
60 rev = scmutil.revsingle(repo, rev, rev).rev() |
60 rev = scmutil.revsingle(repo, rev, rev).rev() |
61 try: |
61 try: |
62 ctx = repo[rev] |
62 ctx = repo[rev] |
63 except KeyError: |
63 except KeyError: |
64 raise util.Abort(_('invalid revision identifier %s') % rev) |
64 raise error.Abort(_('invalid revision identifier %s') % rev) |
65 |
65 |
66 try: |
66 try: |
67 fctx = ctx.filectx(path) |
67 fctx = ctx.filectx(path) |
68 except error.LookupError: |
68 except error.LookupError: |
69 raise util.Abort(_('file does not exist at revision %s') % rev) |
69 raise error.Abort(_('file does not exist at revision %s') % rev) |
70 |
70 |
71 fnode = fctx.filenode() |
71 fnode = fctx.filenode() |
72 headctxs = [repo[c] for c in repo.heads()] |
72 headctxs = [repo[c] for c in repo.heads()] |
73 heads = [c for c in headctxs if path in c and c.filenode(path) == fnode] |
73 heads = [c for c in headctxs if path in c and c.filenode(path) == fnode] |
74 if heads: |
74 if heads: |
75 headlist = ', '.join([short(c.node()) for c in heads]) |
75 headlist = ', '.join([short(c.node()) for c in heads]) |
76 raise util.Abort(_('cannot censor file in heads (%s)') % headlist, |
76 raise error.Abort(_('cannot censor file in heads (%s)') % headlist, |
77 hint=_('clean/delete and commit first')) |
77 hint=_('clean/delete and commit first')) |
78 |
78 |
79 wp = wctx.parents() |
79 wp = wctx.parents() |
80 if ctx.node() in [p.node() for p in wp]: |
80 if ctx.node() in [p.node() for p in wp]: |
81 raise util.Abort(_('cannot censor working directory'), |
81 raise error.Abort(_('cannot censor working directory'), |
82 hint=_('clean/delete/update first')) |
82 hint=_('clean/delete/update first')) |
83 |
83 |
84 flogv = flog.version & 0xFFFF |
84 flogv = flog.version & 0xFFFF |
85 if flogv != revlog.REVLOGNG: |
85 if flogv != revlog.REVLOGNG: |
86 raise util.Abort( |
86 raise error.Abort( |
87 _('censor does not support revlog version %d') % (flogv,)) |
87 _('censor does not support revlog version %d') % (flogv,)) |
88 |
88 |
89 tombstone = filelog.packmeta({"censored": tombstone}, "") |
89 tombstone = filelog.packmeta({"censored": tombstone}, "") |
90 |
90 |
91 crev = fctx.filerev() |
91 crev = fctx.filerev() |
92 |
92 |
93 if len(tombstone) > flog.rawsize(crev): |
93 if len(tombstone) > flog.rawsize(crev): |
94 raise util.Abort(_( |
94 raise error.Abort(_( |
95 'censor tombstone must be no longer than censored data')) |
95 'censor tombstone must be no longer than censored data')) |
96 |
96 |
97 # Using two files instead of one makes it easy to rewrite entry-by-entry |
97 # Using two files instead of one makes it easy to rewrite entry-by-entry |
98 idxread = repo.svfs(flog.indexfile, 'r') |
98 idxread = repo.svfs(flog.indexfile, 'r') |
99 idxwrite = repo.svfs(flog.indexfile, 'wb', atomictemp=True) |
99 idxwrite = repo.svfs(flog.indexfile, 'wb', atomictemp=True) |