##// END OF EJS Templates
censor: accept multiple revision in a single call...
marmoute -
r52163:ceeb8fa2 default
parent child Browse files
Show More
@@ -36,7 +36,6 b' 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 )
@@ -56,7 +55,7 b" testedwith = b'ships-with-hg-core'"
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 ),
@@ -71,7 +70,7 b" testedwith = b'ships-with-hg-core'"
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,
@@ -84,11 +83,11 b" def censor(ui, repo, path, rev=b'', tomb"
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
@@ -100,18 +99,17 b" def _docensor(ui, repo, path, rev=b'', t"
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()
@@ -120,7 +118,7 b" def _docensor(ui, repo, path, rev=b'', t"
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])
@@ -138,7 +136,8 b" def _docensor(ui, repo, path, rev=b'', t"
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)
@@ -810,7 +810,11 b' class sqlitefilestore:'
810
810
811 return not empty
811 return not empty
812
812
813 def censorrevision(self, tr, censornode, tombstone=b''):
813 def censorrevision(self, tr, censor_nodes, tombstone=b''):
814 for node in censor_nodes:
815 self._censor_one_revision(tr, node, tombstone=tombstone)
816
817 def _censor_one_revision(self, tr, censornode, tombstone):
814 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
818 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
815
819
816 # This restriction is cargo culted from revlogs and makes no sense for
820 # This restriction is cargo culted from revlogs and makes no sense for
@@ -3835,16 +3835,16 b' class revlog:'
3835 if addrevisioncb:
3835 if addrevisioncb:
3836 addrevisioncb(self, rev, node)
3836 addrevisioncb(self, rev, node)
3837
3837
3838 def censorrevision(self, tr, censornode, tombstone=b''):
3838 def censorrevision(self, tr, censor_nodes, tombstone=b''):
3839 if self._format_version == REVLOGV0:
3839 if self._format_version == REVLOGV0:
3840 raise error.RevlogError(
3840 raise error.RevlogError(
3841 _(b'cannot censor with version %d revlogs')
3841 _(b'cannot censor with version %d revlogs')
3842 % self._format_version
3842 % self._format_version
3843 )
3843 )
3844 elif self._format_version == REVLOGV1:
3844 elif self._format_version == REVLOGV1:
3845 rewrite.v1_censor(self, tr, censornode, tombstone)
3845 rewrite.v1_censor(self, tr, censor_nodes, tombstone)
3846 else:
3846 else:
3847 rewrite.v2_censor(self, tr, censornode, tombstone)
3847 rewrite.v2_censor(self, tr, censor_nodes, tombstone)
3848
3848
3849 def verifyintegrity(self, state):
3849 def verifyintegrity(self, state):
3850 """Verifies the integrity of the revlog.
3850 """Verifies the integrity of the revlog.
@@ -51,14 +51,14 b' from . import ('
51 )
51 )
52
52
53
53
54 def v1_censor(rl, tr, censornode, tombstone=b''):
54 def v1_censor(rl, tr, censor_nodes, tombstone=b''):
55 """censors a revision in a "version 1" revlog"""
55 """censors a revision in a "version 1" revlog"""
56 assert rl._format_version == constants.REVLOGV1, rl._format_version
56 assert rl._format_version == constants.REVLOGV1, rl._format_version
57
57
58 # avoid cycle
58 # avoid cycle
59 from .. import revlog
59 from .. import revlog
60
60
61 censorrev = rl.rev(censornode)
61 censor_revs = set(rl.rev(node) for node in censor_nodes)
62 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
62 tombstone = storageutil.packmeta({b'censored': tombstone}, b'')
63
63
64 # Rewriting the revlog in place is hard. Our strategy for censoring is
64 # Rewriting the revlog in place is hard. Our strategy for censoring is
@@ -87,14 +87,14 b' def v1_censor(rl, tr, censornode, tombst'
87 node = rl.node(rev)
87 node = rl.node(rev)
88 p1, p2 = rl.parents(node)
88 p1, p2 = rl.parents(node)
89
89
90 if rev == censorrev:
90 if rev in censor_revs:
91 newrl.addrawrevision(
91 newrl.addrawrevision(
92 tombstone,
92 tombstone,
93 tr,
93 tr,
94 rl.linkrev(censorrev),
94 rl.linkrev(rev),
95 p1,
95 p1,
96 p2,
96 p2,
97 censornode,
97 node,
98 constants.REVIDX_ISCENSORED,
98 constants.REVIDX_ISCENSORED,
99 )
99 )
100
100
@@ -138,12 +138,12 b' def v1_censor(rl, tr, censornode, tombst'
138 rl._load_inner(chunk_cache)
138 rl._load_inner(chunk_cache)
139
139
140
140
141 def v2_censor(revlog, tr, censornode, tombstone=b''):
141 def v2_censor(revlog, tr, censor_nodes, tombstone=b''):
142 """censors a revision in a "version 2" revlog"""
142 """censors a revision in a "version 2" revlog"""
143 assert revlog._format_version != REVLOGV0, revlog._format_version
143 assert revlog._format_version != REVLOGV0, revlog._format_version
144 assert revlog._format_version != REVLOGV1, revlog._format_version
144 assert revlog._format_version != REVLOGV1, revlog._format_version
145
145
146 censor_revs = {revlog.rev(censornode)}
146 censor_revs = {revlog.rev(node) for node in censor_nodes}
147 _rewrite_v2(revlog, tr, censor_revs, tombstone)
147 _rewrite_v2(revlog, tr, censor_revs, tombstone)
148
148
149
149
@@ -1280,7 +1280,7 b' class ifilemutationtests(basetestcase):'
1280 node2 = f.add(b'foo\n' * 32, None, tr, 2, node1, f.nullid)
1280 node2 = f.add(b'foo\n' * 32, None, tr, 2, node1, f.nullid)
1281
1281
1282 with self._maketransactionfn() as tr:
1282 with self._maketransactionfn() as tr:
1283 f.censorrevision(tr, node1)
1283 f.censorrevision(tr, [node1])
1284
1284
1285 self.assertEqual(len(f), 3)
1285 self.assertEqual(len(f), 3)
1286 self.assertEqual(list(f.revs()), [0, 1, 2])
1286 self.assertEqual(list(f.revs()), [0, 1, 2])
@@ -80,7 +80,7 b' Censor revision with 2 offenses'
80 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
80 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C2 -t "remove password" ../../../target
81 checking for the censored content in 2 heads
81 checking for the censored content in 2 heads
82 checking for the censored content in the working directory
82 checking for the censored content in the working directory
83 censoring 1 file revision
83 censoring 1 file revisions
84 $ hg cat -r $H1 target | head -n 10
84 $ hg cat -r $H1 target | head -n 10
85 Tainted file is now sanitized
85 Tainted file is now sanitized
86 $ hg cat -r $H2 target | head -n 10
86 $ hg cat -r $H2 target | head -n 10
@@ -101,7 +101,7 b' Censor revision with 1 offense'
101 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C1 path:target
101 $ hg --config extensions.censor= --cwd foo/bar/baz censor -r $C1 path:target
102 checking for the censored content in 2 heads
102 checking for the censored content in 2 heads
103 checking for the censored content in the working directory
103 checking for the censored content in the working directory
104 censoring 1 file revision
104 censoring 1 file revisions
105 $ hg cat -r $H1 target | head -n 10
105 $ hg cat -r $H1 target | head -n 10
106 Tainted file is now sanitized
106 Tainted file is now sanitized
107 $ hg cat -r $H2 target | head -n 10
107 $ hg cat -r $H2 target | head -n 10
@@ -243,7 +243,7 b" with the file censored, but we can't cen"
243 $ hg --config extensions.censor= censor -r $C3 target
243 $ hg --config extensions.censor= censor -r $C3 target
244 checking for the censored content in 2 heads
244 checking for the censored content in 2 heads
245 checking for the censored content in the working directory
245 checking for the censored content in the working directory
246 censoring 1 file revision
246 censoring 1 file revisions
247 $ hg update -r $H2
247 $ hg update -r $H2
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
248 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
249 $ hg merge -r $C3
249 $ hg merge -r $C3
@@ -294,7 +294,7 b' Can re-add file after being deleted + ce'
294 $ hg --config extensions.censor= censor -r $C4 target
294 $ hg --config extensions.censor= censor -r $C4 target
295 checking for the censored content in 2 heads
295 checking for the censored content in 2 heads
296 checking for the censored content in the working directory
296 checking for the censored content in the working directory
297 censoring 1 file revision
297 censoring 1 file revisions
298 $ hg cat -r $C4 target | head -n 10
298 $ hg cat -r $C4 target | head -n 10
299 $ hg cat -r "$H2^^" target | head -n 10
299 $ hg cat -r "$H2^^" target | head -n 10
300 Tainted file now super sanitized
300 Tainted file now super sanitized
@@ -329,7 +329,7 b' Can censor enough revision to move back '
329 $ hg --config extensions.censor= censor -r $C5 target
329 $ hg --config extensions.censor= censor -r $C5 target
330 checking for the censored content in 2 heads
330 checking for the censored content in 2 heads
331 checking for the censored content in the working directory
331 checking for the censored content in the working directory
332 censoring 1 file revision
332 censoring 1 file revisions
333
333
334 The important part is for the censor operation to not crash and the repository
334 The important part is for the censor operation to not crash and the repository
335 to not be corrupted. Right now this involve keeping the revlog split.
335 to not be corrupted. Right now this involve keeping the revlog split.
@@ -410,7 +410,7 b' Censored nodes can be pushed if they cen'
410 $ hg --config extensions.censor= censor -r $REV target
410 $ hg --config extensions.censor= censor -r $REV target
411 checking for the censored content in 3 heads
411 checking for the censored content in 3 heads
412 checking for the censored content in the working directory
412 checking for the censored content in the working directory
413 censoring 1 file revision
413 censoring 1 file revisions
414 $ hg cat -r $REV target | head -n 10
414 $ hg cat -r $REV target | head -n 10
415 $ hg cat -r $CLEANREV target | head -n 10
415 $ hg cat -r $CLEANREV target | head -n 10
416 Re-sanitized; nothing to see here
416 Re-sanitized; nothing to see here
@@ -513,7 +513,7 b' Can import bundle where first revision o'
513 $ hg --config extensions.censor= censor -r 0 target
513 $ hg --config extensions.censor= censor -r 0 target
514 checking for the censored content in 3 heads
514 checking for the censored content in 3 heads
515 checking for the censored content in the working directory
515 checking for the censored content in the working directory
516 censoring 1 file revision
516 censoring 1 file revisions
517 $ hg bundle -r 0 --base null ../rinit/initbundle
517 $ hg bundle -r 0 --base null ../rinit/initbundle
518 1 changesets found
518 1 changesets found
519 $ cd ../rinit
519 $ cd ../rinit
@@ -530,7 +530,17 b' Can skip the head checking steps'
530
530
531 $ hg --config extensions.censor= censor -r 0 --no-check-heads target
531 $ hg --config extensions.censor= censor -r 0 --no-check-heads target
532 checking for the censored content in the working directory
532 checking for the censored content in the working directory
533 censoring 1 file revision
533 censoring 1 file revisions
534
535 Can censor multiple revision in one go.
536
537 $ cd ../r
538 $ hg --config extensions.censor= censor -r 0+1 target
539 checking for the censored content in 3 heads
540 checking for the censored content in the working directory
541 censoring 2 file revisions
542
543
534
544
535 #if revlogv2
545 #if revlogv2
536
546
@@ -572,7 +582,7 b' Censor the file'
572 $ hg --config extensions.censor= censor -r $B1 target
582 $ hg --config extensions.censor= censor -r $B1 target
573 checking for the censored content in 1 heads
583 checking for the censored content in 1 heads
574 checking for the censored content in the working directory
584 checking for the censored content in the working directory
575 censoring 1 file revision
585 censoring 1 file revisions
576 $ hg cat -r $B1 target | wc -l
586 $ hg cat -r $B1 target | wc -l
577 *0 (re)
587 *0 (re)
578
588
@@ -17,7 +17,7 b''
17 $ hg censor target --config extensions.censor= -r ".^^"
17 $ hg censor target --config extensions.censor= -r ".^^"
18 checking for the censored content in 1 heads
18 checking for the censored content in 1 heads
19 checking for the censored content in the working directory
19 checking for the censored content in the working directory
20 censoring 1 file revision
20 censoring 1 file revisions
21 $ hg update ".^"
21 $ hg update ".^"
22 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
22 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
23 $ cat target
23 $ cat target
General Comments 0
You need to be logged in to leave comments. Login now