Show More
@@ -101,8 +101,8 b' def utcdate(date):' | |||||
101 | return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) |
|
101 | return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0])) | |
102 |
|
102 | |||
103 |
|
103 | |||
104 |
_kwtemplater = _cmd = |
|
104 | _kwtemplater = _cmd = None | |
105 |
|
105 | |||
106 | # store originals of monkeypatches |
|
106 | # store originals of monkeypatches | |
107 | _patchfile_init = patch.patchfile.__init__ |
|
107 | _patchfile_init = patch.patchfile.__init__ | |
108 | _patch_diff = patch.diff |
|
108 | _patch_diff = patch.diff | |
@@ -112,10 +112,8 b' def _kwpatchfile_init(self, ui, fname, m' | |||||
112 | '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
|
112 | '''Monkeypatch/wrap patch.patchfile.__init__ to avoid | |
113 | rejects or conflicts due to expanded keywords in working dir.''' |
|
113 | rejects or conflicts due to expanded keywords in working dir.''' | |
114 | _patchfile_init(self, ui, fname, missing=missing) |
|
114 | _patchfile_init(self, ui, fname, missing=missing) | |
115 | if _kwtemplater.matcher(self.fname): |
|
115 | # shrink keywords read from working dir | |
116 | # shrink keywords read from working dir |
|
116 | self.lines = _kwtemplater.shrinklines(self.fname, self.lines) | |
117 | kwshrunk = _kwtemplater.shrink(''.join(self.lines)) |
|
|||
118 | self.lines = kwshrunk.splitlines(True) |
|
|||
119 |
|
117 | |||
120 | def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always, |
|
118 | def _kw_diff(repo, node1=None, node2=None, files=None, match=util.always, | |
121 | fp=None, changes=None, opts=None): |
|
119 | fp=None, changes=None, opts=None): | |
@@ -162,13 +160,11 b' class kwtemplater(object):' | |||||
162 | 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', |
|
160 | 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', | |
163 | } |
|
161 | } | |
164 |
|
162 | |||
165 |
def __init__(self, ui, repo, inc, exc |
|
163 | def __init__(self, ui, repo, inc, exc): | |
166 | self.ui = ui |
|
164 | self.ui = ui | |
167 | self.repo = repo |
|
165 | self.repo = repo | |
168 | self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1] |
|
166 | self.matcher = util.matcher(repo.root, inc=inc, exc=exc)[1] | |
169 |
self.restrict = |
|
167 | self.restrict = _cmd in restricted.split() | |
170 | self.commitnode = None |
|
|||
171 | self.path = '' |
|
|||
172 |
|
168 | |||
173 | kwmaps = self.ui.configitems('keywordmaps') |
|
169 | kwmaps = self.ui.configitems('keywordmaps') | |
174 | if kwmaps: # override default templates |
|
170 | if kwmaps: # override default templates | |
@@ -183,47 +179,89 b' class kwtemplater(object):' | |||||
183 | self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
|
179 | self.ct = cmdutil.changeset_templater(self.ui, self.repo, | |
184 | False, '', False) |
|
180 | False, '', False) | |
185 |
|
181 | |||
186 | def substitute(self, node, data, subfunc): |
|
182 | def getnode(self, path, fnode): | |
187 | '''Obtains file's changenode if commit node not given, |
|
183 | '''Derives changenode from file path and filenode.''' | |
188 | and calls given substitution function.''' |
|
184 | # used by kwfilelog.read and kwexpand | |
189 | if self.commitnode: |
|
185 | c = context.filectx(self.repo, path, fileid=fnode) | |
190 | fnode = self.commitnode |
|
186 | return c.node() | |
191 | else: |
|
|||
192 | c = context.filectx(self.repo, self.path, fileid=node) |
|
|||
193 | fnode = c.node() |
|
|||
194 |
|
187 | |||
|
188 | def substitute(self, data, path, node, subfunc): | |||
|
189 | '''Replaces keywords in data with expanded template.''' | |||
195 | def kwsub(mobj): |
|
190 | def kwsub(mobj): | |
196 | '''Substitutes keyword using corresponding template.''' |
|
|||
197 | kw = mobj.group(1) |
|
191 | kw = mobj.group(1) | |
198 | self.ct.use_template(self.templates[kw]) |
|
192 | self.ct.use_template(self.templates[kw]) | |
199 | self.ui.pushbuffer() |
|
193 | self.ui.pushbuffer() | |
200 |
self.ct.show(changenode= |
|
194 | self.ct.show(changenode=node, root=self.repo.root, file=path) | |
201 | ekw = templatefilters.firstline(self.ui.popbuffer()) |
|
195 | ekw = templatefilters.firstline(self.ui.popbuffer()) | |
202 | return '$%s: %s $' % (kw, ekw) |
|
196 | return '$%s: %s $' % (kw, ekw) | |
203 |
|
||||
204 | return subfunc(kwsub, data) |
|
197 | return subfunc(kwsub, data) | |
205 |
|
198 | |||
206 | def expand(self, node, data): |
|
199 | def expand(self, path, node, data): | |
207 | '''Returns data with keywords expanded.''' |
|
200 | '''Returns data with keywords expanded.''' | |
208 |
if self.restrict |
|
201 | if not self.restrict and self.matcher(path) and not util.binary(data): | |
209 | return data |
|
202 | changenode = self.getnode(path, node) | |
210 |
return self.substitute( |
|
203 | return self.substitute(data, path, changenode, self.re_kw.sub) | |
|
204 | return data | |||
|
205 | ||||
|
206 | def iskwfile(self, path, islink): | |||
|
207 | '''Returns true if path matches [keyword] pattern | |||
|
208 | and is not a symbolic link. | |||
|
209 | Caveat: localrepository._link fails on Windows.''' | |||
|
210 | return self.matcher(path) and not islink(path) | |||
211 |
|
211 | |||
212 |
def |
|
212 | def overwrite(self, node=None, expand=True, files=None): | |
213 | '''Returns a tuple: data, count. |
|
213 | '''Overwrites selected files expanding/shrinking keywords.''' | |
214 | Count is number of keywords/keyword substitutions, |
|
214 | ctx = self.repo.changectx(node) | |
215 | telling caller whether to act on file containing data.''' |
|
215 | mf = ctx.manifest() | |
216 | if util.binary(data): |
|
216 | if node is not None: # commit | |
217 | return data, None |
|
217 | files = [f for f in ctx.files() if f in mf] | |
218 | if expand: |
|
218 | notify = self.ui.debug | |
219 | return self.substitute(node, data, self.re_kw.subn) |
|
219 | else: # kwexpand/kwshrink | |
220 | return data, self.re_kw.search(data) |
|
220 | notify = self.ui.note | |
|
221 | candidates = [f for f in files if self.iskwfile(f, mf.linkf)] | |||
|
222 | if candidates: | |||
|
223 | self.restrict = True # do not expand when reading | |||
|
224 | candidates.sort() | |||
|
225 | action = expand and 'expanding' or 'shrinking' | |||
|
226 | for f in candidates: | |||
|
227 | fp = self.repo.file(f) | |||
|
228 | data = fp.read(mf[f]) | |||
|
229 | if util.binary(data): | |||
|
230 | continue | |||
|
231 | if expand: | |||
|
232 | changenode = node or self.getnode(f, mf[f]) | |||
|
233 | data, found = self.substitute(data, f, changenode, | |||
|
234 | self.re_kw.subn) | |||
|
235 | else: | |||
|
236 | found = self.re_kw.search(data) | |||
|
237 | if found: | |||
|
238 | notify(_('overwriting %s %s keywords\n') % (f, action)) | |||
|
239 | self.repo.wwrite(f, data, mf.flags(f)) | |||
|
240 | self.repo.dirstate.normal(f) | |||
|
241 | self.restrict = False | |||
221 |
|
242 | |||
222 | def shrink(self, text): |
|
243 | def shrinktext(self, text): | |
|
244 | '''Unconditionally removes all keyword substitutions from text.''' | |||
|
245 | return self.re_kw.sub(r'$\1$', text) | |||
|
246 | ||||
|
247 | def shrink(self, fname, text): | |||
223 | '''Returns text with all keyword substitutions removed.''' |
|
248 | '''Returns text with all keyword substitutions removed.''' | |
224 | if util.binary(text): |
|
249 | if self.matcher(fname) and not util.binary(text): | |
225 | return text |
|
250 | return self.shrinktext(text) | |
226 |
return |
|
251 | return text | |
|
252 | ||||
|
253 | def shrinklines(self, fname, lines): | |||
|
254 | '''Returns lines with keyword substitutions removed.''' | |||
|
255 | if self.matcher(fname): | |||
|
256 | text = ''.join(lines) | |||
|
257 | if not util.binary(text): | |||
|
258 | return self.shrinktext(text).splitlines(True) | |||
|
259 | return lines | |||
|
260 | ||||
|
261 | def wread(self, fname, data): | |||
|
262 | '''If in restricted mode returns data read from wdir with | |||
|
263 | keyword substitutions removed.''' | |||
|
264 | return self.restrict and self.shrink(fname, data) or data | |||
227 |
|
265 | |||
228 | class kwfilelog(filelog.filelog): |
|
266 | class kwfilelog(filelog.filelog): | |
229 | ''' |
|
267 | ''' | |
@@ -232,34 +270,26 b' class kwfilelog(filelog.filelog):' | |||||
232 | ''' |
|
270 | ''' | |
233 | def __init__(self, opener, path): |
|
271 | def __init__(self, opener, path): | |
234 | super(kwfilelog, self).__init__(opener, path) |
|
272 | super(kwfilelog, self).__init__(opener, path) | |
235 |
|
|
273 | self.path = path | |
236 |
|
||||
237 | def kwctread(self, node, expand): |
|
|||
238 | '''Reads expanding and counting keywords, called from _overwrite.''' |
|
|||
239 | data = super(kwfilelog, self).read(node) |
|
|||
240 | return _kwtemplater.process(node, data, expand) |
|
|||
241 |
|
274 | |||
242 | def read(self, node): |
|
275 | def read(self, node): | |
243 | '''Expands keywords when reading filelog.''' |
|
276 | '''Expands keywords when reading filelog.''' | |
244 | data = super(kwfilelog, self).read(node) |
|
277 | data = super(kwfilelog, self).read(node) | |
245 | return _kwtemplater.expand(node, data) |
|
278 | return _kwtemplater.expand(self.path, node, data) | |
246 |
|
279 | |||
247 | def add(self, text, meta, tr, link, p1=None, p2=None): |
|
280 | def add(self, text, meta, tr, link, p1=None, p2=None): | |
248 | '''Removes keyword substitutions when adding to filelog.''' |
|
281 | '''Removes keyword substitutions when adding to filelog.''' | |
249 | text = _kwtemplater.shrink(text) |
|
282 | text = _kwtemplater.shrink(self.path, text) | |
250 | return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) |
|
283 | return super(kwfilelog, self).add(text, meta, tr, link, p1=p1, p2=p2) | |
251 |
|
284 | |||
252 | def cmp(self, node, text): |
|
285 | def cmp(self, node, text): | |
253 | '''Removes keyword substitutions for comparison.''' |
|
286 | '''Removes keyword substitutions for comparison.''' | |
254 | text = _kwtemplater.shrink(text) |
|
287 | text = _kwtemplater.shrink(self.path, text) | |
255 | if self.renamed(node): |
|
288 | if self.renamed(node): | |
256 | t2 = super(kwfilelog, self).read(node) |
|
289 | t2 = super(kwfilelog, self).read(node) | |
257 | return t2 != text |
|
290 | return t2 != text | |
258 | return revlog.revlog.cmp(self, node, text) |
|
291 | return revlog.revlog.cmp(self, node, text) | |
259 |
|
292 | |||
260 | def _iskwfile(f, link): |
|
|||
261 | return not link(f) and _kwtemplater.matcher(f) |
|
|||
262 |
|
||||
263 | def _status(ui, repo, *pats, **opts): |
|
293 | def _status(ui, repo, *pats, **opts): | |
264 | '''Bails out if [keyword] configuration is not active. |
|
294 | '''Bails out if [keyword] configuration is not active. | |
265 | Returns status of working directory.''' |
|
295 | Returns status of working directory.''' | |
@@ -270,30 +300,8 b' def _status(ui, repo, *pats, **opts):' | |||||
270 | raise util.Abort(_('[keyword] patterns cannot match')) |
|
300 | raise util.Abort(_('[keyword] patterns cannot match')) | |
271 | raise util.Abort(_('no [keyword] patterns configured')) |
|
301 | raise util.Abort(_('no [keyword] patterns configured')) | |
272 |
|
302 | |||
273 | def _overwrite(ui, repo, node=None, expand=True, files=None): |
|
|||
274 | '''Overwrites selected files expanding/shrinking keywords.''' |
|
|||
275 | ctx = repo.changectx(node) |
|
|||
276 | mf = ctx.manifest() |
|
|||
277 | if node is not None: # commit |
|
|||
278 | _kwtemplater.commitnode = node |
|
|||
279 | files = [f for f in ctx.files() if f in mf] |
|
|||
280 | notify = ui.debug |
|
|||
281 | else: # kwexpand/kwshrink |
|
|||
282 | notify = ui.note |
|
|||
283 | candidates = [f for f in files if _iskwfile(f, mf.linkf)] |
|
|||
284 | if candidates: |
|
|||
285 | candidates.sort() |
|
|||
286 | action = expand and 'expanding' or 'shrinking' |
|
|||
287 | for f in candidates: |
|
|||
288 | fp = repo.file(f, kwmatch=True) |
|
|||
289 | data, kwfound = fp.kwctread(mf[f], expand) |
|
|||
290 | if kwfound: |
|
|||
291 | notify(_('overwriting %s %s keywords\n') % (f, action)) |
|
|||
292 | repo.wwrite(f, data, mf.flags(f)) |
|
|||
293 | repo.dirstate.normal(f) |
|
|||
294 |
|
||||
295 | def _kwfwrite(ui, repo, expand, *pats, **opts): |
|
303 | def _kwfwrite(ui, repo, expand, *pats, **opts): | |
296 |
'''Selects files and passes them to |
|
304 | '''Selects files and passes them to kwtemplater.overwrite.''' | |
297 | status = _status(ui, repo, *pats, **opts) |
|
305 | status = _status(ui, repo, *pats, **opts) | |
298 | modified, added, removed, deleted, unknown, ignored, clean = status |
|
306 | modified, added, removed, deleted, unknown, ignored, clean = status | |
299 | if modified or added or removed or deleted: |
|
307 | if modified or added or removed or deleted: | |
@@ -302,7 +310,7 b' def _kwfwrite(ui, repo, expand, *pats, *' | |||||
302 | try: |
|
310 | try: | |
303 | wlock = repo.wlock() |
|
311 | wlock = repo.wlock() | |
304 | lock = repo.lock() |
|
312 | lock = repo.lock() | |
305 |
_overwrite( |
|
313 | _kwtemplater.overwrite(expand=expand, files=clean) | |
306 | finally: |
|
314 | finally: | |
307 | del wlock, lock |
|
315 | del wlock, lock | |
308 |
|
316 | |||
@@ -412,7 +420,7 b' def files(ui, repo, *pats, **opts):' | |||||
412 | files.sort() |
|
420 | files.sort() | |
413 | wctx = repo.workingctx() |
|
421 | wctx = repo.workingctx() | |
414 | islink = lambda p: 'l' in wctx.fileflags(p) |
|
422 | islink = lambda p: 'l' in wctx.fileflags(p) | |
415 | kwfiles = [f for f in files if _iskwfile(f, islink)] |
|
423 | kwfiles = [f for f in files if _kwtemplater.iskwfile(f, islink)] | |
416 | cwd = pats and repo.getcwd() or '' |
|
424 | cwd = pats and repo.getcwd() or '' | |
417 | kwfstats = not opts.get('ignore') and (('K', kwfiles),) or () |
|
425 | kwfstats = not opts.get('ignore') and (('K', kwfiles),) or () | |
418 | if opts.get('all') or opts.get('ignore'): |
|
426 | if opts.get('all') or opts.get('ignore'): | |
@@ -446,7 +454,7 b' def reposetup(ui, repo):' | |||||
446 | global _kwtemplater |
|
454 | global _kwtemplater | |
447 |
|
455 | |||
448 | try: |
|
456 | try: | |
449 |
if (not repo.local() or _cmd in nokwcommands.split() |
|
457 | if (not repo.local() or _cmd in nokwcommands.split() | |
450 | or '.hg' in util.splitpath(repo.root) |
|
458 | or '.hg' in util.splitpath(repo.root) | |
451 | or repo._url.startswith('bundle:')): |
|
459 | or repo._url.startswith('bundle:')): | |
452 | return |
|
460 | return | |
@@ -462,21 +470,17 b' def reposetup(ui, repo):' | |||||
462 | if not inc: |
|
470 | if not inc: | |
463 | return |
|
471 | return | |
464 |
|
472 | |||
465 |
_kwtemplater = kwtemplater(ui, repo, inc, exc |
|
473 | _kwtemplater = kwtemplater(ui, repo, inc, exc) | |
466 |
|
474 | |||
467 | class kwrepo(repo.__class__): |
|
475 | class kwrepo(repo.__class__): | |
468 |
def file(self, f |
|
476 | def file(self, f): | |
469 | if f[0] == '/': |
|
477 | if f[0] == '/': | |
470 | f = f[1:] |
|
478 | f = f[1:] | |
471 | if kwmatch or _kwtemplater.matcher(f): |
|
479 | return kwfilelog(self.sopener, f) | |
472 | return kwfilelog(self.sopener, f) |
|
|||
473 | return filelog.filelog(self.sopener, f) |
|
|||
474 |
|
480 | |||
475 | def wread(self, filename): |
|
481 | def wread(self, filename): | |
476 | data = super(kwrepo, self).wread(filename) |
|
482 | data = super(kwrepo, self).wread(filename) | |
477 |
|
|
483 | return _kwtemplater.wread(filename, data) | |
478 | return _kwtemplater.shrink(data) |
|
|||
479 | return data |
|
|||
480 |
|
484 | |||
481 | def commit(self, files=None, text='', user=None, date=None, |
|
485 | def commit(self, files=None, text='', user=None, date=None, | |
482 | match=util.always, force=False, force_editor=False, |
|
486 | match=util.always, force=False, force_editor=False, | |
@@ -515,7 +519,7 b' def reposetup(ui, repo):' | |||||
515 | for name, cmd in commithooks.iteritems(): |
|
519 | for name, cmd in commithooks.iteritems(): | |
516 | ui.setconfig('hooks', name, cmd) |
|
520 | ui.setconfig('hooks', name, cmd) | |
517 | if node is not None: |
|
521 | if node is not None: | |
518 |
_overwrite( |
|
522 | _kwtemplater.overwrite(node=node) | |
519 | repo.hook('commit', node=node, parent1=_p1, parent2=_p2) |
|
523 | repo.hook('commit', node=node, parent1=_p1, parent2=_p2) | |
520 | return node |
|
524 | return node | |
521 | finally: |
|
525 | finally: |
General Comments 0
You need to be logged in to leave comments.
Login now