##// END OF EJS Templates
keyword: copy: when copied source is a symlink, follow it...
Christian Ebert -
r13069:6aff4f14 1.7.2 stable
parent child Browse files
Show More
@@ -1,635 +1,649 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 configuration 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 import re, shutil, tempfile
89 import os, 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 144 def _preselect(wstatus, changed):
145 145 '''Retrieves modfied and added files from a working directory state
146 146 and returns the subset of each contained in given changed files
147 147 retrieved from a change context.'''
148 148 modified, added = wstatus[:2]
149 149 modified = [f for f in modified if f in changed]
150 150 added = [f for f in added if f in changed]
151 151 return modified, added
152 152
153 153
154 154 class kwtemplater(object):
155 155 '''
156 156 Sets up keyword templates, corresponding keyword regex, and
157 157 provides keyword substitution functions.
158 158 '''
159 159
160 160 def __init__(self, ui, repo, inc, exc):
161 161 self.ui = ui
162 162 self.repo = repo
163 163 self.match = match.match(repo.root, '', [], inc, exc)
164 164 self.restrict = kwtools['hgcmd'] in restricted.split()
165 165 self.record = False
166 166
167 167 kwmaps = self.ui.configitems('keywordmaps')
168 168 if kwmaps: # override default templates
169 169 self.templates = dict((k, templater.parsestring(v, False))
170 170 for k, v in kwmaps)
171 171 else:
172 172 self.templates = _defaultkwmaps(self.ui)
173 173 escaped = '|'.join(map(re.escape, self.templates.keys()))
174 174 self.re_kw = re.compile(r'\$(%s)\$' % escaped)
175 175 self.re_kwexp = re.compile(r'\$(%s): [^$\n\r]*? \$' % escaped)
176 176
177 177 templatefilters.filters.update({'utcdate': utcdate,
178 178 'svnisodate': svnisodate,
179 179 'svnutcdate': svnutcdate})
180 180
181 181 def substitute(self, data, path, ctx, subfunc):
182 182 '''Replaces keywords in data with expanded template.'''
183 183 def kwsub(mobj):
184 184 kw = mobj.group(1)
185 185 ct = cmdutil.changeset_templater(self.ui, self.repo,
186 186 False, None, '', False)
187 187 ct.use_template(self.templates[kw])
188 188 self.ui.pushbuffer()
189 189 ct.show(ctx, root=self.repo.root, file=path)
190 190 ekw = templatefilters.firstline(self.ui.popbuffer())
191 191 return '$%s: %s $' % (kw, ekw)
192 192 return subfunc(kwsub, data)
193 193
194 194 def expand(self, path, node, data):
195 195 '''Returns data with keywords expanded.'''
196 196 if not self.restrict and self.match(path) and not util.binary(data):
197 197 ctx = self.repo.filectx(path, fileid=node).changectx()
198 198 return self.substitute(data, path, ctx, self.re_kw.sub)
199 199 return data
200 200
201 201 def iskwfile(self, cand, ctx):
202 202 '''Returns subset of candidates which are configured for keyword
203 203 expansion are not symbolic links.'''
204 204 return [f for f in cand if self.match(f) and not 'l' in ctx.flags(f)]
205 205
206 206 def overwrite(self, ctx, candidates, lookup, expand, rekw=False):
207 207 '''Overwrites selected files expanding/shrinking keywords.'''
208 208 if self.restrict or lookup or self.record: # exclude kw_copy
209 209 candidates = self.iskwfile(candidates, ctx)
210 210 if not candidates:
211 211 return
212 212 kwcmd = self.restrict and lookup # kwexpand/kwshrink
213 213 if self.restrict or expand and lookup:
214 214 mf = ctx.manifest()
215 215 fctx = ctx
216 216 subn = (self.restrict or rekw) and self.re_kw.subn or self.re_kwexp.subn
217 217 msg = (expand and _('overwriting %s expanding keywords\n')
218 218 or _('overwriting %s shrinking keywords\n'))
219 219 for f in candidates:
220 220 if self.restrict:
221 221 data = self.repo.file(f).read(mf[f])
222 222 else:
223 223 data = self.repo.wread(f)
224 224 if util.binary(data):
225 225 continue
226 226 if expand:
227 227 if lookup:
228 228 fctx = self.repo.filectx(f, fileid=mf[f]).changectx()
229 229 data, found = self.substitute(data, f, fctx, subn)
230 230 elif self.restrict:
231 231 found = self.re_kw.search(data)
232 232 else:
233 233 data, found = _shrinktext(data, subn)
234 234 if found:
235 235 self.ui.note(msg % f)
236 236 self.repo.wwrite(f, data, ctx.flags(f))
237 237 if kwcmd:
238 238 self.repo.dirstate.normal(f)
239 239 elif self.record:
240 240 self.repo.dirstate.normallookup(f)
241 241
242 242 def shrink(self, fname, text):
243 243 '''Returns text with all keyword substitutions removed.'''
244 244 if self.match(fname) and not util.binary(text):
245 245 return _shrinktext(text, self.re_kwexp.sub)
246 246 return text
247 247
248 248 def shrinklines(self, fname, lines):
249 249 '''Returns lines with keyword substitutions removed.'''
250 250 if self.match(fname):
251 251 text = ''.join(lines)
252 252 if not util.binary(text):
253 253 return _shrinktext(text, self.re_kwexp.sub).splitlines(True)
254 254 return lines
255 255
256 256 def wread(self, fname, data):
257 257 '''If in restricted mode returns data read from wdir with
258 258 keyword substitutions removed.'''
259 259 return self.restrict and self.shrink(fname, data) or data
260 260
261 261 class kwfilelog(filelog.filelog):
262 262 '''
263 263 Subclass of filelog to hook into its read, add, cmp methods.
264 264 Keywords are "stored" unexpanded, and processed on reading.
265 265 '''
266 266 def __init__(self, opener, kwt, path):
267 267 super(kwfilelog, self).__init__(opener, path)
268 268 self.kwt = kwt
269 269 self.path = path
270 270
271 271 def read(self, node):
272 272 '''Expands keywords when reading filelog.'''
273 273 data = super(kwfilelog, self).read(node)
274 274 if self.renamed(node):
275 275 return data
276 276 return self.kwt.expand(self.path, node, data)
277 277
278 278 def add(self, text, meta, tr, link, p1=None, p2=None):
279 279 '''Removes keyword substitutions when adding to filelog.'''
280 280 text = self.kwt.shrink(self.path, text)
281 281 return super(kwfilelog, self).add(text, meta, tr, link, p1, p2)
282 282
283 283 def cmp(self, node, text):
284 284 '''Removes keyword substitutions for comparison.'''
285 285 text = self.kwt.shrink(self.path, text)
286 286 return super(kwfilelog, self).cmp(node, text)
287 287
288 288 def _status(ui, repo, kwt, *pats, **opts):
289 289 '''Bails out if [keyword] configuration is not active.
290 290 Returns status of working directory.'''
291 291 if kwt:
292 292 return repo.status(match=cmdutil.match(repo, pats, opts), clean=True,
293 293 unknown=opts.get('unknown') or opts.get('all'))
294 294 if ui.configitems('keyword'):
295 295 raise util.Abort(_('[keyword] patterns cannot match'))
296 296 raise util.Abort(_('no [keyword] patterns configured'))
297 297
298 298 def _kwfwrite(ui, repo, expand, *pats, **opts):
299 299 '''Selects files and passes them to kwtemplater.overwrite.'''
300 300 wctx = repo[None]
301 301 if len(wctx.parents()) > 1:
302 302 raise util.Abort(_('outstanding uncommitted merge'))
303 303 kwt = kwtools['templater']
304 304 wlock = repo.wlock()
305 305 try:
306 306 status = _status(ui, repo, kwt, *pats, **opts)
307 307 modified, added, removed, deleted, unknown, ignored, clean = status
308 308 if modified or added or removed or deleted:
309 309 raise util.Abort(_('outstanding uncommitted changes'))
310 310 kwt.overwrite(wctx, clean, True, expand)
311 311 finally:
312 312 wlock.release()
313 313
314 314 def demo(ui, repo, *args, **opts):
315 315 '''print [keywordmaps] configuration and an expansion example
316 316
317 317 Show current, custom, or default keyword template maps and their
318 318 expansions.
319 319
320 320 Extend the current configuration by specifying maps as arguments
321 321 and using -f/--rcfile to source an external hgrc file.
322 322
323 323 Use -d/--default to disable current configuration.
324 324
325 325 See :hg:`help templates` for information on templates and filters.
326 326 '''
327 327 def demoitems(section, items):
328 328 ui.write('[%s]\n' % section)
329 329 for k, v in sorted(items):
330 330 ui.write('%s = %s\n' % (k, v))
331 331
332 332 fn = 'demo.txt'
333 333 tmpdir = tempfile.mkdtemp('', 'kwdemo.')
334 334 ui.note(_('creating temporary repository at %s\n') % tmpdir)
335 335 repo = localrepo.localrepository(ui, tmpdir, True)
336 336 ui.setconfig('keyword', fn, '')
337 337
338 338 uikwmaps = ui.configitems('keywordmaps')
339 339 if args or opts.get('rcfile'):
340 340 ui.status(_('\n\tconfiguration using custom keyword template maps\n'))
341 341 if uikwmaps:
342 342 ui.status(_('\textending current template maps\n'))
343 343 if opts.get('default') or not uikwmaps:
344 344 ui.status(_('\toverriding default template maps\n'))
345 345 if opts.get('rcfile'):
346 346 ui.readconfig(opts.get('rcfile'))
347 347 if args:
348 348 # simulate hgrc parsing
349 349 rcmaps = ['[keywordmaps]\n'] + [a + '\n' for a in args]
350 350 fp = repo.opener('hgrc', 'w')
351 351 fp.writelines(rcmaps)
352 352 fp.close()
353 353 ui.readconfig(repo.join('hgrc'))
354 354 kwmaps = dict(ui.configitems('keywordmaps'))
355 355 elif opts.get('default'):
356 356 ui.status(_('\n\tconfiguration using default keyword template maps\n'))
357 357 kwmaps = _defaultkwmaps(ui)
358 358 if uikwmaps:
359 359 ui.status(_('\tdisabling current template maps\n'))
360 360 for k, v in kwmaps.iteritems():
361 361 ui.setconfig('keywordmaps', k, v)
362 362 else:
363 363 ui.status(_('\n\tconfiguration using current keyword template maps\n'))
364 364 kwmaps = dict(uikwmaps) or _defaultkwmaps(ui)
365 365
366 366 uisetup(ui)
367 367 reposetup(ui, repo)
368 368 ui.write('[extensions]\nkeyword =\n')
369 369 demoitems('keyword', ui.configitems('keyword'))
370 370 demoitems('keywordmaps', kwmaps.iteritems())
371 371 keywords = '$' + '$\n$'.join(sorted(kwmaps.keys())) + '$\n'
372 372 repo.wopener(fn, 'w').write(keywords)
373 373 repo[None].add([fn])
374 374 ui.note(_('\nkeywords written to %s:\n') % fn)
375 375 ui.note(keywords)
376 376 repo.dirstate.setbranch('demobranch')
377 377 for name, cmd in ui.configitems('hooks'):
378 378 if name.split('.', 1)[0].find('commit') > -1:
379 379 repo.ui.setconfig('hooks', name, '')
380 380 msg = _('hg keyword configuration and expansion example')
381 381 ui.note("hg ci -m '%s'\n" % msg)
382 382 repo.commit(text=msg)
383 383 ui.status(_('\n\tkeywords expanded\n'))
384 384 ui.write(repo.wread(fn))
385 385 shutil.rmtree(tmpdir, ignore_errors=True)
386 386
387 387 def expand(ui, repo, *pats, **opts):
388 388 '''expand keywords in the working directory
389 389
390 390 Run after (re)enabling keyword expansion.
391 391
392 392 kwexpand refuses to run if given files contain local changes.
393 393 '''
394 394 # 3rd argument sets expansion to True
395 395 _kwfwrite(ui, repo, True, *pats, **opts)
396 396
397 397 def files(ui, repo, *pats, **opts):
398 398 '''show files configured for keyword expansion
399 399
400 400 List which files in the working directory are matched by the
401 401 [keyword] configuration patterns.
402 402
403 403 Useful to prevent inadvertent keyword expansion and to speed up
404 404 execution by including only files that are actual candidates for
405 405 expansion.
406 406
407 407 See :hg:`help keyword` on how to construct patterns both for
408 408 inclusion and exclusion of files.
409 409
410 410 With -A/--all and -v/--verbose the codes used to show the status
411 411 of files are::
412 412
413 413 K = keyword expansion candidate
414 414 k = keyword expansion candidate (not tracked)
415 415 I = ignored
416 416 i = ignored (not tracked)
417 417 '''
418 418 kwt = kwtools['templater']
419 419 status = _status(ui, repo, kwt, *pats, **opts)
420 420 cwd = pats and repo.getcwd() or ''
421 421 modified, added, removed, deleted, unknown, ignored, clean = status
422 422 files = []
423 423 if not opts.get('unknown') or opts.get('all'):
424 424 files = sorted(modified + added + clean)
425 425 wctx = repo[None]
426 426 kwfiles = kwt.iskwfile(files, wctx)
427 427 kwunknown = kwt.iskwfile(unknown, wctx)
428 428 if not opts.get('ignore') or opts.get('all'):
429 429 showfiles = kwfiles, kwunknown
430 430 else:
431 431 showfiles = [], []
432 432 if opts.get('all') or opts.get('ignore'):
433 433 showfiles += ([f for f in files if f not in kwfiles],
434 434 [f for f in unknown if f not in kwunknown])
435 435 for char, filenames in zip('KkIi', showfiles):
436 436 fmt = (opts.get('all') or ui.verbose) and '%s %%s\n' % char or '%s\n'
437 437 for f in filenames:
438 438 ui.write(fmt % repo.pathto(f, cwd))
439 439
440 440 def shrink(ui, repo, *pats, **opts):
441 441 '''revert expanded keywords in the working directory
442 442
443 443 Run before changing/disabling active keywords or if you experience
444 444 problems with :hg:`import` or :hg:`merge`.
445 445
446 446 kwshrink refuses to run if given files contain local changes.
447 447 '''
448 448 # 3rd argument sets expansion to False
449 449 _kwfwrite(ui, repo, False, *pats, **opts)
450 450
451 451
452 452 def uisetup(ui):
453 453 ''' Monkeypatches dispatch._parse to retrieve user command.'''
454 454
455 455 def kwdispatch_parse(orig, ui, args):
456 456 '''Monkeypatch dispatch._parse to obtain running hg command.'''
457 457 cmd, func, args, options, cmdoptions = orig(ui, args)
458 458 kwtools['hgcmd'] = cmd
459 459 return cmd, func, args, options, cmdoptions
460 460
461 461 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse)
462 462
463 463 def reposetup(ui, repo):
464 464 '''Sets up repo as kwrepo for keyword substitution.
465 465 Overrides file method to return kwfilelog instead of filelog
466 466 if file matches user configuration.
467 467 Wraps commit to overwrite configured files with updated
468 468 keyword substitutions.
469 469 Monkeypatches patch and webcommands.'''
470 470
471 471 try:
472 472 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split()
473 473 or '.hg' in util.splitpath(repo.root)
474 474 or repo._url.startswith('bundle:')):
475 475 return
476 476 except AttributeError:
477 477 pass
478 478
479 479 inc, exc = [], ['.hg*']
480 480 for pat, opt in ui.configitems('keyword'):
481 481 if opt != 'ignore':
482 482 inc.append(pat)
483 483 else:
484 484 exc.append(pat)
485 485 if not inc:
486 486 return
487 487
488 488 kwtools['templater'] = kwt = kwtemplater(ui, repo, inc, exc)
489 489
490 490 class kwrepo(repo.__class__):
491 491 def file(self, f):
492 492 if f[0] == '/':
493 493 f = f[1:]
494 494 return kwfilelog(self.sopener, kwt, f)
495 495
496 496 def wread(self, filename):
497 497 data = super(kwrepo, self).wread(filename)
498 498 return kwt.wread(filename, data)
499 499
500 500 def commit(self, *args, **opts):
501 501 # use custom commitctx for user commands
502 502 # other extensions can still wrap repo.commitctx directly
503 503 self.commitctx = self.kwcommitctx
504 504 try:
505 505 return super(kwrepo, self).commit(*args, **opts)
506 506 finally:
507 507 del self.commitctx
508 508
509 509 def kwcommitctx(self, ctx, error=False):
510 510 n = super(kwrepo, self).commitctx(ctx, error)
511 511 # no lock needed, only called from repo.commit() which already locks
512 512 if not kwt.record:
513 513 restrict = kwt.restrict
514 514 kwt.restrict = True
515 515 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()),
516 516 False, True)
517 517 kwt.restrict = restrict
518 518 return n
519 519
520 520 def rollback(self, dryrun=False):
521 521 wlock = self.wlock()
522 522 try:
523 523 if not dryrun:
524 524 changed = self['.'].files()
525 525 ret = super(kwrepo, self).rollback(dryrun)
526 526 if not dryrun:
527 527 ctx = self['.']
528 528 modified, added = _preselect(self[None].status(), changed)
529 529 kwt.overwrite(ctx, modified, True, True)
530 530 kwt.overwrite(ctx, added, True, False)
531 531 return ret
532 532 finally:
533 533 wlock.release()
534 534
535 535 # monkeypatches
536 536 def kwpatchfile_init(orig, self, ui, fname, opener,
537 537 missing=False, eolmode=None):
538 538 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid
539 539 rejects or conflicts due to expanded keywords in working dir.'''
540 540 orig(self, ui, fname, opener, missing, eolmode)
541 541 # shrink keywords read from working dir
542 542 self.lines = kwt.shrinklines(self.fname, self.lines)
543 543
544 544 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None,
545 545 opts=None, prefix=''):
546 546 '''Monkeypatch patch.diff to avoid expansion.'''
547 547 kwt.restrict = True
548 548 return orig(repo, node1, node2, match, changes, opts, prefix)
549 549
550 550 def kwweb_skip(orig, web, req, tmpl):
551 551 '''Wraps webcommands.x turning off keyword expansion.'''
552 552 kwt.match = util.never
553 553 return orig(web, req, tmpl)
554 554
555 555 def kw_copy(orig, ui, repo, pats, opts, rename=False):
556 556 '''Wraps cmdutil.copy so that copy/rename destinations do not
557 557 contain expanded keywords.
558 Note that the source may also be a symlink as:
558 Note that the source of a regular file destination may also be a
559 symlink:
559 560 hg cp sym x -> x is symlink
560 561 cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords)
561 '''
562 For the latter we have to follow the symlink to find out whether its
563 target is configured for expansion and we therefore must unexpand the
564 keywords in the destination.'''
562 565 orig(ui, repo, pats, opts, rename)
563 566 if opts.get('dry_run'):
564 567 return
565 568 wctx = repo[None]
569 cwd = repo.getcwd()
570
571 def haskwsource(dest):
572 '''Returns true if dest is a regular file and configured for
573 expansion or a symlink which points to a file configured for
574 expansion. '''
575 source = repo.dirstate.copied(dest)
576 if 'l' in wctx.flags(source):
577 source = util.canonpath(repo.root, cwd,
578 os.path.realpath(source))
579 return kwt.match(source)
580
566 581 candidates = [f for f in repo.dirstate.copies() if
567 kwt.match(repo.dirstate.copied(f)) and
568 not 'l' in wctx.flags(f)]
582 not 'l' in wctx.flags(f) and haskwsource(f)]
569 583 kwt.overwrite(wctx, candidates, False, False)
570 584
571 585 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts):
572 586 '''Wraps record.dorecord expanding keywords after recording.'''
573 587 wlock = repo.wlock()
574 588 try:
575 589 # record returns 0 even when nothing has changed
576 590 # therefore compare nodes before and after
577 591 kwt.record = True
578 592 ctx = repo['.']
579 593 wstatus = repo[None].status()
580 594 ret = orig(ui, repo, commitfunc, *pats, **opts)
581 595 recctx = repo['.']
582 596 if ctx != recctx:
583 597 modified, added = _preselect(wstatus, recctx.files())
584 598 kwt.restrict = False
585 599 kwt.overwrite(recctx, modified, False, True)
586 600 kwt.overwrite(recctx, added, False, True, True)
587 601 kwt.restrict = True
588 602 return ret
589 603 finally:
590 604 wlock.release()
591 605
592 606 repo.__class__ = kwrepo
593 607
594 608 def kwfilectx_cmp(orig, self, fctx):
595 609 # keyword affects data size, comparing wdir and filelog size does
596 610 # not make sense
597 611 if (fctx._filerev is None and
598 612 (self._repo._encodefilterpats or
599 613 kwt.match(fctx.path()) and not 'l' in fctx.flags()) or
600 614 self.size() == fctx.size()):
601 615 return self._filelog.cmp(self._filenode, fctx.data())
602 616 return True
603 617
604 618 extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp)
605 619 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init)
606 620 extensions.wrapfunction(patch, 'diff', kw_diff)
607 621 extensions.wrapfunction(cmdutil, 'copy', kw_copy)
608 622 for c in 'annotate changeset rev filediff diff'.split():
609 623 extensions.wrapfunction(webcommands, c, kwweb_skip)
610 624 for name in recordextensions.split():
611 625 try:
612 626 record = extensions.find(name)
613 627 extensions.wrapfunction(record, 'dorecord', kw_dorecord)
614 628 except KeyError:
615 629 pass
616 630
617 631 cmdtable = {
618 632 'kwdemo':
619 633 (demo,
620 634 [('d', 'default', None, _('show default keyword template maps')),
621 635 ('f', 'rcfile', '',
622 636 _('read maps from rcfile'), _('FILE'))],
623 637 _('hg kwdemo [-d] [-f RCFILE] [TEMPLATEMAP]...')),
624 638 'kwexpand': (expand, commands.walkopts,
625 639 _('hg kwexpand [OPTION]... [FILE]...')),
626 640 'kwfiles':
627 641 (files,
628 642 [('A', 'all', None, _('show keyword status flags of all files')),
629 643 ('i', 'ignore', None, _('show files excluded from expansion')),
630 644 ('u', 'unknown', None, _('only show unknown (not tracked) files')),
631 645 ] + commands.walkopts,
632 646 _('hg kwfiles [OPTION]... [FILE]...')),
633 647 'kwshrink': (shrink, commands.walkopts,
634 648 _('hg kwshrink [OPTION]... [FILE]...')),
635 649 }
@@ -1,1039 +1,1060 b''
1 1 $ cat <<EOF >> $HGRCPATH
2 2 > [extensions]
3 3 > keyword =
4 4 > mq =
5 5 > notify =
6 6 > record =
7 7 > transplant =
8 8 > [ui]
9 9 > interactive = true
10 10 > EOF
11 11
12 12 Run kwdemo before [keyword] files are set up
13 13 as it would succeed without uisetup otherwise
14 14
15 15 $ hg --quiet kwdemo
16 16 [extensions]
17 17 keyword =
18 18 [keyword]
19 19 demo.txt =
20 20 [keywordmaps]
21 21 Author = {author|user}
22 22 Date = {date|utcdate}
23 23 Header = {root}/{file},v {node|short} {date|utcdate} {author|user}
24 24 Id = {file|basename},v {node|short} {date|utcdate} {author|user}
25 25 RCSFile = {file|basename},v
26 26 RCSfile = {file|basename},v
27 27 Revision = {node|short}
28 28 Source = {root}/{file},v
29 29 $Author: test $
30 30 $Date: ????/??/?? ??:??:?? $ (glob)
31 31 $Header: */demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
32 32 $Id: demo.txt,v ???????????? ????/??/?? ??:??:?? test $ (glob)
33 33 $RCSFile: demo.txt,v $
34 34 $RCSfile: demo.txt,v $
35 35 $Revision: ???????????? $ (glob)
36 36 $Source: */demo.txt,v $ (glob)
37 37
38 38 $ hg --quiet kwdemo "Branch = {branches}"
39 39 [extensions]
40 40 keyword =
41 41 [keyword]
42 42 demo.txt =
43 43 [keywordmaps]
44 44 Branch = {branches}
45 45 $Branch: demobranch $
46 46
47 47 $ cat <<EOF >> $HGRCPATH
48 48 > [keyword]
49 49 > ** =
50 50 > b = ignore
51 51 > i = ignore
52 52 > [hooks]
53 53 > EOF
54 54 $ cp $HGRCPATH $HGRCPATH.nohooks
55 55 > cat <<EOF >> $HGRCPATH
56 56 > commit=
57 57 > commit.test=cp a hooktest
58 58 > EOF
59 59
60 60 $ hg init Test-bndl
61 61 $ cd Test-bndl
62 62
63 63 kwshrink should exit silently in empty/invalid repo
64 64
65 65 $ hg kwshrink
66 66
67 67 Symlinks cannot be created on Windows.
68 68 A bundle to test this was made with:
69 69 hg init t
70 70 cd t
71 71 echo a > a
72 72 ln -s a sym
73 73 hg add sym
74 74 hg ci -m addsym -u mercurial
75 75 hg bundle --base null ../test-keyword.hg
76 76
77 77 $ hg pull -u "$TESTDIR"/test-keyword.hg
78 78 pulling from *test-keyword.hg (glob)
79 79 requesting all changes
80 80 adding changesets
81 81 adding manifests
82 82 adding file changes
83 83 added 1 changesets with 1 changes to 1 files
84 84 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
85 85
86 86 $ echo 'expand $Id$' > a
87 87 $ echo 'do not process $Id:' >> a
88 88 $ echo 'xxx $' >> a
89 89 $ echo 'ignore $Id$' > b
90 90
91 91 Output files as they were created
92 92
93 93 $ cat a b
94 94 expand $Id$
95 95 do not process $Id:
96 96 xxx $
97 97 ignore $Id$
98 98
99 99 no kwfiles
100 100
101 101 $ hg kwfiles
102 102
103 103 untracked candidates
104 104
105 105 $ hg -v kwfiles --unknown
106 106 k a
107 107
108 108 Add files and check status
109 109
110 110 $ hg addremove
111 111 adding a
112 112 adding b
113 113 $ hg status
114 114 A a
115 115 A b
116 116
117 117
118 118 Default keyword expansion including commit hook
119 119 Interrupted commit should not change state or run commit hook
120 120
121 121 $ hg --debug commit
122 122 abort: empty commit message
123 123 [255]
124 124 $ hg status
125 125 A a
126 126 A b
127 127
128 128 Commit with several checks
129 129
130 130 $ hg --debug commit -mabsym -u 'User Name <user@example.com>'
131 131 a
132 132 b
133 133 overwriting a expanding keywords
134 134 running hook commit.test: cp a hooktest
135 135 committed changeset 1:ef63ca68695bc9495032c6fda1350c71e6d256e9
136 136 $ hg status
137 137 ? hooktest
138 138 $ hg debugrebuildstate
139 139 $ hg --quiet identify
140 140 ef63ca68695b
141 141
142 142 cat files in working directory with keywords expanded
143 143
144 144 $ cat a b
145 145 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
146 146 do not process $Id:
147 147 xxx $
148 148 ignore $Id$
149 149
150 150 hg cat files and symlink, no expansion
151 151
152 152 $ hg cat sym a b && echo
153 153 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
154 154 do not process $Id:
155 155 xxx $
156 156 ignore $Id$
157 157 a
158 158
159 159 Test hook execution
160 160
161 161 $ diff a hooktest
162 162
163 163 $ cp $HGRCPATH.nohooks $HGRCPATH
164 164 $ rm hooktest
165 165
166 166 bundle
167 167
168 168 $ hg bundle --base null ../kw.hg
169 169 2 changesets found
170 170 $ cd ..
171 171 $ hg init Test
172 172 $ cd Test
173 173
174 174 Notify on pull to check whether keywords stay as is in email
175 175 ie. if patch.diff wrapper acts as it should
176 176
177 177 $ cat <<EOF >> $HGRCPATH
178 178 > [hooks]
179 179 > incoming.notify = python:hgext.notify.hook
180 180 > [notify]
181 181 > sources = pull
182 182 > diffstat = False
183 183 > maxsubject = 15
184 184 > [reposubs]
185 185 > * = Test
186 186 > EOF
187 187
188 188 Pull from bundle and trigger notify
189 189
190 190 $ hg pull -u ../kw.hg
191 191 pulling from ../kw.hg
192 192 requesting all changes
193 193 adding changesets
194 194 adding manifests
195 195 adding file changes
196 196 added 2 changesets with 3 changes to 3 files
197 197 Content-Type: text/plain; charset="us-ascii"
198 198 MIME-Version: 1.0
199 199 Content-Transfer-Encoding: 7bit
200 200 Date: * (glob)
201 201 Subject: changeset in...
202 202 From: mercurial
203 203 X-Hg-Notification: changeset a2392c293916
204 204 Message-Id: <hg.a2392c293916*> (glob)
205 205 To: Test
206 206
207 207 changeset a2392c293916 in $TESTTMP/Test
208 208 details: *cmd=changeset;node=a2392c293916 (glob)
209 209 description:
210 210 addsym
211 211
212 212 diffs (6 lines):
213 213
214 214 diff -r 000000000000 -r a2392c293916 sym
215 215 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
216 216 +++ b/sym Sat Feb 09 20:25:47 2008 +0100
217 217 @@ -0,0 +1,1 @@
218 218 +a
219 219 \ No newline at end of file
220 220 Content-Type: text/plain; charset="us-ascii"
221 221 MIME-Version: 1.0
222 222 Content-Transfer-Encoding: 7bit
223 223 Date:* (glob)
224 224 Subject: changeset in...
225 225 From: User Name <user@example.com>
226 226 X-Hg-Notification: changeset ef63ca68695b
227 227 Message-Id: <hg.ef63ca68695b*> (glob)
228 228 To: Test
229 229
230 230 changeset ef63ca68695b in $TESTTMP/Test
231 231 details: $TESTTMP/Test?cmd=changeset;node=ef63ca68695b
232 232 description:
233 233 absym
234 234
235 235 diffs (12 lines):
236 236
237 237 diff -r a2392c293916 -r ef63ca68695b a
238 238 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
239 239 +++ b/a Thu Jan 01 00:00:00 1970 +0000
240 240 @@ -0,0 +1,3 @@
241 241 +expand $Id$
242 242 +do not process $Id:
243 243 +xxx $
244 244 diff -r a2392c293916 -r ef63ca68695b b
245 245 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
246 246 +++ b/b Thu Jan 01 00:00:00 1970 +0000
247 247 @@ -0,0 +1,1 @@
248 248 +ignore $Id$
249 249 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
250 250
251 251 $ cp $HGRCPATH.nohooks $HGRCPATH
252 252
253 253 Touch files and check with status
254 254
255 255 $ touch a b
256 256 $ hg status
257 257
258 258 Update and expand
259 259
260 260 $ rm sym a b
261 261 $ hg update -C
262 262 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
263 263 $ cat a b
264 264 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
265 265 do not process $Id:
266 266 xxx $
267 267 ignore $Id$
268 268
269 269 Check whether expansion is filewise
270 270
271 271 $ echo '$Id$' > c
272 272 $ echo 'tests for different changenodes' >> c
273 273
274 274 commit file c
275 275
276 276 $ hg commit -A -mcndiff -d '1 0' -u 'User Name <user@example.com>'
277 277 adding c
278 278
279 279 force expansion
280 280
281 281 $ hg -v kwexpand
282 282 overwriting a expanding keywords
283 283 overwriting c expanding keywords
284 284
285 285 compare changenodes in a and c
286 286
287 287 $ cat a c
288 288 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
289 289 do not process $Id:
290 290 xxx $
291 291 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
292 292 tests for different changenodes
293 293
294 294 record
295 295
296 296 $ echo '$Id$' > r
297 297 $ hg add r
298 298
299 299 record chunk
300 300
301 301 $ python -c \
302 302 > 'l=open("a").readlines();l.insert(1,"foo\n");l.append("bar\n");open("a","w").writelines(l);'
303 303 $ hg record -d '1 10' -m rectest a<<EOF
304 304 > y
305 305 > y
306 306 > n
307 307 > EOF
308 308 diff --git a/a b/a
309 309 2 hunks, 2 lines changed
310 310 examine changes to 'a'? [Ynsfdaq?]
311 311 @@ -1,3 +1,4 @@
312 312 expand $Id$
313 313 +foo
314 314 do not process $Id:
315 315 xxx $
316 316 record change 1/2 to 'a'? [Ynsfdaq?]
317 317 @@ -2,2 +3,3 @@
318 318 do not process $Id:
319 319 xxx $
320 320 +bar
321 321 record change 2/2 to 'a'? [Ynsfdaq?]
322 322
323 323 $ hg identify
324 324 d17e03c92c97+ tip
325 325 $ hg status
326 326 M a
327 327 A r
328 328
329 329 Cat modified file a
330 330
331 331 $ cat a
332 332 expand $Id: a,v d17e03c92c97 1970/01/01 00:00:01 test $
333 333 foo
334 334 do not process $Id:
335 335 xxx $
336 336 bar
337 337
338 338 Diff remaining chunk
339 339
340 340 $ hg diff a
341 341 diff -r d17e03c92c97 a
342 342 --- a/a Wed Dec 31 23:59:51 1969 -0000
343 343 +++ b/a * (glob)
344 344 @@ -2,3 +2,4 @@
345 345 foo
346 346 do not process $Id:
347 347 xxx $
348 348 +bar
349 349
350 350 $ hg rollback
351 351 rolling back to revision 2 (undo commit)
352 352
353 353 Record all chunks in file a
354 354
355 355 $ echo foo > msg
356 356
357 357 - do not use "hg record -m" here!
358 358
359 359 $ hg record -l msg -d '1 11' a<<EOF
360 360 > y
361 361 > y
362 362 > y
363 363 > EOF
364 364 diff --git a/a b/a
365 365 2 hunks, 2 lines changed
366 366 examine changes to 'a'? [Ynsfdaq?]
367 367 @@ -1,3 +1,4 @@
368 368 expand $Id$
369 369 +foo
370 370 do not process $Id:
371 371 xxx $
372 372 record change 1/2 to 'a'? [Ynsfdaq?]
373 373 @@ -2,2 +3,3 @@
374 374 do not process $Id:
375 375 xxx $
376 376 +bar
377 377 record change 2/2 to 'a'? [Ynsfdaq?]
378 378
379 379 File a should be clean
380 380
381 381 $ hg status -A a
382 382 C a
383 383
384 384 rollback and revert expansion
385 385
386 386 $ cat a
387 387 expand $Id: a,v 59f969a3b52c 1970/01/01 00:00:01 test $
388 388 foo
389 389 do not process $Id:
390 390 xxx $
391 391 bar
392 392 $ hg --verbose rollback
393 393 rolling back to revision 2 (undo commit)
394 394 overwriting a expanding keywords
395 395 $ hg status a
396 396 M a
397 397 $ cat a
398 398 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
399 399 foo
400 400 do not process $Id:
401 401 xxx $
402 402 bar
403 403 $ echo '$Id$' > y
404 404 $ echo '$Id$' > z
405 405 $ hg add y
406 406 $ hg commit -Am "rollback only" z
407 407 $ cat z
408 408 $Id: z,v 45a5d3adce53 1970/01/01 00:00:00 test $
409 409 $ hg --verbose rollback
410 410 rolling back to revision 2 (undo commit)
411 411 overwriting z shrinking keywords
412 412
413 413 Only z should be overwritten
414 414
415 415 $ hg status a y z
416 416 M a
417 417 A y
418 418 A z
419 419 $ cat z
420 420 $Id$
421 421 $ hg forget y z
422 422 $ rm y z
423 423
424 424 record added file alone
425 425
426 426 $ hg -v record -l msg -d '1 12' r<<EOF
427 427 > y
428 428 > EOF
429 429 diff --git a/r b/r
430 430 new file mode 100644
431 431 examine changes to 'r'? [Ynsfdaq?]
432 432 r
433 433 committed changeset 3:899491280810
434 434 overwriting r expanding keywords
435 435 $ hg --verbose rollback
436 436 rolling back to revision 2 (undo commit)
437 437 overwriting r shrinking keywords
438 438 $ hg forget r
439 439 $ rm msg r
440 440 $ hg update -C
441 441 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
442 442
443 443 record added keyword ignored file
444 444
445 445 $ echo '$Id$' > i
446 446 $ hg add i
447 447 $ hg --verbose record -d '1 13' -m recignored<<EOF
448 448 > y
449 449 > EOF
450 450 diff --git a/i b/i
451 451 new file mode 100644
452 452 examine changes to 'i'? [Ynsfdaq?]
453 453 i
454 454 committed changeset 3:5f40fe93bbdc
455 455 $ cat i
456 456 $Id$
457 457 $ hg -q rollback
458 458 $ hg forget i
459 459 $ rm i
460 460
461 461 Test patch queue repo
462 462
463 463 $ hg init --mq
464 464 $ hg qimport -r tip -n mqtest.diff
465 465 $ hg commit --mq -m mqtest
466 466
467 467 Keywords should not be expanded in patch
468 468
469 469 $ cat .hg/patches/mqtest.diff
470 470 # HG changeset patch
471 471 # User User Name <user@example.com>
472 472 # Date 1 0
473 473 # Node ID 40a904bbbe4cd4ab0a1f28411e35db26341a40ad
474 474 # Parent ef63ca68695bc9495032c6fda1350c71e6d256e9
475 475 cndiff
476 476
477 477 diff -r ef63ca68695b -r 40a904bbbe4c c
478 478 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
479 479 +++ b/c Thu Jan 01 00:00:01 1970 +0000
480 480 @@ -0,0 +1,2 @@
481 481 +$Id$
482 482 +tests for different changenodes
483 483
484 484 $ hg qpop
485 485 popping mqtest.diff
486 486 patch queue now empty
487 487
488 488 qgoto, implying qpush, should expand
489 489
490 490 $ hg qgoto mqtest.diff
491 491 applying mqtest.diff
492 492 now at: mqtest.diff
493 493 $ cat c
494 494 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
495 495 tests for different changenodes
496 496 $ hg cat c
497 497 $Id: c,v 40a904bbbe4c 1970/01/01 00:00:01 user $
498 498 tests for different changenodes
499 499
500 500 Keywords should not be expanded in filelog
501 501
502 502 $ hg --config 'extensions.keyword=!' cat c
503 503 $Id$
504 504 tests for different changenodes
505 505
506 506 qpop and move on
507 507
508 508 $ hg qpop
509 509 popping mqtest.diff
510 510 patch queue now empty
511 511
512 512 Copy and show added kwfiles
513 513
514 514 $ hg cp a c
515 515 $ hg kwfiles
516 516 a
517 517 c
518 518
519 519 Commit and show expansion in original and copy
520 520
521 521 $ hg --debug commit -ma2c -d '1 0' -u 'User Name <user@example.com>'
522 522 c
523 523 c: copy a:0045e12f6c5791aac80ca6cbfd97709a88307292
524 524 overwriting c expanding keywords
525 525 committed changeset 2:25736cf2f5cbe41f6be4e6784ef6ecf9f3bbcc7d
526 526 $ cat a c
527 527 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
528 528 do not process $Id:
529 529 xxx $
530 530 expand $Id: c,v 25736cf2f5cb 1970/01/01 00:00:01 user $
531 531 do not process $Id:
532 532 xxx $
533 533
534 534 Touch copied c and check its status
535 535
536 536 $ touch c
537 537 $ hg status
538 538
539 539 Copy kwfile to keyword ignored file unexpanding keywords
540 540
541 541 $ hg --verbose copy a i
542 542 copying a to i
543 543 overwriting i shrinking keywords
544 544 $ head -n 1 i
545 545 expand $Id$
546 546 $ hg forget i
547 547 $ rm i
548 548
549 549 Copy ignored file to ignored file: no overwriting
550 550
551 551 $ hg --verbose copy b i
552 552 copying b to i
553 553 $ hg forget i
554 554 $ rm i
555 555
556 cp symlink (becomes regular file), and hg copy after
556 cp symlink file; hg cp -A symlink file (part1)
557 - copied symlink points to kwfile: overwrite
557 558
558 559 $ cp sym i
559 560 $ ls -l i
560 561 -rw-r--r--* (glob)
561 562 $ head -1 i
562 563 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
563 564 $ hg copy --after --verbose sym i
564 565 copying sym to i
565 566 overwriting i shrinking keywords
566 567 $ head -1 i
567 568 expand $Id$
568 569 $ hg forget i
569 570 $ rm i
570 571
571 572 Test different options of hg kwfiles
572 573
573 574 $ hg kwfiles
574 575 a
575 576 c
576 577 $ hg -v kwfiles --ignore
577 578 I b
578 579 I sym
579 580 $ hg kwfiles --all
580 581 K a
581 582 K c
582 583 I b
583 584 I sym
584 585
585 586 Diff specific revision
586 587
587 588 $ hg diff --rev 1
588 589 diff -r ef63ca68695b c
589 590 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
590 591 +++ b/c * (glob)
591 592 @@ -0,0 +1,3 @@
592 593 +expand $Id$
593 594 +do not process $Id:
594 595 +xxx $
595 596
596 597 Status after rollback:
597 598
598 599 $ hg rollback
599 600 rolling back to revision 1 (undo commit)
600 601 $ hg status
601 602 A c
602 603 $ hg update --clean
603 604 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
604 605
606 cp symlink file; hg cp -A symlink file (part2)
607 - copied symlink points to kw ignored file: do not overwrite
608
609 $ cat a > i
610 $ ln -s i symignored
611 $ hg commit -Am 'fake expansion in ignored and symlink' i symignored
612 $ cp symignored x
613 $ hg copy --after --verbose symignored x
614 copying symignored to x
615 $ head -n 1 x
616 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
617 $ hg forget x
618 $ rm x
619
620 $ hg rollback
621 rolling back to revision 1 (undo commit)
622 $ hg update --clean
623 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
624 $ rm i symignored
625
605 626 Custom keywordmaps as argument to kwdemo
606 627
607 628 $ hg --quiet kwdemo "Xinfo = {author}: {desc}"
608 629 [extensions]
609 630 keyword =
610 631 [keyword]
611 632 ** =
612 633 b = ignore
613 634 demo.txt =
614 635 i = ignore
615 636 [keywordmaps]
616 637 Xinfo = {author}: {desc}
617 638 $Xinfo: test: hg keyword configuration and expansion example $
618 639
619 640 Configure custom keywordmaps
620 641
621 642 $ cat <<EOF >>$HGRCPATH
622 643 > [keywordmaps]
623 644 > Id = {file} {node|short} {date|rfc822date} {author|user}
624 645 > Xinfo = {author}: {desc}
625 646 > EOF
626 647
627 648 Cat and hg cat files before custom expansion
628 649
629 650 $ cat a b
630 651 expand $Id: a,v ef63ca68695b 1970/01/01 00:00:00 user $
631 652 do not process $Id:
632 653 xxx $
633 654 ignore $Id$
634 655 $ hg cat sym a b && echo
635 656 expand $Id: a ef63ca68695b Thu, 01 Jan 1970 00:00:00 +0000 user $
636 657 do not process $Id:
637 658 xxx $
638 659 ignore $Id$
639 660 a
640 661
641 662 Write custom keyword and prepare multiline commit message
642 663
643 664 $ echo '$Xinfo$' >> a
644 665 $ cat <<EOF >> log
645 666 > firstline
646 667 > secondline
647 668 > EOF
648 669
649 670 Interrupted commit should not change state
650 671
651 672 $ hg commit
652 673 abort: empty commit message
653 674 [255]
654 675 $ hg status
655 676 M a
656 677 ? c
657 678 ? log
658 679
659 680 Commit with multiline message and custom expansion
660 681
661 682 $ hg --debug commit -l log -d '2 0' -u 'User Name <user@example.com>'
662 683 a
663 684 overwriting a expanding keywords
664 685 committed changeset 2:bb948857c743469b22bbf51f7ec8112279ca5d83
665 686 $ rm log
666 687
667 688 Stat, verify and show custom expansion (firstline)
668 689
669 690 $ hg status
670 691 ? c
671 692 $ hg verify
672 693 checking changesets
673 694 checking manifests
674 695 crosschecking files in changesets and manifests
675 696 checking files
676 697 3 files, 3 changesets, 4 total revisions
677 698 $ cat a b
678 699 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
679 700 do not process $Id:
680 701 xxx $
681 702 $Xinfo: User Name <user@example.com>: firstline $
682 703 ignore $Id$
683 704 $ hg cat sym a b && echo
684 705 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
685 706 do not process $Id:
686 707 xxx $
687 708 $Xinfo: User Name <user@example.com>: firstline $
688 709 ignore $Id$
689 710 a
690 711
691 712 annotate
692 713
693 714 $ hg annotate a
694 715 1: expand $Id$
695 716 1: do not process $Id:
696 717 1: xxx $
697 718 2: $Xinfo$
698 719
699 720 remove with status checks
700 721
701 722 $ hg debugrebuildstate
702 723 $ hg remove a
703 724 $ hg --debug commit -m rma
704 725 committed changeset 3:d14c712653769de926994cf7fbb06c8fbd68f012
705 726 $ hg status
706 727 ? c
707 728
708 729 Rollback, revert, and check expansion
709 730
710 731 $ hg rollback
711 732 rolling back to revision 2 (undo commit)
712 733 $ hg status
713 734 R a
714 735 ? c
715 736 $ hg revert --no-backup --rev tip a
716 737 $ cat a
717 738 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
718 739 do not process $Id:
719 740 xxx $
720 741 $Xinfo: User Name <user@example.com>: firstline $
721 742
722 743 Clone to test global and local configurations
723 744
724 745 $ cd ..
725 746
726 747 Expansion in destinaton with global configuration
727 748
728 749 $ hg --quiet clone Test globalconf
729 750 $ cat globalconf/a
730 751 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
731 752 do not process $Id:
732 753 xxx $
733 754 $Xinfo: User Name <user@example.com>: firstline $
734 755
735 756 No expansion in destination with local configuration in origin only
736 757
737 758 $ hg --quiet --config 'keyword.**=ignore' clone Test localconf
738 759 $ cat localconf/a
739 760 expand $Id$
740 761 do not process $Id:
741 762 xxx $
742 763 $Xinfo$
743 764
744 765 Clone to test incoming
745 766
746 767 $ hg clone -r1 Test Test-a
747 768 adding changesets
748 769 adding manifests
749 770 adding file changes
750 771 added 2 changesets with 3 changes to 3 files
751 772 updating to branch default
752 773 3 files updated, 0 files merged, 0 files removed, 0 files unresolved
753 774 $ cd Test-a
754 775 $ cat <<EOF >> .hg/hgrc
755 776 > [paths]
756 777 > default = ../Test
757 778 > EOF
758 779 $ hg incoming
759 780 comparing with $TESTTMP/Test
760 781 searching for changes
761 782 changeset: 2:bb948857c743
762 783 tag: tip
763 784 user: User Name <user@example.com>
764 785 date: Thu Jan 01 00:00:02 1970 +0000
765 786 summary: firstline
766 787
767 788 Imported patch should not be rejected
768 789
769 790 $ python -c \
770 791 > 'import re; s=re.sub("(Id.*)","\\1 rejecttest",open("a").read()); open("a","wb").write(s);'
771 792 $ hg --debug commit -m'rejects?' -d '3 0' -u 'User Name <user@example.com>'
772 793 a
773 794 overwriting a expanding keywords
774 795 committed changeset 2:85e279d709ffc28c9fdd1b868570985fc3d87082
775 796 $ hg export -o ../rejecttest.diff tip
776 797 $ cd ../Test
777 798 $ hg import ../rejecttest.diff
778 799 applying ../rejecttest.diff
779 800 $ cat a b
780 801 expand $Id: a 4e0994474d25 Thu, 01 Jan 1970 00:00:03 +0000 user $ rejecttest
781 802 do not process $Id: rejecttest
782 803 xxx $
783 804 $Xinfo: User Name <user@example.com>: rejects? $
784 805 ignore $Id$
785 806
786 807 $ hg rollback
787 808 rolling back to revision 2 (undo commit)
788 809 $ hg update --clean
789 810 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
790 811
791 812 kwexpand/kwshrink on selected files
792 813
793 814 $ mkdir x
794 815 $ hg copy a x/a
795 816 $ hg --verbose kwshrink a
796 817 overwriting a shrinking keywords
797 818 $ hg status a
798 819 $ hg --verbose kwexpand a
799 820 overwriting a expanding keywords
800 821 $ hg status a
801 822
802 823 kwexpand x/a should abort
803 824
804 825 $ hg --verbose kwexpand x/a
805 826 abort: outstanding uncommitted changes
806 827 [255]
807 828 $ cd x
808 829 $ hg --debug commit -m xa -d '3 0' -u 'User Name <user@example.com>'
809 830 x/a
810 831 x/a: copy a:779c764182ce5d43e2b1eb66ce06d7b47bfe342e
811 832 overwriting x/a expanding keywords
812 833 committed changeset 3:b4560182a3f9a358179fd2d835c15e9da379c1e4
813 834 $ cat a
814 835 expand $Id: x/a b4560182a3f9 Thu, 01 Jan 1970 00:00:03 +0000 user $
815 836 do not process $Id:
816 837 xxx $
817 838 $Xinfo: User Name <user@example.com>: xa $
818 839
819 840 kwshrink a inside directory x
820 841
821 842 $ hg --verbose kwshrink a
822 843 overwriting x/a shrinking keywords
823 844 $ cat a
824 845 expand $Id$
825 846 do not process $Id:
826 847 xxx $
827 848 $Xinfo$
828 849 $ cd ..
829 850
830 851 kwexpand nonexistent
831 852
832 853 $ hg kwexpand nonexistent
833 854 nonexistent:* (glob)
834 855
835 856
836 857 hg serve
837 858 - expand with hgweb file
838 859 - no expansion with hgweb annotate/changeset/filediff
839 860 - check errors
840 861
841 862 $ hg serve -p $HGPORT -d --pid-file=hg.pid -A access.log -E errors.log
842 863 $ cat hg.pid >> $DAEMON_PIDS
843 864 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/file/tip/a/?style=raw'
844 865 200 Script output follows
845 866
846 867 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
847 868 do not process $Id:
848 869 xxx $
849 870 $Xinfo: User Name <user@example.com>: firstline $
850 871 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/annotate/tip/a/?style=raw'
851 872 200 Script output follows
852 873
853 874
854 875 user@1: expand $Id$
855 876 user@1: do not process $Id:
856 877 user@1: xxx $
857 878 user@2: $Xinfo$
858 879
859 880
860 881
861 882
862 883 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/rev/tip/?style=raw'
863 884 200 Script output follows
864 885
865 886
866 887 # HG changeset patch
867 888 # User User Name <user@example.com>
868 889 # Date 3 0
869 890 # Node ID b4560182a3f9a358179fd2d835c15e9da379c1e4
870 891 # Parent bb948857c743469b22bbf51f7ec8112279ca5d83
871 892 xa
872 893
873 894 diff -r bb948857c743 -r b4560182a3f9 x/a
874 895 --- /dev/null Thu Jan 01 00:00:00 1970 +0000
875 896 +++ b/x/a Thu Jan 01 00:00:03 1970 +0000
876 897 @@ -0,0 +1,4 @@
877 898 +expand $Id$
878 899 +do not process $Id:
879 900 +xxx $
880 901 +$Xinfo$
881 902
882 903 $ $TESTDIR/get-with-headers.py localhost:$HGPORT '/diff/bb948857c743/a?style=raw'
883 904 200 Script output follows
884 905
885 906
886 907 diff -r ef63ca68695b -r bb948857c743 a
887 908 --- a/a Thu Jan 01 00:00:00 1970 +0000
888 909 +++ b/a Thu Jan 01 00:00:02 1970 +0000
889 910 @@ -1,3 +1,4 @@
890 911 expand $Id$
891 912 do not process $Id:
892 913 xxx $
893 914 +$Xinfo$
894 915
895 916
896 917
897 918
898 919 $ cat errors.log
899 920
900 921 Prepare merge and resolve tests
901 922
902 923 $ echo '$Id$' > m
903 924 $ hg add m
904 925 $ hg commit -m 4kw
905 926 $ echo foo >> m
906 927 $ hg commit -m 5foo
907 928
908 929 simplemerge
909 930
910 931 $ hg update 4
911 932 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
912 933 $ echo foo >> m
913 934 $ hg commit -m 6foo
914 935 created new head
915 936 $ hg merge
916 937 0 files updated, 0 files merged, 0 files removed, 0 files unresolved
917 938 (branch merge, don't forget to commit)
918 939 $ hg commit -m simplemerge
919 940 $ cat m
920 941 $Id: m 27d48ee14f67 Thu, 01 Jan 1970 00:00:00 +0000 test $
921 942 foo
922 943
923 944 conflict: keyword should stay outside conflict zone
924 945
925 946 $ hg update 4
926 947 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
927 948 $ echo bar >> m
928 949 $ hg commit -m 8bar
929 950 created new head
930 951 $ hg merge
931 952 merging m
932 953 warning: conflicts during merge.
933 954 merging m failed!
934 955 0 files updated, 0 files merged, 0 files removed, 1 files unresolved
935 956 use 'hg resolve' to retry unresolved file merges or 'hg update -C .' to abandon
936 957 [1]
937 958 $ cat m
938 959 $Id$
939 960 <<<<<<< local
940 961 bar
941 962 =======
942 963 foo
943 964 >>>>>>> other
944 965
945 966 resolve to local
946 967
947 968 $ HGMERGE=internal:local hg resolve -a
948 969 $ hg commit -m localresolve
949 970 $ cat m
950 971 $Id: m 41efa6d38e9b Thu, 01 Jan 1970 00:00:00 +0000 test $
951 972 bar
952 973
953 974 Test restricted mode with transplant -b
954 975
955 976 $ hg update 6
956 977 1 files updated, 0 files merged, 0 files removed, 0 files unresolved
957 978 $ hg branch foo
958 979 marked working directory as branch foo
959 980 $ mv a a.bak
960 981 $ echo foobranch > a
961 982 $ cat a.bak >> a
962 983 $ rm a.bak
963 984 $ hg commit -m 9foobranch
964 985 $ hg update default
965 986 2 files updated, 0 files merged, 0 files removed, 0 files unresolved
966 987 $ hg -y transplant -b foo tip
967 988 applying 4aa30d025d50
968 989 4aa30d025d50 transplanted to 5a4da427c162
969 990
970 991 Expansion in changeset but not in file
971 992
972 993 $ hg tip -p
973 994 changeset: 11:5a4da427c162
974 995 tag: tip
975 996 parent: 9:41efa6d38e9b
976 997 user: test
977 998 date: Thu Jan 01 00:00:00 1970 +0000
978 999 summary: 9foobranch
979 1000
980 1001 diff -r 41efa6d38e9b -r 5a4da427c162 a
981 1002 --- a/a Thu Jan 01 00:00:00 1970 +0000
982 1003 +++ b/a Thu Jan 01 00:00:00 1970 +0000
983 1004 @@ -1,3 +1,4 @@
984 1005 +foobranch
985 1006 expand $Id$
986 1007 do not process $Id:
987 1008 xxx $
988 1009
989 1010 $ head -n 2 a
990 1011 foobranch
991 1012 expand $Id: a 5a4da427c162 Thu, 01 Jan 1970 00:00:00 +0000 test $
992 1013
993 1014 Turn off expansion
994 1015
995 1016 $ hg -q rollback
996 1017 $ hg -q update -C
997 1018
998 1019 kwshrink with unknown file u
999 1020
1000 1021 $ cp a u
1001 1022 $ hg --verbose kwshrink
1002 1023 overwriting a shrinking keywords
1003 1024 overwriting m shrinking keywords
1004 1025 overwriting x/a shrinking keywords
1005 1026
1006 1027 Keywords shrunk in working directory, but not yet disabled
1007 1028 - cat shows unexpanded keywords
1008 1029 - hg cat shows expanded keywords
1009 1030
1010 1031 $ cat a b
1011 1032 expand $Id$
1012 1033 do not process $Id:
1013 1034 xxx $
1014 1035 $Xinfo$
1015 1036 ignore $Id$
1016 1037 $ hg cat sym a b && echo
1017 1038 expand $Id: a bb948857c743 Thu, 01 Jan 1970 00:00:02 +0000 user $
1018 1039 do not process $Id:
1019 1040 xxx $
1020 1041 $Xinfo: User Name <user@example.com>: firstline $
1021 1042 ignore $Id$
1022 1043 a
1023 1044
1024 1045 Now disable keyword expansion
1025 1046
1026 1047 $ rm "$HGRCPATH"
1027 1048 $ cat a b
1028 1049 expand $Id$
1029 1050 do not process $Id:
1030 1051 xxx $
1031 1052 $Xinfo$
1032 1053 ignore $Id$
1033 1054 $ hg cat sym a b && echo
1034 1055 expand $Id$
1035 1056 do not process $Id:
1036 1057 xxx $
1037 1058 $Xinfo$
1038 1059 ignore $Id$
1039 1060 a
General Comments 0
You need to be logged in to leave comments. Login now