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