##// END OF EJS Templates
keyword: improve English
timeless -
r8763:fccdf5ca default
parent child Browse files
Show More
@@ -1,534 +1,534 b''
1 1 # keyword.py - $Keyword$ expansion for Mercurial
2 2 #
3 3 # Copyright 2007, 2008 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, incorporated herein by reference.
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://www.selenic.com/mercurial/wiki/index.cgi/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 # Setup in hgrc:
25 25 #
26 26 # [extensions]
27 27 # # enable extension
28 28 # hgext.keyword =
29 29 #
30 30 # Files to act upon/ignore are specified in the [keyword] section.
31 31 # Customized keyword template mappings in the [keywordmaps] section.
32 32 #
33 33 # Run "hg help keyword" and "hg kwdemo" to get info on configuration.
34 34
35 35 '''keyword expansion in local repositories
36 36
37 37 This extension expands RCS/CVS-like or self-customized $Keywords$ in
38 38 tracked text files selected by your configuration.
39 39
40 40 Keywords are only expanded in local repositories and not stored in the
41 41 change history. The mechanism can be regarded as a convenience for the
42 42 current user or for archive distribution.
43 43
44 44 Configuration is done in the [keyword] and [keywordmaps] sections of
45 45 hgrc files.
46 46
47 47 Example:
48 48
49 49 [keyword]
50 50 # expand keywords in every python file except those matching "x*"
51 51 **.py =
52 52 x* = ignore
53 53
54 54 Note: the more specific you are in your filename patterns
55 55 the less you lose speed in huge repositories.
56 56
57 57 For [keywordmaps] template mapping and expansion demonstration and
58 58 control run "hg kwdemo".
59 59
60 60 An additional date template filter {date|utcdate} is provided.
61 61
62 62 The default template mappings (view with "hg kwdemo -d") can be
63 63 replaced with customized keywords and templates. Again, run "hg
64 64 kwdemo" to control the results of your config changes.
65 65
66 66 Before changing/disabling active keywords, run "hg kwshrink" to avoid
67 67 the risk of inadvertently storing expanded keywords in the change
68 68 history.
69 69
70 70 To force expansion after enabling it, or a configuration change, run
71 71 "hg kwexpand".
72 72
73 73 Also, when committing with the record extension or using mq's qrecord,
74 74 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
75 75 the files in question to update keyword expansions after all changes
76 76 have been checked in.
77 77
78 78 Expansions spanning more than one line and incremental expansions,
79 79 like CVS' $Log$, are not supported. A keyword template map
80 80 "Log = {desc}" expands to the first line of the changeset description.
81 81 '''
82 82
83 83 from mercurial import commands, cmdutil, dispatch, filelog, revlog, extensions
84 84 from mercurial import patch, localrepo, templater, templatefilters, util, match
85 85 from mercurial.hgweb import webcommands
86 86 from mercurial.lock import release
87 87 from mercurial.node import nullid, hex
88 88 from mercurial.i18n import _
89 89 import re, shutil, tempfile, time
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 copy export grep incoming init'
95 95 ' log outgoing push rename rollback tip verify'
96 96 ' convert email glog')
97 97
98 98 # hg commands that trigger expansion only when writing to working dir,
99 99 # not when reading filelog, and unexpand when reading from working dir
100 100 restricted = 'merge record resolve qfold qimport qnew qpush qrefresh qrecord'
101 101
102 102 def utcdate(date):
103 103 '''Returns hgdate in cvs-like UTC format.'''
104 104 return time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date[0]))
105 105
106 106 # make keyword tools accessible
107 107 kwtools = {'templater': None, 'hgcmd': '', 'inc': [], 'exc': ['.hg*']}
108 108
109 109
110 110 class kwtemplater(object):
111 111 '''
112 112 Sets up keyword templates, corresponding keyword regex, and
113 113 provides keyword substitution functions.
114 114 '''
115 115 templates = {
116 116 'Revision': '{node|short}',
117 117 'Author': '{author|user}',
118 118 'Date': '{date|utcdate}',
119 119 'RCSFile': '{file|basename},v',
120 120 'Source': '{root}/{file},v',
121 121 'Id': '{file|basename},v {node|short} {date|utcdate} {author|user}',
122 122 'Header': '{root}/{file},v {node|short} {date|utcdate} {author|user}',
123 123 }
124 124
125 125 def __init__(self, ui, repo):
126 126 self.ui = ui
127 127 self.repo = repo
128 128 self.match = match.match(repo.root, '', [],
129 129 kwtools['inc'], kwtools['exc'])
130 130 self.restrict = kwtools['hgcmd'] in restricted.split()
131 131
132 132 kwmaps = self.ui.configitems('keywordmaps')
133 133 if kwmaps: # override default templates
134 134 kwmaps = [(k, templater.parsestring(v, False))
135 135 for (k, v) in kwmaps]
136 136 self.templates = dict(kwmaps)
137 137 escaped = map(re.escape, self.templates.keys())
138 138 kwpat = r'\$(%s)(: [^$\n\r]*? )??\$' % '|'.join(escaped)
139 139 self.re_kw = re.compile(kwpat)
140 140
141 141 templatefilters.filters['utcdate'] = utcdate
142 142 self.ct = cmdutil.changeset_templater(self.ui, self.repo,
143 143 False, None, '', False)
144 144
145 145 def substitute(self, data, path, ctx, subfunc):
146 146 '''Replaces keywords in data with expanded template.'''
147 147 def kwsub(mobj):
148 148 kw = mobj.group(1)
149 149 self.ct.use_template(self.templates[kw])
150 150 self.ui.pushbuffer()
151 151 self.ct.show(ctx, root=self.repo.root, file=path)
152 152 ekw = templatefilters.firstline(self.ui.popbuffer())
153 153 return '$%s: %s $' % (kw, ekw)
154 154 return subfunc(kwsub, data)
155 155
156 156 def expand(self, path, node, data):
157 157 '''Returns data with keywords expanded.'''
158 158 if not self.restrict and self.match(path) and not util.binary(data):
159 159 ctx = self.repo.filectx(path, fileid=node).changectx()
160 160 return self.substitute(data, path, ctx, self.re_kw.sub)
161 161 return data
162 162
163 163 def iskwfile(self, path, flagfunc):
164 164 '''Returns true if path matches [keyword] pattern
165 165 and is not a symbolic link.
166 166 Caveat: localrepository._link fails on Windows.'''
167 167 return self.match(path) and not 'l' in flagfunc(path)
168 168
169 169 def overwrite(self, node, expand, files):
170 170 '''Overwrites selected files expanding/shrinking keywords.'''
171 171 ctx = self.repo[node]
172 172 mf = ctx.manifest()
173 173 if node is not None: # commit
174 174 files = [f for f in ctx.files() if f in mf]
175 175 notify = self.ui.debug
176 176 else: # kwexpand/kwshrink
177 177 notify = self.ui.note
178 178 candidates = [f for f in files if self.iskwfile(f, ctx.flags)]
179 179 if candidates:
180 180 self.restrict = True # do not expand when reading
181 181 msg = (expand and _('overwriting %s expanding keywords\n')
182 182 or _('overwriting %s shrinking keywords\n'))
183 183 for f in candidates:
184 184 fp = self.repo.file(f)
185 185 data = fp.read(mf[f])
186 186 if util.binary(data):
187 187 continue
188 188 if expand:
189 189 if node is None:
190 190 ctx = self.repo.filectx(f, fileid=mf[f]).changectx()
191 191 data, found = self.substitute(data, f, ctx,
192 192 self.re_kw.subn)
193 193 else:
194 194 found = self.re_kw.search(data)
195 195 if found:
196 196 notify(msg % f)
197 197 self.repo.wwrite(f, data, mf.flags(f))
198 198 self.repo.dirstate.normal(f)
199 199 self.restrict = False
200 200
201 201 def shrinktext(self, text):
202 202 '''Unconditionally removes all keyword substitutions from text.'''
203 203 return self.re_kw.sub(r'$\1$', text)
204 204
205 205 def shrink(self, fname, text):
206 206 '''Returns text with all keyword substitutions removed.'''
207 207 if self.match(fname) and not util.binary(text):
208 208 return self.shrinktext(text)
209 209 return text
210 210
211 211 def shrinklines(self, fname, lines):
212 212 '''Returns lines with keyword substitutions removed.'''
213 213 if self.match(fname):
214 214 text = ''.join(lines)
215 215 if not util.binary(text):
216 216 return self.shrinktext(text).splitlines(True)
217 217 return lines
218 218
219 219 def wread(self, fname, data):
220 220 '''If in restricted mode returns data read from wdir with
221 221 keyword substitutions removed.'''
222 222 return self.restrict and self.shrink(fname, data) or data
223 223
224 224 class kwfilelog(filelog.filelog):
225 225 '''
226 226 Subclass of filelog to hook into its read, add, cmp methods.
227 227 Keywords are "stored" unexpanded, and processed on reading.
228 228 '''
229 229 def __init__(self, opener, kwt, path):
230 230 super(kwfilelog, self).__init__(opener, path)
231 231 self.kwt = kwt
232 232 self.path = path
233 233
234 234 def read(self, node):
235 235 '''Expands keywords when reading filelog.'''
236 236 data = super(kwfilelog, self).read(node)
237 237 return self.kwt.expand(self.path, node, data)
238 238
239 239 def add(self, text, meta, tr, link, p1=None, p2=None):
240 240 '''Removes keyword substitutions when adding to filelog.'''
241 241 text = self.kwt.shrink(self.path, text)
242 242 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
243 243
244 244 def cmp(self, node, text):
245 245 '''Removes keyword substitutions for comparison.'''
246 246 text = self.kwt.shrink(self.path, text)
247 247 if self.renamed(node):
248 248 t2 = super(kwfilelog, self).read(node)
249 249 return t2 != text
250 250 return revlog.revlog.cmp(self, node, text)
251 251
252 252 def _status(ui, repo, kwt, unknown, *pats, **opts):
253 253 '''Bails out if [keyword] configuration is not active.
254 254 Returns status of working directory.'''
255 255 if kwt:
256 256 match = cmdutil.match(repo, pats, opts)
257 257 return repo.status(match=match, unknown=unknown, clean=True)
258 258 if ui.configitems('keyword'):
259 259 raise util.Abort(_('[keyword] patterns cannot match'))
260 260 raise util.Abort(_('no [keyword] patterns configured'))
261 261
262 262 def _kwfwrite(ui, repo, expand, *pats, **opts):
263 263 '''Selects files and passes them to kwtemplater.overwrite.'''
264 264 if repo.dirstate.parents()[1] != nullid:
265 265 raise util.Abort(_('outstanding uncommitted merge'))
266 266 kwt = kwtools['templater']
267 267 status = _status(ui, repo, kwt, False, *pats, **opts)
268 268 modified, added, removed, deleted = status[:4]
269 269 if modified or added or removed or deleted:
270 270 raise util.Abort(_('outstanding uncommitted changes'))
271 271 wlock = lock = None
272 272 try:
273 273 wlock = repo.wlock()
274 274 lock = repo.lock()
275 275 kwt.overwrite(None, expand, status[6])
276 276 finally:
277 277 release(lock, wlock)
278 278
279 279 def demo(ui, repo, *args, **opts):
280 280 '''print [keywordmaps] configuration and an expansion example
281 281
282 282 Show current, custom, or default keyword template maps and their
283 expansion.
283 expansions.
284 284
285 285 Extend current configuration by specifying maps as arguments and
286 286 optionally by reading from an additional hgrc file.
287 287
288 288 Override current keyword template maps with "default" option.
289 289 '''
290 290 def demostatus(stat):
291 291 ui.status(_('\n\t%s\n') % stat)
292 292
293 293 def demoitems(section, items):
294 294 ui.write('[%s]\n' % section)
295 295 for k, v in items:
296 296 ui.write('%s = %s\n' % (k, v))
297 297
298 298 msg = 'hg keyword config and expansion example'
299 299 kwstatus = 'current'
300 300 fn = 'demo.txt'
301 301 branchname = 'demobranch'
302 302 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
303 303 ui.note(_('creating temporary repository at %s\n') % tmpdir)
304 304 repo = localrepo.localrepository(ui, tmpdir, True)
305 305 ui.setconfig('keyword', fn, '')
306 306 if args or opts.get('rcfile'):
307 307 kwstatus = 'custom'
308 308 if opts.get('rcfile'):
309 309 ui.readconfig(opts.get('rcfile'))
310 310 if opts.get('default'):
311 311 kwstatus = 'default'
312 312 kwmaps = kwtemplater.templates
313 313 if ui.configitems('keywordmaps'):
314 314 # override maps from optional rcfile
315 315 for k, v in kwmaps.iteritems():
316 316 ui.setconfig('keywordmaps', k, v)
317 317 elif args:
318 318 # simulate hgrc parsing
319 319 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
320 320 fp = repo.opener('hgrc', 'w')
321 321 fp.writelines(rcmaps)
322 322 fp.close()
323 323 ui.readconfig(repo.join('hgrc'))
324 324 if not opts.get('default'):
325 325 kwmaps = dict(ui.configitems('keywordmaps')) or kwtemplater.templates
326 326 uisetup(ui)
327 327 reposetup(ui, repo)
328 328 for k, v in ui.configitems('extensions'):
329 329 if k.endswith('keyword'):
330 330 extension = '%s = %s' % (k, v)
331 331 break
332 332 demostatus('config using %s keyword template maps' % kwstatus)
333 333 ui.write('[extensions]\n%s\n' % extension)
334 334 demoitems('keyword', ui.configitems('keyword'))
335 335 demoitems('keywordmaps', kwmaps.iteritems())
336 336 keywords = '$' + '$\n$'.join(kwmaps.keys()) + '$\n'
337 337 repo.wopener(fn, 'w').write(keywords)
338 338 repo.add([fn])
339 339 path = repo.wjoin(fn)
340 340 ui.note(_('\n%s keywords written to %s:\n') % (kwstatus, path))
341 341 ui.note(keywords)
342 342 ui.note('\nhg -R "%s" branch "%s"\n' % (tmpdir, branchname))
343 343 # silence branch command if not verbose
344 344 quiet = ui.quiet
345 345 ui.quiet = not ui.verbose
346 346 commands.branch(ui, repo, branchname)
347 347 ui.quiet = quiet
348 348 for name, cmd in ui.configitems('hooks'):
349 349 if name.split('.', 1)[0].find('commit') > -1:
350 350 repo.ui.setconfig('hooks', name, '')
351 351 ui.note(_('unhooked all commit hooks\n'))
352 352 ui.note('hg -R "%s" ci -m "%s"\n' % (tmpdir, msg))
353 353 repo.commit(text=msg)
354 354 fmt = ui.verbose and ' in %s' % path or ''
355 355 demostatus('%s keywords expanded%s' % (kwstatus, fmt))
356 356 ui.write(repo.wread(fn))
357 357 ui.debug(_('\nremoving temporary repository %s\n') % tmpdir)
358 358 shutil.rmtree(tmpdir, ignore_errors=True)
359 359
360 360 def expand(ui, repo, *pats, **opts):
361 '''expand keywords in working directory
361 '''expand keywords in the working directory
362 362
363 363 Run after (re)enabling keyword expansion.
364 364
365 365 kwexpand refuses to run if given files contain local changes.
366 366 '''
367 367 # 3rd argument sets expansion to True
368 368 _kwfwrite(ui, repo, True, *pats, **opts)
369 369
370 370 def files(ui, repo, *pats, **opts):
371 371 '''print files currently configured for keyword expansion
372 372
373 373 Crosscheck which files in working directory are potential targets
374 374 for keyword expansion. That is, files matched by [keyword] config
375 375 patterns but not symlinks.
376 376 '''
377 377 kwt = kwtools['templater']
378 378 status = _status(ui, repo, kwt, opts.get('untracked'), *pats, **opts)
379 379 modified, added, removed, deleted, unknown, ignored, clean = status
380 380 files = sorted(modified + added + clean + unknown)
381 381 wctx = repo[None]
382 382 kwfiles = [f for f in files if kwt.iskwfile(f, wctx.flags)]
383 383 cwd = pats and repo.getcwd() or ''
384 384 kwfstats = not opts.get('ignore') and (('K', kwfiles),) or ()
385 385 if opts.get('all') or opts.get('ignore'):
386 386 kwfstats += (('I', [f for f in files if f not in kwfiles]),)
387 387 for char, filenames in kwfstats:
388 388 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
389 389 for f in filenames:
390 390 ui.write(fmt % repo.pathto(f, cwd))
391 391
392 392 def shrink(ui, repo, *pats, **opts):
393 '''revert expanded keywords in working directory
393 '''revert expanded keywords in the working directory
394 394
395 395 Run before changing/disabling active keywords or if you experience
396 396 problems with "hg import" or "hg merge".
397 397
398 398 kwshrink refuses to run if given files contain local changes.
399 399 '''
400 400 # 3rd argument sets expansion to False
401 401 _kwfwrite(ui, repo, False, *pats, **opts)
402 402
403 403
404 404 def uisetup(ui):
405 405 '''Collects [keyword] config in kwtools.
406 406 Monkeypatches dispatch._parse if needed.'''
407 407
408 408 for pat, opt in ui.configitems('keyword'):
409 409 if opt != 'ignore':
410 410 kwtools['inc'].append(pat)
411 411 else:
412 412 kwtools['exc'].append(pat)
413 413
414 414 if kwtools['inc']:
415 415 def kwdispatch_parse(orig, ui, args):
416 416 '''Monkeypatch dispatch._parse to obtain running hg command.'''
417 417 cmd, func, args, options, cmdoptions = orig(ui, args)
418 418 kwtools['hgcmd'] = cmd
419 419 return cmd, func, args, options, cmdoptions
420 420
421 421 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
422 422
423 423 def reposetup(ui, repo):
424 424 '''Sets up repo as kwrepo for keyword substitution.
425 425 Overrides file method to return kwfilelog instead of filelog
426 426 if file matches user configuration.
427 427 Wraps commit to overwrite configured files with updated
428 428 keyword substitutions.
429 429 Monkeypatches patch and webcommands.'''
430 430
431 431 try:
432 432 if (not repo.local() or not kwtools['inc']
433 433 or kwtools['hgcmd'] in nokwcommands.split()
434 434 or '.hg' in util.splitpath(repo.root)
435 435 or repo._url.startswith('bundle:')):
436 436 return
437 437 except AttributeError:
438 438 pass
439 439
440 440 kwtools['templater'] = kwt = kwtemplater(ui, repo)
441 441
442 442 class kwrepo(repo.__class__):
443 443 def file(self, f):
444 444 if f[0] == '/':
445 445 f = f[1:]
446 446 return kwfilelog(self.sopener, kwt, f)
447 447
448 448 def wread(self, filename):
449 449 data = super(kwrepo, self).wread(filename)
450 450 return kwt.wread(filename, data)
451 451
452 452 def commit(self, text='', user=None, date=None, match=None,
453 453 force=False, editor=None, extra={}):
454 454 wlock = lock = None
455 455 _p1 = _p2 = None
456 456 try:
457 457 wlock = self.wlock()
458 458 lock = self.lock()
459 459 # store and postpone commit hooks
460 460 commithooks = {}
461 461 for name, cmd in ui.configitems('hooks'):
462 462 if name.split('.', 1)[0] == 'commit':
463 463 commithooks[name] = cmd
464 464 ui.setconfig('hooks', name, None)
465 465 if commithooks:
466 466 # store parents for commit hook environment
467 467 _p1, _p2 = repo.dirstate.parents()
468 468 _p1 = hex(_p1)
469 469 if _p2 == nullid:
470 470 _p2 = ''
471 471 else:
472 472 _p2 = hex(_p2)
473 473
474 474 n = super(kwrepo, self).commit(text, user, date, match, force,
475 475 editor, extra)
476 476
477 477 # restore commit hooks
478 478 for name, cmd in commithooks.iteritems():
479 479 ui.setconfig('hooks', name, cmd)
480 480 if n is not None:
481 481 kwt.overwrite(n, True, None)
482 482 repo.hook('commit', node=n, parent1=_p1, parent2=_p2)
483 483 return n
484 484 finally:
485 485 release(lock, wlock)
486 486
487 487 # monkeypatches
488 488 def kwpatchfile_init(orig, self, ui, fname, opener, missing=False):
489 489 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
490 490 rejects or conflicts due to expanded keywords in working dir.'''
491 491 orig(self, ui, fname, opener, missing)
492 492 # shrink keywords read from working dir
493 493 self.lines = kwt.shrinklines(self.fname, self.lines)
494 494
495 495 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
496 496 opts=None):
497 497 '''Monkeypatch patch.diff to avoid expansion except when
498 498 comparing against working dir.'''
499 499 if node2 is not None:
500 500 kwt.match = util.never
501 501 elif node1 is not None and node1 != repo['.'].node():
502 502 kwt.restrict = True
503 503 return orig(repo, node1, node2, match, changes, opts)
504 504
505 505 def kwweb_skip(orig, web, req, tmpl):
506 506 '''Wraps webcommands.x turning off keyword expansion.'''
507 507 kwt.match = util.never
508 508 return orig(web, req, tmpl)
509 509
510 510 repo.__class__ = kwrepo
511 511
512 512 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
513 513 extensions.wrapfunction(patch, 'diff', kw_diff)
514 514 for c in 'annotate changeset rev filediff diff'.split():
515 515 extensions.wrapfunction(webcommands, c, kwweb_skip)
516 516
517 517 cmdtable = {
518 518 'kwdemo':
519 519 (demo,
520 520 [('d', 'default', None, _('show default keyword template maps')),
521 521 ('f', 'rcfile', [], _('read maps from rcfile'))],
522 522 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
523 523 'kwexpand': (expand, commands.walkopts,
524 524 _('hg kwexpand [OPTION]... [FILE]...')),
525 525 'kwfiles':
526 526 (files,
527 527 [('a', 'all', None, _('show keyword status flags of all files')),
528 528 ('i', 'ignore', None, _('show files excluded from expansion')),
529 529 ('u', 'untracked', None, _('additionally show untracked files')),
530 530 ] + commands.walkopts,
531 531 _('hg kwfiles [OPTION]... [FILE]...')),
532 532 'kwshrink': (shrink, commands.walkopts,
533 533 _('hg kwshrink [OPTION]... [FILE]...')),
534 534 }
@@ -1,502 +1,502 b''
1 1 % help
2 2 keyword extension - keyword expansion in local repositories
3 3
4 4 This extension expands RCS/CVS-like or self-customized $Keywords$ in
5 5 tracked text files selected by your configuration.
6 6
7 7 Keywords are only expanded in local repositories and not stored in the
8 8 change history. The mechanism can be regarded as a convenience for the
9 9 current user or for archive distribution.
10 10
11 11 Configuration is done in the [keyword] and [keywordmaps] sections of
12 12 hgrc files.
13 13
14 14 Example:
15 15
16 16 [keyword]
17 17 # expand keywords in every python file except those matching "x*"
18 18 **.py =
19 19 x* = ignore
20 20
21 21 Note: the more specific you are in your filename patterns
22 22 the less you lose speed in huge repositories.
23 23
24 24 For [keywordmaps] template mapping and expansion demonstration and
25 25 control run "hg kwdemo".
26 26
27 27 An additional date template filter {date|utcdate} is provided.
28 28
29 29 The default template mappings (view with "hg kwdemo -d") can be
30 30 replaced with customized keywords and templates. Again, run "hg
31 31 kwdemo" to control the results of your config changes.
32 32
33 33 Before changing/disabling active keywords, run "hg kwshrink" to avoid
34 34 the risk of inadvertently storing expanded keywords in the change
35 35 history.
36 36
37 37 To force expansion after enabling it, or a configuration change, run
38 38 "hg kwexpand".
39 39
40 40 Also, when committing with the record extension or using mq's qrecord,
41 41 be aware that keywords cannot be updated. Again, run "hg kwexpand" on
42 42 the files in question to update keyword expansions after all changes
43 43 have been checked in.
44 44
45 45 Expansions spanning more than one line and incremental expansions,
46 46 like CVS' $Log$, are not supported. A keyword template map
47 47 "Log = {desc}" expands to the first line of the changeset description.
48 48
49 49 list of commands:
50 50
51 51 kwdemo print [keywordmaps] configuration and an expansion example
52 kwexpand expand keywords in working directory
52 kwexpand expand keywords in the working directory
53 53 kwfiles print files currently configured for keyword expansion
54 kwshrink revert expanded keywords in working directory
54 kwshrink revert expanded keywords in the working directory
55 55
56 56 enabled extensions:
57 57
58 58 keyword keyword expansion in local repositories
59 59 mq patch management and development
60 60 notify hook extension to email notifications on commits/pushes
61 61
62 62 use "hg -v help keyword" to show aliases and global options
63 63 % hg kwdemo
64 64 [extensions]
65 65 hgext.keyword =
66 66 [keyword]
67 67 * =
68 68 b = ignore
69 69 demo.txt =
70 70 [keywordmaps]
71 71 RCSFile = {file|basename},v
72 72 Author = {author|user}
73 73 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
74 74 Source = {root}/{file},v
75 75 Date = {date|utcdate}
76 76 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
77 77 Revision = {node|short}
78 78 $RCSFile: demo.txt,v $
79 79 $Author: test $
80 80 $Header: /TMP/demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
81 81 $Source: /TMP/demo.txt,v $
82 82 $Date: 2000/00/00 00:00:00 $
83 83 $Id: demo.txt,v xxxxxxxxxxxx 2000/00/00 00:00:00 test $
84 84 $Revision: xxxxxxxxxxxx $
85 85 [extensions]
86 86 hgext.keyword =
87 87 [keyword]
88 88 * =
89 89 b = ignore
90 90 demo.txt =
91 91 [keywordmaps]
92 92 Branch = {branches}
93 93 $Branch: demobranch $
94 94 % kwshrink should exit silently in empty/invalid repo
95 95 pulling from test-keyword.hg
96 96 requesting all changes
97 97 adding changesets
98 98 adding manifests
99 99 adding file changes
100 100 added 1 changesets with 1 changes to 1 files
101 101 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
102 102 % cat
103 103 expand $Id$
104 104 do not process $Id:
105 105 xxx $
106 106 ignore $Id$
107 107 % addremove
108 108 adding a
109 109 adding b
110 110 % status
111 111 A a
112 112 A b
113 113 % default keyword expansion including commit hook
114 114 % interrupted commit should not change state or run commit hook
115 115 abort: empty commit message
116 116 % status
117 117 A a
118 118 A b
119 119 % commit
120 120 a
121 121 b
122 122 overwriting a expanding keywords
123 123 running hook commit.test: cp a hooktest
124 124 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
125 125 % status
126 126 ? hooktest
127 127 % identify
128 128 ef63ca68695b
129 129 % cat
130 130 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
131 131 do not process $Id:
132 132 xxx $
133 133 ignore $Id$
134 134 % hg cat
135 135 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
136 136 do not process $Id:
137 137 xxx $
138 138 ignore $Id$
139 139 a
140 140 % diff a hooktest
141 141 % removing commit hook from config
142 142 % bundle
143 143 2 changesets found
144 144 % notify on pull to check whether keywords stay as is in email
145 145 % ie. if patch.diff wrapper acts as it should
146 146 % pull from bundle
147 147 pulling from ../kw.hg
148 148 requesting all changes
149 149 adding changesets
150 150 adding manifests
151 151 adding file changes
152 152 added 2 changesets with 3 changes to 3 files
153 153
154 154 diff -r 000000000000 -r a2392c293916 sym
155 155 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
156 156 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
157 157 @@ -0,0 +1,1 @@
158 158 +a
159 159 \ No newline at end of file
160 160
161 161 diff -r a2392c293916 -r ef63ca68695b a
162 162 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
163 163 +++ b/a Thu Jan 01 00:00:00 1970 +0000
164 164 @@ -0,0 +1,3 @@
165 165 +expand $Id$
166 166 +do not process $Id:
167 167 +xxx $
168 168 diff -r a2392c293916 -r ef63ca68695b b
169 169 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
170 170 +++ b/b Thu Jan 01 00:00:00 1970 +0000
171 171 @@ -0,0 +1,1 @@
172 172 +ignore $Id$
173 173 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
174 174 % remove notify config
175 175 % touch
176 176 % status
177 177 % update
178 178 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
179 179 % cat
180 180 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
181 181 do not process $Id:
182 182 xxx $
183 183 ignore $Id$
184 184 % check whether expansion is filewise
185 185 % commit c
186 186 adding c
187 187 % force expansion
188 188 overwriting a expanding keywords
189 189 overwriting c expanding keywords
190 190 % compare changenodes in a c
191 191 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
192 192 do not process $Id:
193 193 xxx $
194 194 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
195 195 tests for different changenodes
196 196 % qinit -c
197 197 % qimport
198 198 % qcommit
199 199 % keywords should not be expanded in patch
200 200 # HG changeset patch
201 201 # User User Name <user@example.com>
202 202 # Date 1 0
203 203 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
204 204 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
205 205 cndiff
206 206
207 207 diff -r ef63ca68695b -r 40a904bbbe4c c
208 208 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
209 209 +++ b/c Thu Jan 01 00:00:01 1970 +0000
210 210 @@ -0,0 +1,2 @@
211 211 +$Id$
212 212 +tests for different changenodes
213 213 % qpop
214 214 patch queue now empty
215 215 % qgoto - should imply qpush
216 216 applying mqtest.diff
217 217 now at: mqtest.diff
218 218 % cat
219 219 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
220 220 tests for different changenodes
221 221 % qpop and move on
222 222 patch queue now empty
223 223 % copy
224 224 % kwfiles added
225 225 a
226 226 c
227 227 % commit
228 228 c
229 229 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
230 230 overwriting c expanding keywords
231 231 committed changeset 2:e22d299ac0c2bd8897b3df5114374b9e4d4ca62f
232 232 % cat a c
233 233 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
234 234 do not process $Id:
235 235 xxx $
236 236 expand $Id: c,v e22d299ac0c2 1970/01/01 00:00:01 user $
237 237 do not process $Id:
238 238 xxx $
239 239 % touch copied c
240 240 % status
241 241 % kwfiles
242 242 a
243 243 c
244 244 % diff --rev
245 245 diff -r ef63ca68695b c
246 246 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
247 247 @@ -0,0 +1,3 @@
248 248 +expand $Id$
249 249 +do not process $Id:
250 250 +xxx $
251 251 % rollback
252 252 rolling back last transaction
253 253 % status
254 254 A c
255 255 % update -C
256 256 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
257 257 % custom keyword expansion
258 258 % try with kwdemo
259 259 [extensions]
260 260 hgext.keyword =
261 261 [keyword]
262 262 * =
263 263 b = ignore
264 264 demo.txt =
265 265 [keywordmaps]
266 266 Xinfo = {author}: {desc}
267 267 $Xinfo: test: hg keyword config and expansion example $
268 268 % cat
269 269 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
270 270 do not process $Id:
271 271 xxx $
272 272 ignore $Id$
273 273 % hg cat
274 274 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
275 275 do not process $Id:
276 276 xxx $
277 277 ignore $Id$
278 278 a
279 279 % interrupted commit should not change state
280 280 abort: empty commit message
281 281 % status
282 282 M a
283 283 ? c
284 284 ? log
285 285 % commit
286 286 a
287 287 overwriting a expanding keywords
288 288 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
289 289 % status
290 290 ? c
291 291 % verify
292 292 checking changesets
293 293 checking manifests
294 294 crosschecking files in changesets and manifests
295 295 checking files
296 296 3 files, 3 changesets, 4 total revisions
297 297 % cat
298 298 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
299 299 do not process $Id:
300 300 xxx $
301 301 $Xinfo: User Name <user@example.com>: firstline $
302 302 ignore $Id$
303 303 % hg cat
304 304 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
305 305 do not process $Id:
306 306 xxx $
307 307 $Xinfo: User Name <user@example.com>: firstline $
308 308 ignore $Id$
309 309 a
310 310 % annotate
311 311 1: expand $Id$
312 312 1: do not process $Id:
313 313 1: xxx $
314 314 2: $Xinfo$
315 315 % remove
316 316 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
317 317 % status
318 318 ? c
319 319 % rollback
320 320 rolling back last transaction
321 321 % status
322 322 R a
323 323 ? c
324 324 % revert a
325 325 % cat a
326 326 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
327 327 do not process $Id:
328 328 xxx $
329 329 $Xinfo: User Name <user@example.com>: firstline $
330 330 % clone to test incoming
331 331 requesting all changes
332 332 adding changesets
333 333 adding manifests
334 334 adding file changes
335 335 added 2 changesets with 3 changes to 3 files
336 336 updating working directory
337 337 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
338 338 % incoming
339 339 comparing with test-keyword/Test
340 340 searching for changes
341 341 changeset: 2:bb948857c743
342 342 tag: tip
343 343 user: User Name <user@example.com>
344 344 date: Thu Jan 01 00:00:02 1970 +0000
345 345 summary: firstline
346 346
347 347 % commit rejecttest
348 348 a
349 349 overwriting a expanding keywords
350 350 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
351 351 % export
352 352 % import
353 353 applying ../rejecttest.diff
354 354 % cat
355 355 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
356 356 do not process $Id: rejecttest
357 357 xxx $
358 358 $Xinfo: User Name <user@example.com>: rejects? $
359 359 ignore $Id$
360 360
361 361 % rollback
362 362 rolling back last transaction
363 363 % clean update
364 364 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
365 365 % kwexpand/kwshrink on selected files
366 366 % copy a x/a
367 367 % kwexpand a
368 368 overwriting a expanding keywords
369 369 % kwexpand x/a should abort
370 370 abort: outstanding uncommitted changes
371 371 x/a
372 372 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
373 373 overwriting x/a expanding keywords
374 374 committed changeset 3:cfa68229c1167443337266ebac453c73b1d5d16e
375 375 % cat a
376 376 expand $Id: x/a cfa68229c116 Thu, 01 Jan 1970 00:00:03 +0000 user $
377 377 do not process $Id:
378 378 xxx $
379 379 $Xinfo: User Name <user@example.com>: xa $
380 380 % kwshrink a inside directory x
381 381 overwriting x/a shrinking keywords
382 382 % cat a
383 383 expand $Id$
384 384 do not process $Id:
385 385 xxx $
386 386 $Xinfo$
387 387 % kwexpand nonexistent
388 388 nonexistent:
389 389 % hg serve
390 390 % expansion
391 391 % hgweb file
392 392 200 Script output follows
393 393
394 394 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
395 395 do not process $Id:
396 396 xxx $
397 397 $Xinfo: User Name <user@example.com>: firstline $
398 398 % no expansion
399 399 % hgweb annotate
400 400 200 Script output follows
401 401
402 402
403 403 user@1: expand $Id$
404 404 user@1: do not process $Id:
405 405 user@1: xxx $
406 406 user@2: $Xinfo$
407 407
408 408
409 409
410 410
411 411 % hgweb changeset
412 412 200 Script output follows
413 413
414 414
415 415 # HG changeset patch
416 416 # User User Name <user@example.com>
417 417 # Date 3 0
418 418 # Node ID cfa68229c1167443337266ebac453c73b1d5d16e
419 419 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
420 420 xa
421 421
422 422 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
423 423 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
424 424 @@ -0,0 +1,4 @@
425 425 +expand $Id$
426 426 +do not process $Id:
427 427 +xxx $
428 428 +$Xinfo$
429 429
430 430 % hgweb filediff
431 431 200 Script output follows
432 432
433 433
434 434 --- a/a Thu Jan 01 00:00:00 1970 +0000
435 435 +++ b/a Thu Jan 01 00:00:02 1970 +0000
436 436 @@ -1,3 +1,4 @@
437 437 expand $Id$
438 438 do not process $Id:
439 439 xxx $
440 440 +$Xinfo$
441 441
442 442
443 443
444 444
445 445 % errors encountered
446 446 % merge/resolve
447 447 % simplemerge
448 448 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
449 449 created new head
450 450 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
451 451 (branch merge, don't forget to commit)
452 452 $Id: m 8731e1dadc99 Thu, 01 Jan 1970 00:00:00 +0000 test $
453 453 foo
454 454 % conflict
455 455 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
456 456 created new head
457 457 merging m
458 458 warning: conflicts during merge.
459 459 merging m failed!
460 460 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
461 461 use 'hg resolve' to retry unresolved file merges or 'hg up --clean' to abandon
462 462 % keyword stays outside conflict zone
463 463 $Id$
464 464 <<<<<<< local
465 465 bar
466 466 =======
467 467 foo
468 468 >>>>>>> other
469 469 % resolve to local
470 470 $Id: m 43dfd2854b5b Thu, 01 Jan 1970 00:00:00 +0000 test $
471 471 bar
472 472 % switch off expansion
473 473 % kwshrink with unknown file u
474 474 overwriting a shrinking keywords
475 475 overwriting m shrinking keywords
476 476 overwriting x/a shrinking keywords
477 477 % cat
478 478 expand $Id$
479 479 do not process $Id:
480 480 xxx $
481 481 $Xinfo$
482 482 ignore $Id$
483 483 % hg cat
484 484 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
485 485 do not process $Id:
486 486 xxx $
487 487 $Xinfo: User Name <user@example.com>: firstline $
488 488 ignore $Id$
489 489 a
490 490 % cat
491 491 expand $Id$
492 492 do not process $Id:
493 493 xxx $
494 494 $Xinfo$
495 495 ignore $Id$
496 496 % hg cat
497 497 expand $Id$
498 498 do not process $Id:
499 499 xxx $
500 500 $Xinfo$
501 501 ignore $Id$
502 502 a
General Comments 0
You need to be logged in to leave comments. Login now