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) |