183 |
179 |
184 templatefilters.filters['utcdate'] = utcdate |
180 templatefilters.filters['utcdate'] = utcdate |
185 self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
181 self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
186 False, '', False) |
182 False, '', False) |
187 |
183 |
188 def substitute(self, node, data, subfunc): |
184 def getnode(self, path, fnode): |
189 '''Obtains file's changenode if commit node not given, |
185 '''Derives changenode from file context.''' |
190 and calls given substitution function.''' |
186 c = context.filectx(self.repo, path, fileid=fnode) |
191 if self.commitnode: |
187 return c.node() |
192 fnode = self.commitnode |
188 |
193 else: |
189 def substitute(self, data, path, node, subfunc): |
194 c = context.filectx(self.repo, self.path, fileid=node) |
190 '''Replaces keywords in data with expanded template.''' |
195 fnode = c.node() |
|
196 |
|
197 def kwsub(mobj): |
191 def kwsub(mobj): |
198 '''Substitutes keyword using corresponding template.''' |
|
199 kw = mobj.group(1) |
192 kw = mobj.group(1) |
200 self.ct.use_template(self.templates[kw]) |
193 self.ct.use_template(self.templates[kw]) |
201 self.ui.pushbuffer() |
194 self.ui.pushbuffer() |
202 self.ct.show(changenode=fnode, root=self.repo.root, file=self.path) |
195 self.ct.show(changenode=node, root=self.repo.root, file=path) |
203 ekw = templatefilters.firstline(self.ui.popbuffer()) |
196 ekw = templatefilters.firstline(self.ui.popbuffer()) |
204 return '$%s: %s $' % (kw, ekw) |
197 return '$%s: %s $' % (kw, ekw) |
205 |
|
206 return subfunc(kwsub, data) |
198 return subfunc(kwsub, data) |
207 |
199 |
208 def expand(self, node, data): |
200 def expand(self, path, node, data): |
209 '''Returns data with keywords expanded.''' |
201 '''Returns data with keywords expanded.''' |
210 if self.restrict or util.binary(data): |
202 if not self.restrict and self.matcher(path) and not util.binary(data): |
211 return data |
203 changenode = self.getnode(path, node) |
212 return self.substitute(node, data, self.re_kw.sub) |
204 return self.substitute(data, path, changenode, self.re_kw.sub) |
213 |
205 return data |
214 def process(self, node, data, expand): |
206 |
215 '''Returns a tuple: data, count. |
207 def iskwfile(self, path, islink): |
216 Count is number of keywords/keyword substitutions, |
208 '''Returns true if path matches [keyword] pattern |
217 telling caller whether to act on file containing data.''' |
209 and is not a symbolic link. |
218 if util.binary(data): |
210 Caveat: localrepository._link fails on Windows.''' |
219 return data, None |
211 return self.matcher(path) and not islink(path) |
220 if expand: |
212 |
221 return self.substitute(node, data, self.re_kw.subn) |
213 def overwrite(self, node=None, expand=True, files=None): |
222 return data, self.re_kw.search(data) |
214 '''Overwrites selected files expanding/shrinking keywords.''' |
223 |
215 ctx = self.repo.changectx(node) |
224 def shrink(self, text): |
216 mf = ctx.manifest() |
|
217 if node is not None: # commit |
|
218 files = [f for f in ctx.files() if f in mf] |
|
219 notify = self.ui.debug |
|
220 else: # kwexpand/kwshrink |
|
221 notify = self.ui.note |
|
222 candidates = [f for f in files if self.iskwfile(f, mf.linkf)] |
|
223 if candidates: |
|
224 self.restrict = True # do not expand when reading |
|
225 candidates.sort() |
|
226 action = expand and 'expanding' or 'shrinking' |
|
227 for f in candidates: |
|
228 fp = self.repo.file(f) |
|
229 data = fp.read(mf[f]) |
|
230 if util.binary(data): |
|
231 continue |
|
232 if expand: |
|
233 changenode = node or self.getnode(f, mf[f]) |
|
234 data, found = self.substitute(data, f, changenode, |
|
235 self.re_kw.subn) |
|
236 else: |
|
237 found = self.re_kw.search(data) |
|
238 if found: |
|
239 notify(_('overwriting %s %s keywords\n') % (f, action)) |
|
240 self.repo.wwrite(f, data, mf.flags(f)) |
|
241 self.repo.dirstate.normal(f) |
|
242 self.restrict = False |
|
243 |
|
244 def shrinktext(self, text): |
|
245 '''Unconditionally removes all keyword substitutions from text.''' |
|
246 return self.re_kw.sub(r'$\1$', text) |
|
247 |
|
248 def shrink(self, fname, text): |
225 '''Returns text with all keyword substitutions removed.''' |
249 '''Returns text with all keyword substitutions removed.''' |
226 if util.binary(text): |
250 if self.matcher(fname) and not util.binary(text): |
227 return text |
251 return self.shrinktext(text) |
228 return self.re_kw.sub(r'$\1$', text) |
252 return text |
|
253 |
|
254 def shrinklines(self, fname, lines): |
|
255 '''Returns lines with keyword substitutions removed.''' |
|
256 if self.matcher(fname): |
|
257 text = ''.join(lines) |
|
258 if not util.binary(text): |
|
259 return self.shrinktext(text).splitlines(True) |
|
260 return lines |
|
261 |
|
262 def wread(self, fname, data): |
|
263 '''If in restricted mode returns data read from wdir with |
|
264 keyword substitutions removed.''' |
|
265 return self.restrict and self.shrink(fname, data) or data |
229 |
266 |
230 class kwfilelog(filelog.filelog): |
267 class kwfilelog(filelog.filelog): |
231 ''' |
268 ''' |
232 Subclass of filelog to hook into its read, add, cmp methods. |
269 Subclass of filelog to hook into its read, add, cmp methods. |
233 Keywords are "stored" unexpanded, and processed on reading. |
270 Keywords are "stored" unexpanded, and processed on reading. |
234 ''' |
271 ''' |
235 def __init__(self, opener, path): |
272 def __init__(self, opener, path): |
236 super(kwfilelog, self).__init__(opener, path) |
273 super(kwfilelog, self).__init__(opener, path) |
237 _kwtemplater.path = path |
274 self.path = path |
238 |
|
239 def kwctread(self, node, expand): |
|
240 '''Reads expanding and counting keywords, called from _overwrite.''' |
|
241 data = super(kwfilelog, self).read(node) |
|
242 return _kwtemplater.process(node, data, expand) |
|
243 |
275 |
244 def read(self, node): |
276 def read(self, node): |
245 '''Expands keywords when reading filelog.''' |
277 '''Expands keywords when reading filelog.''' |
246 data = super(kwfilelog, self).read(node) |
278 data = super(kwfilelog, self).read(node) |
247 return _kwtemplater.expand(node, data) |
279 return _kwtemplater.expand(self.path, node, data) |
248 |
280 |
249 def add(self, text, meta, tr, link, p1=None, p2=None): |
281 def add(self, text, meta, tr, link, p1=None, p2=None): |
250 '''Removes keyword substitutions when adding to filelog.''' |
282 '''Removes keyword substitutions when adding to filelog.''' |
251 text = _kwtemplater.shrink(text) |
283 text = _kwtemplater.shrink(self.path, text) |
252 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
284 return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
253 |
285 |
254 def cmp(self, node, text): |
286 def cmp(self, node, text): |
255 '''Removes keyword substitutions for comparison.''' |
287 '''Removes keyword substitutions for comparison.''' |
256 text = _kwtemplater.shrink(text) |
288 text = _kwtemplater.shrink(self.path, text) |
257 if self.renamed(node): |
289 if self.renamed(node): |
258 t2 = super(kwfilelog, self).read(node) |
290 t2 = super(kwfilelog, self).read(node) |
259 return t2 != text |
291 return t2 != text |
260 return revlog.revlog.cmp(self, node, text) |
292 return revlog.revlog.cmp(self, node, text) |
261 |
|
262 def _iskwfile(f, link): |
|
263 return not link(f) and _kwtemplater.matcher(f) |
|
264 |
293 |
265 def _status(ui, repo, *pats, **opts): |
294 def _status(ui, repo, *pats, **opts): |
266 '''Bails out if [keyword] configuration is not active. |
295 '''Bails out if [keyword] configuration is not active. |
267 Returns status of working directory.''' |
296 Returns status of working directory.''' |
268 if _kwtemplater: |
297 if _kwtemplater: |
270 return repo.status(files=files, match=match, list_clean=True) |
299 return repo.status(files=files, match=match, list_clean=True) |
271 if ui.configitems('keyword'): |
300 if ui.configitems('keyword'): |
272 raise util.Abort(_('[keyword] patterns cannot match')) |
301 raise util.Abort(_('[keyword] patterns cannot match')) |
273 raise util.Abort(_('no [keyword] patterns configured')) |
302 raise util.Abort(_('no [keyword] patterns configured')) |
274 |
303 |
275 def _overwrite(ui, repo, node=None, expand=True, files=None): |
|
276 '''Overwrites selected files expanding/shrinking keywords.''' |
|
277 ctx = repo.changectx(node) |
|
278 mf = ctx.manifest() |
|
279 if node is not None: # commit |
|
280 _kwtemplater.commitnode = node |
|
281 files = [f for f in ctx.files() if f in mf] |
|
282 notify = ui.debug |
|
283 else: # kwexpand/kwshrink |
|
284 notify = ui.note |
|
285 candidates = [f for f in files if _iskwfile(f, mf.linkf)] |
|
286 if candidates: |
|
287 candidates.sort() |
|
288 action = expand and 'expanding' or 'shrinking' |
|
289 for f in candidates: |
|
290 fp = repo.file(f, kwmatch=True) |
|
291 data, kwfound = fp.kwctread(mf[f], expand) |
|
292 if kwfound: |
|
293 notify(_('overwriting %s %s keywords\n') % (f, action)) |
|
294 repo.wwrite(f, data, mf.flags(f)) |
|
295 repo.dirstate.normal(f) |
|
296 |
|
297 def _kwfwrite(ui, repo, expand, *pats, **opts): |
304 def _kwfwrite(ui, repo, expand, *pats, **opts): |
298 '''Selects files and passes them to _overwrite.''' |
305 '''Selects files and passes them to kwtemplater.overwrite.''' |
299 status = _status(ui, repo, *pats, **opts) |
306 status = _status(ui, repo, *pats, **opts) |
300 modified, added, removed, deleted, unknown, ignored, clean = status |
307 modified, added, removed, deleted, unknown, ignored, clean = status |
301 if modified or added or removed or deleted: |
308 if modified or added or removed or deleted: |
302 raise util.Abort(_('outstanding uncommitted changes in given files')) |
309 raise util.Abort(_('outstanding uncommitted changes in given files')) |
303 wlock = lock = None |
310 wlock = lock = None |
304 try: |
311 try: |
305 wlock = repo.wlock() |
312 wlock = repo.wlock() |
306 lock = repo.lock() |
313 lock = repo.lock() |
307 _overwrite(ui, repo, expand=expand, files=clean) |
314 _kwtemplater.overwrite(expand=expand, files=clean) |
308 finally: |
315 finally: |
309 del wlock, lock |
316 del wlock, lock |
310 |
317 |
311 |
318 |
312 def demo(ui, repo, *args, **opts): |
319 def demo(ui, repo, *args, **opts): |