105 'Source': '{root}/{file},v', |
107 'Source': '{root}/{file},v', |
106 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', |
108 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}', |
107 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', |
109 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}', |
108 } |
110 } |
109 |
111 |
110 def __init__(self, ui, repo, expand, path='', node=None): |
112 def __init__(self, ui, repo, inc, exc): |
111 self.ui = ui |
113 self.ui = ui |
112 self.repo = repo |
114 self.repo = repo |
113 self.ct = expand or None |
115 self.match = util.matcher(repo.root, inc=inc, exc=exc)[1] |
114 self.path = path |
116 self.node = None |
115 self.node = node |
117 self.path = '' |
116 |
118 |
117 kwmaps = self.ui.configitems('keywordmaps') |
119 kwmaps = self.ui.configitems('keywordmaps') |
118 if kwmaps: # override default templates |
120 if kwmaps: # override default templates |
119 kwmaps = [(k, templater.parsestring(v, quoted=False)) |
121 kwmaps = [(k, templater.parsestring(v, quoted=False)) |
120 for (k, v) in kwmaps] |
122 for (k, v) in kwmaps] |
121 self.templates = dict(kwmaps) |
123 self.templates = dict(kwmaps) |
122 escaped = map(re.escape, self.templates.keys()) |
124 escaped = map(re.escape, self.templates.keys()) |
123 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped) |
125 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped) |
124 self.re_kw = re.compile(kwpat) |
126 self.re_kw = re.compile(kwpat) |
125 if self.ct: |
127 |
126 templater.common_filters['utcdate'] = utcdate |
128 templater.common_filters['utcdate'] = utcdate |
127 self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
129 self.ct = cmdutil.changeset_templater(self.ui, self.repo, |
128 False, '', False) |
130 False, '', False) |
129 |
131 |
130 def substitute(self, node, data, subfunc): |
132 def substitute(self, node, data, subfunc): |
131 '''Obtains node if missing, and calls given substitution function.''' |
133 '''Obtains node if missing, and calls given substitution function.''' |
132 if not self.node: |
134 if not self.node: |
133 c = context.filectx(self.repo, self.path, fileid=node) |
135 c = context.filectx(self.repo, self.path, fileid=node) |
148 '''Returns data with keywords expanded.''' |
150 '''Returns data with keywords expanded.''' |
149 if util.binary(data): |
151 if util.binary(data): |
150 return data |
152 return data |
151 return self.substitute(node, data, self.re_kw.sub) |
153 return self.substitute(node, data, self.re_kw.sub) |
152 |
154 |
153 def process(self, node, data): |
155 def process(self, node, data, expand): |
154 '''Returns a tuple: data, count. |
156 '''Returns a tuple: data, count. |
155 Count is number of keywords/keyword substitutions, indicates |
157 Count is number of keywords/keyword substitutions, indicates |
156 to caller whether to act on file containing data. |
158 to caller whether to act on file containing data. |
157 Keywords in data are expanded, if templater was initialized.''' |
159 Keywords in data are expanded, if templater was initialized.''' |
158 if util.binary(data): |
160 if util.binary(data): |
159 return data, None |
161 return data, None |
160 if self.ct: |
162 if expand: |
161 return self.substitute(node, data, self.re_kw.subn) |
163 return self.substitute(node, data, self.re_kw.subn) |
162 return data, self.re_kw.search(data) |
164 return data, self.re_kw.search(data) |
163 |
165 |
164 def shrink(self, text): |
166 def shrink(self, text): |
165 '''Returns text with all keyword substitutions removed.''' |
167 '''Returns text with all keyword substitutions removed.''' |
166 if util.binary(text): |
168 if util.binary(text): |
167 return text |
169 return text |
168 return self.re_kw.sub(r'$\1$', text) |
170 return self.re_kw.sub(r'$\1$', text) |
169 |
171 |
170 def overwrite(self, candidates, man, commit): |
|
171 '''Overwrites files in working directory if keywords are detected. |
|
172 Keywords are expanded if keyword templater is initialized, |
|
173 otherwise their substitution is removed.''' |
|
174 expand = self.ct is not None |
|
175 action = ('shrinking', 'expanding')[expand] |
|
176 notify = (self.ui.note, self.ui.debug)[commit] |
|
177 for f in candidates: |
|
178 fp = self.repo.file(f, kwexp=expand, kwmatch=True) |
|
179 data, kwfound = fp.kwctread(man[f]) |
|
180 if kwfound: |
|
181 notify(_('overwriting %s %s keywords\n') % (f, action)) |
|
182 self.repo.wwrite(f, data, man.flags(f)) |
|
183 self.repo.dirstate.normal(f) |
|
184 |
|
185 class kwfilelog(filelog.filelog): |
172 class kwfilelog(filelog.filelog): |
186 ''' |
173 ''' |
187 Subclass of filelog to hook into its read, add, cmp methods. |
174 Subclass of filelog to hook into its read, add, cmp methods. |
188 Keywords are "stored" unexpanded, and processed on reading. |
175 Keywords are "stored" unexpanded, and processed on reading. |
189 ''' |
176 ''' |
190 def __init__(self, opener, path, kwtemplater): |
177 def __init__(self, opener, path, kwtemplater): |
191 super(kwfilelog, self).__init__(opener, path) |
178 super(kwfilelog, self).__init__(opener, path) |
192 self.kwtemplater = kwtemplater |
179 self.kwtemplater = kwtemplater |
193 |
180 |
194 def kwctread(self, node): |
181 def kwctread(self, node, expand): |
195 '''Reads expanding and counting keywords |
182 '''Reads expanding and counting keywords |
196 (only called from kwtemplater.overwrite).''' |
183 (only called from kwtemplater.overwrite).''' |
197 data = super(kwfilelog, self).read(node) |
184 data = super(kwfilelog, self).read(node) |
198 return self.kwtemplater.process(node, data) |
185 return self.kwtemplater.process(node, data, expand) |
199 |
186 |
200 def read(self, node): |
187 def read(self, node): |
201 '''Expands keywords when reading filelog.''' |
188 '''Expands keywords when reading filelog.''' |
202 data = super(kwfilelog, self).read(node) |
189 data = super(kwfilelog, self).read(node) |
203 return self.kwtemplater.expand(node, data) |
190 return self.kwtemplater.expand(node, data) |
213 if self.renamed(node): |
200 if self.renamed(node): |
214 t2 = super(kwfilelog, self).read(node) |
201 t2 = super(kwfilelog, self).read(node) |
215 return t2 != text |
202 return t2 != text |
216 return revlog.revlog.cmp(self, node, text) |
203 return revlog.revlog.cmp(self, node, text) |
217 |
204 |
218 def _status(ui, repo, *pats, **opts): |
205 def _status(ui, repo, kwtemplater, *pats, **opts): |
219 '''Bails out if [keyword] configuration is not active. |
206 '''Bails out if [keyword] configuration is not active. |
220 Returns status of working directory.''' |
207 Returns status of working directory.''' |
221 if hasattr(ui, 'kwfmatcher'): |
208 if kwtemplater: |
222 files, match, anypats = cmdutil.matchpats(repo, pats, opts) |
209 files, match, anypats = cmdutil.matchpats(repo, pats, opts) |
223 return repo.status(files=files, match=match, list_clean=True) |
210 return repo.status(files=files, match=match, list_clean=True) |
224 if ui.configitems('keyword'): |
211 if ui.configitems('keyword'): |
225 raise util.Abort(_('[keyword] patterns cannot match')) |
212 raise util.Abort(_('[keyword] patterns cannot match')) |
226 raise util.Abort(_('no [keyword] patterns configured')) |
213 raise util.Abort(_('no [keyword] patterns configured')) |
227 |
214 |
228 def _iskwfile(ui, man, f): |
215 def _overwrite(ui, repo, kwtemplater, node=None, expand=True, files=None): |
229 return not man.linkf(f) and ui.kwfmatcher(f) |
216 '''Overwrites selected files expanding/shrinking keywords.''' |
230 |
217 ctx = repo.changectx(node) |
231 def _overwrite(ui, repo, files, node, man, expand, commit): |
218 mf = ctx.manifest() |
232 '''Passes given files to kwtemplater for overwriting.''' |
219 if files is None: |
233 files.sort() |
220 notify = ui.debug # commit |
234 kwt = kwtemplater(ui, repo, expand, node=node) |
221 files = [f for f in ctx.files() if mf.has_key(f)] |
235 kwt.overwrite(files, man, commit) |
222 else: |
|
223 notify = ui.note # kwexpand/kwshrink |
|
224 candidates = [f for f in files if not mf.linkf(f) and kwtemplater.match(f)] |
|
225 if candidates: |
|
226 candidates.sort() |
|
227 action = expand and 'expanding' or 'shrinking' |
|
228 kwtemplater.node = node or ctx.node() |
|
229 for f in candidates: |
|
230 fp = repo.file(f, kwmatch=True) |
|
231 data, kwfound = fp.kwctread(mf[f], expand) |
|
232 if kwfound: |
|
233 notify(_('overwriting %s %s keywords\n') % (f, action)) |
|
234 repo.wwrite(f, data, mf.flags(f)) |
|
235 repo.dirstate.normal(f) |
236 |
236 |
237 def _kwfwrite(ui, repo, expand, *pats, **opts): |
237 def _kwfwrite(ui, repo, expand, *pats, **opts): |
238 '''Selects files and passes them to _overwrite.''' |
238 '''Selects files and passes them to _overwrite.''' |
239 status = _status(ui, repo, *pats, **opts) |
239 global _kwtemplater |
|
240 status = _status(ui, repo, _kwtemplater, *pats, **opts) |
240 modified, added, removed, deleted, unknown, ignored, clean = status |
241 modified, added, removed, deleted, unknown, ignored, clean = status |
241 if modified or added or removed or deleted: |
242 if modified or added or removed or deleted: |
242 raise util.Abort(_('outstanding uncommitted changes in given files')) |
243 raise util.Abort(_('outstanding uncommitted changes in given files')) |
243 wlock = lock = None |
244 wlock = lock = None |
244 try: |
245 try: |
245 wlock = repo.wlock() |
246 wlock = repo.wlock() |
246 lock = repo.lock() |
247 lock = repo.lock() |
247 ctx = repo.changectx() |
248 _overwrite(ui, repo, _kwtemplater, expand=expand, files=clean) |
248 man = ctx.manifest() |
|
249 candidates = [f for f in clean if _iskwfile(ui, man, f)] |
|
250 if candidates: |
|
251 # 7th argument sets commit to False |
|
252 _overwrite(ui, repo, candidates, ctx.node(), man, expand, False) |
|
253 finally: |
249 finally: |
254 del wlock, lock |
250 del wlock, lock |
255 |
251 |
256 |
252 |
257 def demo(ui, repo, *args, **opts): |
253 def demo(ui, repo, *args, **opts): |
350 |
346 |
351 Crosscheck which files in working directory are potential targets for |
347 Crosscheck which files in working directory are potential targets for |
352 keyword expansion. |
348 keyword expansion. |
353 That is, files matched by [keyword] config patterns but not symlinks. |
349 That is, files matched by [keyword] config patterns but not symlinks. |
354 ''' |
350 ''' |
355 status = _status(ui, repo, *pats, **opts) |
351 global _kwtemplater |
|
352 status = _status(ui, repo, _kwtemplater, *pats, **opts) |
356 modified, added, removed, deleted, unknown, ignored, clean = status |
353 modified, added, removed, deleted, unknown, ignored, clean = status |
357 if opts['untracked']: |
354 if opts['untracked']: |
358 files = modified + added + unknown + clean |
355 files = modified + added + unknown + clean |
359 else: |
356 else: |
360 files = modified + added + clean |
357 files = modified + added + clean |
361 files.sort() |
358 files.sort() |
362 kwfiles = [f for f in files if ui.kwfmatcher(f) and not repo._link(f)] |
359 kwfiles = [f for f in files if _kwtemplater.match(f) and not repo._link(f)] |
363 cwd = pats and repo.getcwd() or '' |
360 cwd = pats and repo.getcwd() or '' |
364 allf = opts['all'] |
361 allf = opts['all'] |
365 ignore = opts['ignore'] |
362 ignore = opts['ignore'] |
366 if ignore: |
363 if ignore: |
367 kwfstats = () |
364 kwfstats = () |
419 else: |
416 else: |
420 exc.append(pat) |
417 exc.append(pat) |
421 if not inc: |
418 if not inc: |
422 return |
419 return |
423 |
420 |
424 ui.kwfmatcher = util.matcher(repo.root, inc=inc, exc=exc)[1] |
421 global _kwtemplater |
|
422 _kwtemplater = kwtemplater(ui, repo, inc, exc) |
425 |
423 |
426 class kwrepo(repo.__class__): |
424 class kwrepo(repo.__class__): |
427 def file(self, f, kwexp=True, kwmatch=False): |
425 def file(self, f, kwmatch=False): |
428 if f[0] == '/': |
426 if f[0] == '/': |
429 f = f[1:] |
427 f = f[1:] |
430 if kwmatch or ui.kwfmatcher(f): |
428 if kwmatch or _kwtemplater.match(f): |
431 kwt = kwtemplater(ui, self, kwexp, path=f) |
429 _kwtemplater.path = f |
432 return kwfilelog(self.sopener, f, kwt) |
430 return kwfilelog(self.sopener, f, _kwtemplater) |
433 return filelog.filelog(self.sopener, f) |
431 return filelog.filelog(self.sopener, f) |
434 |
432 |
435 def commit(self, files=None, text='', user=None, date=None, |
433 def commit(self, files=None, text='', user=None, date=None, |
436 match=util.always, force=False, force_editor=False, |
434 match=util.always, force=False, force_editor=False, |
437 p1=None, p2=None, extra={}): |
435 p1=None, p2=None, extra={}): |
466 |
464 |
467 # restore commit hooks |
465 # restore commit hooks |
468 for name, cmd in commithooks: |
466 for name, cmd in commithooks: |
469 ui.setconfig('hooks', name, cmd) |
467 ui.setconfig('hooks', name, cmd) |
470 if node is not None: |
468 if node is not None: |
471 cl = self.changelog.read(node) |
469 _overwrite(ui, self, _kwtemplater, node=node) |
472 mn = self.manifest.read(cl[0]) |
|
473 candidates = [f for f in cl[3] if mn.has_key(f) |
|
474 and _iskwfile(ui, mn, f)] |
|
475 if candidates: |
|
476 # 6th, 7th arguments set expansion, commit to True |
|
477 _overwrite(ui, self, candidates, node, mn, True, True) |
|
478 repo.hook('commit', node=node, parent1=_p1, parent2=_p2) |
470 repo.hook('commit', node=node, parent1=_p1, parent2=_p2) |
479 return node |
471 return node |
480 finally: |
472 finally: |
481 del wlock, lock |
473 del wlock, lock |
482 |
474 |