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