##// END OF EJS Templates
keyword: code cleanup...
Christian Ebert -
r12723:eaa09d25 default
parent child Browse files
Show More
@@ -1,625 +1,630 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007-2010 Christian Ebert <blacktrash@gmx.net>
4 4 #
5 5 # This software may be used and distributed according to the terms of the
6 6 # GNU General Public License version 2 or any later version.
7 7 #
8 8 # $Id$
9 9 #
10 10 # Keyword expansion hack against the grain of a DSCM
11 11 #
12 12 # There are many good reasons why this is not needed in a distributed
13 13 # SCM, still it may be useful in very small projects based on single
14 14 # files (like LaTeX packages), that are mostly addressed to an
15 15 # audience not running a version control system.
16 16 #
17 17 # For in-depth discussion refer to
18 18 # <http://mercurial.selenic.com/wiki/KeywordPlan>.
19 19 #
20 20 # Keyword expansion is based on Mercurial's changeset template mappings.
21 21 #
22 22 # Binary files are not touched.
23 23 #
24 24 # Files to act upon/ignore are specified in the [keyword] section.
25 25 # Customized keyword template mappings in the [keywordmaps] section.
26 26 #
27 27 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
28 28
29 29 '''expand keywords in tracked files
30 30
31 31 This extension expands RCS/CVS-like or self-customized $Keywords$ in
32 32 tracked text files selected by your configuration.
33 33
34 34 Keywords are only expanded in local repositories and not stored in the
35 35 change history. The mechanism can be regarded as a convenience for the
36 36 current user or for archive distribution.
37 37
38 38 Keywords expand to the changeset data pertaining to the latest change
39 39 relative to the working directory parent of each file.
40 40
41 41 Configuration is done in the [keyword], [keywordset] and [keywordmaps]
42 42 sections of hgrc files.
43 43
44 44 Example::
45 45
46 46 [keyword]
47 47 # expand keywords in every python file except those matching "x*"
48 48 **.py =
49 49 x* = ignore
50 50
51 51 [keywordset]
52 52 # prefer svn- over cvs-like default keywordmaps
53 53 svn = True
54 54
55 55 .. note::
56 56 The more specific you are in your filename patterns the less you
57 57 lose speed in huge repositories.
58 58
59 59 For [keywordmaps] template mapping and expansion demonstration and
60 60 control run :hg:`kwdemo`. See :hg:`help templates` for a list of
61 61 available templates and filters.
62 62
63 63 Three additional date template filters are provided::
64 64
65 65 utcdate "2006/09/18 15:13:13"
66 66 svnutcdate "2006-09-18 15:13:13Z"
67 67 svnisodate "2006-09-18 08:13:13 -700 (Mon, 18 Sep 2006)"
68 68
69 69 The default template mappings (view with :hg:`kwdemo -d`) can be
70 70 replaced with customized keywords and templates. Again, run
71 71 :hg:`kwdemo` to control the results of your config changes.
72 72
73 73 Before changing/disabling active keywords, run :hg:`kwshrink` to avoid
74 74 the risk of inadvertently storing expanded keywords in the change
75 75 history.
76 76
77 77 To force expansion after enabling it, or a configuration change, run
78 78 :hg:`kwexpand`.
79 79
80 80 Expansions spanning more than one line and incremental expansions,
81 81 like CVS' $Log$, are not supported. A keyword template map "Log =
82 82 {desc}" expands to the first line of the changeset description.
83 83 '''
84 84
85 85 from mercurial import commands, context, cmdutil, dispatch, filelog, extensions
86 86 from mercurial import localrepo, match, patch, templatefilters, templater, util
87 87 from mercurial.hgweb import webcommands
88 88 from mercurial.i18n import _
89 89 import re, shutil, tempfile
90 90
91 91 commands.optionalrepo += ' kwdemo'
92 92
93 93 # hg commands that do not act on keywords
94 94 nokwcommands = ('add addremove annotate bundle export grep incoming init log'
95 95 ' outgoing push tip verify convert email glog')
96 96
97 97 # hg commands that trigger expansion only when writing to working dir,
98 98 # not when reading filelog, and unexpand when reading from working dir
99 99 restricted = 'merge kwexpand kwshrink record qrecord resolve transplant'
100 100
101 101 # names of extensions using dorecord
102 102 recordextensions = 'record'
103 103
104 104 # date like in cvs' $Date
105 105 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S')
106 106 # date like in svn's $Date
107 107 svnisodate = lambda x: util.datestr(x, '%Y-%m-%d %H:%M:%S %1%2 (%a, %d %b %Y)')
108 108 # date like in svn's $Id
109 109 svnutcdate = lambda x: util.datestr((x[0], 0), '%Y-%m-%d %H:%M:%SZ')
110 110
111 111 # make keyword tools accessible
112 112 kwtools = {'templater': None, 'hgcmd': ''}
113 113
114 114
115 115 def _defaultkwmaps(ui):
116 116 '''Returns default keywordmaps according to keywordset configuration.'''
117 117 templates = {
118 118 'Revision': '{node|short}',
119 119 'Author': '{author|user}',
120 120 }
121 121 kwsets = ({
122 122 'Date': '{date|utcdate}',
123 123 'RCSfile': '{file|basename},v',
124 124 'RCSFile': '{file|basename},v', # kept for backwards compatibility
125 125 # with hg-keyword
126 126 'Source': '{root}/{file},v',
127 127 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
128 128 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
129 129 }, {
130 130 'Date': '{date|svnisodate}',
131 131 'Id': '{file|basename},v {node|short} {date|svnutcdate} {author|user}',
132 132 'LastChangedRevision': '{node|short}',
133 133 'LastChangedBy': '{author|user}',
134 134 'LastChangedDate': '{date|svnisodate}',
135 135 })
136 136 templates.update(kwsets[ui.configbool('keywordset', 'svn')])
137 137 return templates
138 138
139 139 def _shrinktext(text, subfunc):
140 140 '''Helper for keyword expansion removal in text.
141 141 Depending on subfunc also returns number of substitutions.'''
142 142 return subfunc(r'$\1$', text)
143 143
144 def _preselect(wstatus, changed):
145 '''Retrieves modfied and added files from a working directory state
146 and returns the subset of each contained in given changed files
147 retrieved from a change context.'''
148 modified, added = wstatus[:2]
149 modified = [f for f in modified if f in changed]
150 added = [f for f in added if f in changed]
151 return modified, added
152
144 153
145 154 class kwtemplater(object):
146 155 '''
147 156 Sets up keyword templates, corresponding keyword regex, and
148 157 provides keyword substitution functions.
149 158 '''
150 159
151 160 def __init__(self, ui, repo, inc, exc):
152 161 self.ui = ui
153 162 self.repo = repo
154 163 self.match = match.match(repo.root, '', [], inc, exc)
155 164 self.restrict = kwtools['hgcmd'] in restricted.split()
156 165 self.record = False
157 166
158 167 kwmaps = self.ui.configitems('keywordmaps')
159 168 if kwmaps: # override default templates
160 169 self.templates = dict((k, templater.parsestring(v, False))
161 170 for k, v in kwmaps)
162 171 else:
163 172 self.templates = _defaultkwmaps(self.ui)
164 173 escaped = '|'.join(map(re.escape, self.templates.keys()))
165 174 self.re_kw = re.compile(r'\$(%s)\$' % escaped)
166 175 self.re_kwexp = re.compile(r'\$(%s): [^$\n\r]*? \$' % escaped)
167 176
168 177 templatefilters.filters.update({'utcdate': utcdate,
169 178 'svnisodate': svnisodate,
170 179 'svnutcdate': svnutcdate})
171 180
172 181 def substitute(self, data, path, ctx, subfunc):
173 182 '''Replaces keywords in data with expanded template.'''
174 183 def kwsub(mobj):
175 184 kw = mobj.group(1)
176 185 ct = cmdutil.changeset_templater(self.ui, self.repo,
177 186 False, None, '', False)
178 187 ct.use_template(self.templates[kw])
179 188 self.ui.pushbuffer()
180 189 ct.show(ctx, root=self.repo.root, file=path)
181 190 ekw = templatefilters.firstline(self.ui.popbuffer())
182 191 return '$%s: %s $' % (kw, ekw)
183 192 return subfunc(kwsub, data)
184 193
185 194 def expand(self, path, node, data):
186 195 '''Returns data with keywords expanded.'''
187 196 if not self.restrict and self.match(path) and not util.binary(data):
188 197 ctx = self.repo.filectx(path, fileid=node).changectx()
189 198 return self.substitute(data, path, ctx, self.re_kw.sub)
190 199 return data
191 200
192 201 def iskwfile(self, cand, ctx):
193 202 '''Returns subset of candidates which are configured for keyword
194 203 expansion are not symbolic links.'''
195 204 return [f for f in cand if self.match(f) and not 'l' in ctx.flags(f)]
196 205
197 206 def overwrite(self, ctx, candidates, lookup, expand, rekw=False):
198 207 '''Overwrites selected files expanding/shrinking keywords.'''
199 208 if self.restrict or lookup: # exclude kw_copy
200 209 candidates = self.iskwfile(candidates, ctx)
201 210 if not candidates:
202 211 return
203 212 commit = self.restrict and not lookup
204 213 if self.restrict or expand and lookup:
205 214 mf = ctx.manifest()
206 215 fctx = ctx
207 216 subn = (self.restrict or rekw) and self.re_kw.subn or self.re_kwexp.subn
208 217 msg = (expand and _('overwriting %s expanding keywords\n')
209 218 or _('overwriting %s shrinking keywords\n'))
210 219 for f in candidates:
211 220 if self.restrict:
212 221 data = self.repo.file(f).read(mf[f])
213 222 else:
214 223 data = self.repo.wread(f)
215 224 if util.binary(data):
216 225 continue
217 226 if expand:
218 227 if lookup:
219 228 fctx = self.repo.filectx(f, fileid=mf[f]).changectx()
220 229 data, found = self.substitute(data, f, fctx, subn)
221 230 elif self.restrict:
222 231 found = self.re_kw.search(data)
223 232 else:
224 233 data, found = _shrinktext(data, subn)
225 234 if found:
226 235 self.ui.note(msg % f)
227 236 self.repo.wwrite(f, data, ctx.flags(f))
228 237 if commit:
229 238 self.repo.dirstate.normal(f)
230 239 elif self.record:
231 240 self.repo.dirstate.normallookup(f)
232 241
233 242 def shrink(self, fname, text):
234 243 '''Returns text with all keyword substitutions removed.'''
235 244 if self.match(fname) and not util.binary(text):
236 245 return _shrinktext(text, self.re_kwexp.sub)
237 246 return text
238 247
239 248 def shrinklines(self, fname, lines):
240 249 '''Returns lines with keyword substitutions removed.'''
241 250 if self.match(fname):
242 251 text = ''.join(lines)
243 252 if not util.binary(text):
244 253 return _shrinktext(text, self.re_kwexp.sub).splitlines(True)
245 254 return lines
246 255
247 256 def wread(self, fname, data):
248 257 '''If in restricted mode returns data read from wdir with
249 258 keyword substitutions removed.'''
250 259 return self.restrict and self.shrink(fname, data) or data
251 260
252 261 class kwfilelog(filelog.filelog):
253 262 '''
254 263 Subclass of filelog to hook into its read, add, cmp methods.
255 264 Keywords are "stored" unexpanded, and processed on reading.
256 265 '''
257 266 def __init__(self, opener, kwt, path):
258 267 super(kwfilelog, self).__init__(opener, path)
259 268 self.kwt = kwt
260 269 self.path = path
261 270
262 271 def read(self, node):
263 272 '''Expands keywords when reading filelog.'''
264 273 data = super(kwfilelog, self).read(node)
265 274 if self.renamed(node):
266 275 return data
267 276 return self.kwt.expand(self.path, node, data)
268 277
269 278 def add(self, text, meta, tr, link, p1=None, p2=None):
270 279 '''Removes keyword substitutions when adding to filelog.'''
271 280 text = self.kwt.shrink(self.path, text)
272 281 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
273 282
274 283 def cmp(self, node, text):
275 284 '''Removes keyword substitutions for comparison.'''
276 285 text = self.kwt.shrink(self.path, text)
277 286 return super(kwfilelog, self).cmp(node, text)
278 287
279 288 def _status(ui, repo, kwt, *pats, **opts):
280 289 '''Bails out if [keyword] configuration is not active.
281 290 Returns status of working directory.'''
282 291 if kwt:
283 292 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
284 293 unknown=opts.get('unknown') or opts.get('all'))
285 294 if ui.configitems('keyword'):
286 295 raise util.Abort(_('[keyword] patterns cannot match'))
287 296 raise util.Abort(_('no [keyword] patterns configured'))
288 297
289 298 def _kwfwrite(ui, repo, expand, *pats, **opts):
290 299 '''Selects files and passes them to kwtemplater.overwrite.'''
291 300 wctx = repo[None]
292 301 if len(wctx.parents()) > 1:
293 302 raise util.Abort(_('outstanding uncommitted merge'))
294 303 kwt = kwtools['templater']
295 304 wlock = repo.wlock()
296 305 try:
297 306 status = _status(ui, repo, kwt, *pats, **opts)
298 307 modified, added, removed, deleted, unknown, ignored, clean = status
299 308 if modified or added or removed or deleted:
300 309 raise util.Abort(_('outstanding uncommitted changes'))
301 310 kwt.overwrite(wctx, clean, True, expand)
302 311 finally:
303 312 wlock.release()
304 313
305 314 def demo(ui, repo, *args, **opts):
306 315 '''print [keywordmaps] configuration and an expansion example
307 316
308 317 Show current, custom, or default keyword template maps and their
309 318 expansions.
310 319
311 320 Extend the current configuration by specifying maps as arguments
312 321 and using -f/--rcfile to source an external hgrc file.
313 322
314 323 Use -d/--default to disable current configuration.
315 324
316 325 See :hg:`help templates` for information on templates and filters.
317 326 '''
318 327 def demoitems(section, items):
319 328 ui.write('[%s]\n' % section)
320 329 for k, v in sorted(items):
321 330 ui.write('%s = %s\n' % (k, v))
322 331
323 332 fn = 'demo.txt'
324 333 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
325 334 ui.note(_('creating temporary repository at %s\n') % tmpdir)
326 335 repo = localrepo.localrepository(ui, tmpdir, True)
327 336 ui.setconfig('keyword', fn, '')
328 337
329 338 uikwmaps = ui.configitems('keywordmaps')
330 339 if args or opts.get('rcfile'):
331 340 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
332 341 if uikwmaps:
333 342 ui.status(_('\textending current template maps\n'))
334 343 if opts.get('default') or not uikwmaps:
335 344 ui.status(_('\toverriding default template maps\n'))
336 345 if opts.get('rcfile'):
337 346 ui.readconfig(opts.get('rcfile'))
338 347 if args:
339 348 # simulate hgrc parsing
340 349 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
341 350 fp = repo.opener('hgrc', 'w')
342 351 fp.writelines(rcmaps)
343 352 fp.close()
344 353 ui.readconfig(repo.join('hgrc'))
345 354 kwmaps = dict(ui.configitems('keywordmaps'))
346 355 elif opts.get('default'):
347 356 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
348 357 kwmaps = _defaultkwmaps(ui)
349 358 if uikwmaps:
350 359 ui.status(_('\tdisabling current template maps\n'))
351 360 for k, v in kwmaps.iteritems():
352 361 ui.setconfig('keywordmaps', k, v)
353 362 else:
354 363 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
355 364 kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
356 365
357 366 uisetup(ui)
358 367 reposetup(ui, repo)
359 368 ui.write('[extensions]\nkeyword =\n')
360 369 demoitems('keyword', ui.configitems('keyword'))
361 370 demoitems('keywordmaps', kwmaps.iteritems())
362 371 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
363 372 repo.wopener(fn, 'w').write(keywords)
364 373 repo[None].add([fn])
365 374 ui.note(_('\nkeywords written to %s:\n') % fn)
366 375 ui.note(keywords)
367 376 repo.dirstate.setbranch('demobranch')
368 377 for name, cmd in ui.configitems('hooks'):
369 378 if name.split('.', 1)[0].find('commit') > -1:
370 379 repo.ui.setconfig('hooks', name, '')
371 380 msg = _('hg keyword configuration and expansion example')
372 381 ui.note("hg ci -m '%s'\n" % msg)
373 382 repo.commit(text=msg)
374 383 ui.status(_('\n\tkeywords expanded\n'))
375 384 ui.write(repo.wread(fn))
376 385 shutil.rmtree(tmpdir, ignore_errors=True)
377 386
378 387 def expand(ui, repo, *pats, **opts):
379 388 '''expand keywords in the working directory
380 389
381 390 Run after (re)enabling keyword expansion.
382 391
383 392 kwexpand refuses to run if given files contain local changes.
384 393 '''
385 394 # 3rd argument sets expansion to True
386 395 _kwfwrite(ui, repo, True, *pats, **opts)
387 396
388 397 def files(ui, repo, *pats, **opts):
389 398 '''show files configured for keyword expansion
390 399
391 400 List which files in the working directory are matched by the
392 401 [keyword] configuration patterns.
393 402
394 403 Useful to prevent inadvertent keyword expansion and to speed up
395 404 execution by including only files that are actual candidates for
396 405 expansion.
397 406
398 407 See :hg:`help keyword` on how to construct patterns both for
399 408 inclusion and exclusion of files.
400 409
401 410 With -A/--all and -v/--verbose the codes used to show the status
402 411 of files are::
403 412
404 413 K = keyword expansion candidate
405 414 k = keyword expansion candidate (not tracked)
406 415 I = ignored
407 416 i = ignored (not tracked)
408 417 '''
409 418 kwt = kwtools['templater']
410 419 status = _status(ui, repo, kwt, *pats, **opts)
411 420 cwd = pats and repo.getcwd() or ''
412 421 modified, added, removed, deleted, unknown, ignored, clean = status
413 422 files = []
414 423 if not opts.get('unknown') or opts.get('all'):
415 424 files = sorted(modified + added + clean)
416 425 wctx = repo[None]
417 426 kwfiles = kwt.iskwfile(files, wctx)
418 427 kwunknown = kwt.iskwfile(unknown, wctx)
419 428 if not opts.get('ignore') or opts.get('all'):
420 429 showfiles = kwfiles, kwunknown
421 430 else:
422 431 showfiles = [], []
423 432 if opts.get('all') or opts.get('ignore'):
424 433 showfiles += ([f for f in files if f not in kwfiles],
425 434 [f for f in unknown if f not in kwunknown])
426 435 for char, filenames in zip('KkIi', showfiles):
427 436 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
428 437 for f in filenames:
429 438 ui.write(fmt % repo.pathto(f, cwd))
430 439
431 440 def shrink(ui, repo, *pats, **opts):
432 441 '''revert expanded keywords in the working directory
433 442
434 443 Run before changing/disabling active keywords or if you experience
435 444 problems with :hg:`import` or :hg:`merge`.
436 445
437 446 kwshrink refuses to run if given files contain local changes.
438 447 '''
439 448 # 3rd argument sets expansion to False
440 449 _kwfwrite(ui, repo, False, *pats, **opts)
441 450
442 451
443 452 def uisetup(ui):
444 453 ''' Monkeypatches dispatch._parse to retrieve user command.'''
445 454
446 455 def kwdispatch_parse(orig, ui, args):
447 456 '''Monkeypatch dispatch._parse to obtain running hg command.'''
448 457 cmd, func, args, options, cmdoptions = orig(ui, args)
449 458 kwtools['hgcmd'] = cmd
450 459 return cmd, func, args, options, cmdoptions
451 460
452 461 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
453 462
454 463 def reposetup(ui, repo):
455 464 '''Sets up repo as kwrepo for keyword substitution.
456 465 Overrides file method to return kwfilelog instead of filelog
457 466 if file matches user configuration.
458 467 Wraps commit to overwrite configured files with updated
459 468 keyword substitutions.
460 469 Monkeypatches patch and webcommands.'''
461 470
462 471 try:
463 472 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
464 473 or '.hg' in util.splitpath(repo.root)
465 474 or repo._url.startswith('bundle:')):
466 475 return
467 476 except AttributeError:
468 477 pass
469 478
470 479 inc, exc = [], ['.hg*']
471 480 for pat, opt in ui.configitems('keyword'):
472 481 if opt != 'ignore':
473 482 inc.append(pat)
474 483 else:
475 484 exc.append(pat)
476 485 if not inc:
477 486 return
478 487
479 488 kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
480 489
481 490 class kwrepo(repo.__class__):
482 491 def file(self, f):
483 492 if f[0] == '/':
484 493 f = f[1:]
485 494 return kwfilelog(self.sopener, kwt, f)
486 495
487 496 def wread(self, filename):
488 497 data = super(kwrepo, self).wread(filename)
489 498 return kwt.wread(filename, data)
490 499
491 500 def commit(self, *args, **opts):
492 501 # use custom commitctx for user commands
493 502 # other extensions can still wrap repo.commitctx directly
494 503 self.commitctx = self.kwcommitctx
495 504 try:
496 505 return super(kwrepo, self).commit(*args, **opts)
497 506 finally:
498 507 del self.commitctx
499 508
500 509 def kwcommitctx(self, ctx, error=False):
501 510 n = super(kwrepo, self).commitctx(ctx, error)
502 511 # no lock needed, only called from repo.commit() which already locks
503 512 if not kwt.record:
504 513 restrict = kwt.restrict
505 514 kwt.restrict = True
506 515 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
507 516 False, True)
508 517 kwt.restrict = restrict
509 518 return n
510 519
511 520 def rollback(self, dryrun=False):
512 wlock = repo.wlock()
521 wlock = self.wlock()
513 522 try:
514 523 if not dryrun:
515 524 changed = self['.'].files()
516 525 ret = super(kwrepo, self).rollback(dryrun)
517 526 if not dryrun:
518 527 ctx = self['.']
519 modified, added = self[None].status()[:2]
520 modified = [f for f in modified if f in changed]
521 added = [f for f in added if f in changed]
528 modified, added = _preselect(self[None].status(), changed)
529 kwt.overwrite(ctx, modified, True, True)
522 530 kwt.overwrite(ctx, added, True, False)
523 kwt.overwrite(ctx, modified, True, True)
524 531 return ret
525 532 finally:
526 533 wlock.release()
527 534
528 535 # monkeypatches
529 536 def kwpatchfile_init(orig, self, ui, fname, opener,
530 537 missing=False, eolmode=None):
531 538 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
532 539 rejects or conflicts due to expanded keywords in working dir.'''
533 540 orig(self, ui, fname, opener, missing, eolmode)
534 541 # shrink keywords read from working dir
535 542 self.lines = kwt.shrinklines(self.fname, self.lines)
536 543
537 544 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
538 545 opts=None, prefix=''):
539 546 '''Monkeypatch patch.diff to avoid expansion.'''
540 547 kwt.restrict = True
541 548 return orig(repo, node1, node2, match, changes, opts, prefix)
542 549
543 550 def kwweb_skip(orig, web, req, tmpl):
544 551 '''Wraps webcommands.x turning off keyword expansion.'''
545 552 kwt.match = util.never
546 553 return orig(web, req, tmpl)
547 554
548 555 def kw_copy(orig, ui, repo, pats, opts, rename=False):
549 556 '''Wraps cmdutil.copy so that copy/rename destinations do not
550 557 contain expanded keywords.
551 558 Note that the source may also be a symlink as:
552 559 hg cp sym x -> x is symlink
553 560 cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords)
554 561 '''
555 562 orig(ui, repo, pats, opts, rename)
556 563 if opts.get('dry_run'):
557 564 return
558 565 wctx = repo[None]
559 566 candidates = [f for f in repo.dirstate.copies() if
560 567 kwt.match(repo.dirstate.copied(f)) and
561 568 not 'l' in wctx.flags(f)]
562 569 kwt.overwrite(wctx, candidates, False, False)
563 570
564 571 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
565 572 '''Wraps record.dorecord expanding keywords after recording.'''
566 573 wlock = repo.wlock()
567 574 try:
568 575 # record returns 0 even when nothing has changed
569 576 # therefore compare nodes before and after
570 577 kwt.record = True
571 578 ctx = repo['.']
572 modified, added = repo[None].status()[:2]
579 wstatus = repo[None].status()
573 580 ret = orig(ui, repo, commitfunc, *pats, **opts)
574 581 recctx = repo['.']
575 582 if ctx != recctx:
576 changed = recctx.files()
577 modified = [f for f in modified if f in changed]
578 added = [f for f in added if f in changed]
583 modified, added = _preselect(wstatus, recctx.files())
579 584 kwt.restrict = False
580 585 kwt.overwrite(recctx, modified, False, True)
581 586 kwt.overwrite(recctx, added, False, True, True)
582 587 kwt.restrict = True
583 588 return ret
584 589 finally:
585 590 wlock.release()
586 591
587 592 repo.__class__ = kwrepo
588 593
589 594 def kwfilectx_cmp(orig, self, fctx):
590 595 # keyword affects data size, comparing wdir and filelog size does
591 596 # not make sense
592 597 return self._filelog.cmp(self._filenode, fctx.data())
593 598 extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp)
594 599
595 600 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
596 601 extensions.wrapfunction(patch, 'diff', kw_diff)
597 602 extensions.wrapfunction(cmdutil, 'copy', kw_copy)
598 603 for c in 'annotate changeset rev filediff diff'.split():
599 604 extensions.wrapfunction(webcommands, c, kwweb_skip)
600 605 for name in recordextensions.split():
601 606 try:
602 607 record = extensions.find(name)
603 608 extensions.wrapfunction(record, 'dorecord', kw_dorecord)
604 609 except KeyError:
605 610 pass
606 611
607 612 cmdtable = {
608 613 'kwdemo':
609 614 (demo,
610 615 [('d', 'default', None, _('show default keyword template maps')),
611 616 ('f', 'rcfile', '',
612 617 _('read maps from rcfile'), _('FILE'))],
613 618 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
614 619 'kwexpand': (expand, commands.walkopts,
615 620 _('hg kwexpand [OPTION]... [FILE]...')),
616 621 'kwfiles':
617 622 (files,
618 623 [('A', 'all', None, _('show keyword status flags of all files')),
619 624 ('i', 'ignore', None, _('show files excluded from expansion')),
620 625 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
621 626 ] + commands.walkopts,
622 627 _('hg kwfiles [OPTION]... [FILE]...')),
623 628 'kwshrink': (shrink, commands.walkopts,
624 629 _('hg kwshrink [OPTION]... [FILE]...')),
625 630 }
General Comments 0
You need to be logged in to leave comments. Login now