Mercurial > hg
comparison hgext/keyword.py @ 11045:eb67196d20fa
keyword: support (q)record
Monkeypatch hgext.dorecord to trigger keyword expansion.
Read data from working directory, not from filelog.
Prevent keyword expansion from within record's commitfunc,
thereby fixing a bug/inconsistency where files which are clean
after recording were overwritten twice.
author | Christian Ebert <blacktrash@gmx.net> |
---|---|
date | Mon, 26 Apr 2010 04:01:07 +0200 |
parents | 5ab414f71e87 |
children | dc2f37864348 |
comparison
equal
deleted
inserted
replaced
11044:5ab414f71e87 | 11045:eb67196d20fa |
---|---|
63 the risk of inadvertently storing expanded keywords in the change | 63 the risk of inadvertently storing expanded keywords in the change |
64 history. | 64 history. |
65 | 65 |
66 To force expansion after enabling it, or a configuration change, run | 66 To force expansion after enabling it, or a configuration change, run |
67 :hg:`kwexpand`. | 67 :hg:`kwexpand`. |
68 | |
69 Also, when committing with the record extension or using mq's qrecord, | |
70 be aware that keywords cannot be updated. Again, run :hg:`kwexpand` on | |
71 the files in question to update keyword expansions after all changes | |
72 have been checked in. | |
73 | 68 |
74 Expansions spanning more than one line and incremental expansions, | 69 Expansions spanning more than one line and incremental expansions, |
75 like CVS' $Log$, are not supported. A keyword template map "Log = | 70 like CVS' $Log$, are not supported. A keyword template map "Log = |
76 {desc}" expands to the first line of the changeset description. | 71 {desc}" expands to the first line of the changeset description. |
77 ''' | 72 ''' |
91 ' convert email glog') | 86 ' convert email glog') |
92 | 87 |
93 # hg commands that trigger expansion only when writing to working dir, | 88 # hg commands that trigger expansion only when writing to working dir, |
94 # not when reading filelog, and unexpand when reading from working dir | 89 # not when reading filelog, and unexpand when reading from working dir |
95 restricted = 'merge record qrecord resolve transplant' | 90 restricted = 'merge record qrecord resolve transplant' |
91 | |
92 # commands using dorecord | |
93 recordcommands = 'record qrecord' | |
96 | 94 |
97 # provide cvs-like UTC date filter | 95 # provide cvs-like UTC date filter |
98 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S') | 96 utcdate = lambda x: util.datestr((x[0], 0), '%Y/%m/%d %H:%M:%S') |
99 | 97 |
100 # make keyword tools accessible | 98 # make keyword tools accessible |
122 self.ui = ui | 120 self.ui = ui |
123 self.repo = repo | 121 self.repo = repo |
124 self.match = match.match(repo.root, '', [], | 122 self.match = match.match(repo.root, '', [], |
125 kwtools['inc'], kwtools['exc']) | 123 kwtools['inc'], kwtools['exc']) |
126 self.restrict = kwtools['hgcmd'] in restricted.split() | 124 self.restrict = kwtools['hgcmd'] in restricted.split() |
125 self.record = kwtools['hgcmd'] in recordcommands.split() | |
127 | 126 |
128 kwmaps = self.ui.configitems('keywordmaps') | 127 kwmaps = self.ui.configitems('keywordmaps') |
129 if kwmaps: # override default templates | 128 if kwmaps: # override default templates |
130 self.templates = dict((k, templater.parsestring(v, False)) | 129 self.templates = dict((k, templater.parsestring(v, False)) |
131 for k, v in kwmaps) | 130 for k, v in kwmaps) |
159 '''Returns true if path matches [keyword] pattern | 158 '''Returns true if path matches [keyword] pattern |
160 and is not a symbolic link. | 159 and is not a symbolic link. |
161 Caveat: localrepository._link fails on Windows.''' | 160 Caveat: localrepository._link fails on Windows.''' |
162 return self.match(path) and not 'l' in flagfunc(path) | 161 return self.match(path) and not 'l' in flagfunc(path) |
163 | 162 |
164 def overwrite(self, node, expand, candidates): | 163 def overwrite(self, node, expand, candidates, recctx=None): |
165 '''Overwrites selected files expanding/shrinking keywords.''' | 164 '''Overwrites selected files expanding/shrinking keywords.''' |
166 ctx = self.repo[node] | 165 if recctx is None: |
166 ctx = self.repo[node] | |
167 else: | |
168 ctx = recctx | |
167 mf = ctx.manifest() | 169 mf = ctx.manifest() |
168 if node is not None: # commit | 170 if node is not None: # commit, record |
169 candidates = [f for f in ctx.files() if f in mf] | 171 candidates = [f for f in ctx.files() if f in mf] |
170 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)] | 172 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)] |
171 if candidates: | 173 if candidates: |
172 self.restrict = True # do not expand when reading | 174 self.restrict = True # do not expand when reading |
173 msg = (expand and _('overwriting %s expanding keywords\n') | 175 msg = (expand and _('overwriting %s expanding keywords\n') |
174 or _('overwriting %s shrinking keywords\n')) | 176 or _('overwriting %s shrinking keywords\n')) |
175 for f in candidates: | 177 for f in candidates: |
176 fp = self.repo.file(f) | 178 if recctx is None: |
177 data = fp.read(mf[f]) | 179 data = self.repo.file(f).read(mf[f]) |
180 else: | |
181 data = self.repo.wread(f) | |
178 if util.binary(data): | 182 if util.binary(data): |
179 continue | 183 continue |
180 if expand: | 184 if expand: |
181 if node is None: | 185 if node is None: |
182 ctx = self.repo.filectx(f, fileid=mf[f]).changectx() | 186 ctx = self.repo.filectx(f, fileid=mf[f]).changectx() |
463 del self.commitctx | 467 del self.commitctx |
464 | 468 |
465 def kwcommitctx(self, ctx, error=False): | 469 def kwcommitctx(self, ctx, error=False): |
466 n = super(kwrepo, self).commitctx(ctx, error) | 470 n = super(kwrepo, self).commitctx(ctx, error) |
467 # no lock needed, only called from repo.commit() which already locks | 471 # no lock needed, only called from repo.commit() which already locks |
468 kwt.overwrite(n, True, None) | 472 if not kwt.record: |
473 kwt.overwrite(n, True, None) | |
469 return n | 474 return n |
470 | 475 |
471 # monkeypatches | 476 # monkeypatches |
472 def kwpatchfile_init(orig, self, ui, fname, opener, | 477 def kwpatchfile_init(orig, self, ui, fname, opener, |
473 missing=False, eolmode=None): | 478 missing=False, eolmode=None): |
490 def kwweb_skip(orig, web, req, tmpl): | 495 def kwweb_skip(orig, web, req, tmpl): |
491 '''Wraps webcommands.x turning off keyword expansion.''' | 496 '''Wraps webcommands.x turning off keyword expansion.''' |
492 kwt.match = util.never | 497 kwt.match = util.never |
493 return orig(web, req, tmpl) | 498 return orig(web, req, tmpl) |
494 | 499 |
500 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): | |
501 '''Wraps record.dorecord expanding keywords after recording.''' | |
502 wlock = repo.wlock() | |
503 try: | |
504 # record returns 0 even when nothing has changed | |
505 # therefore compare nodes before and after | |
506 ctx = repo['.'] | |
507 ret = orig(ui, repo, commitfunc, *pats, **opts) | |
508 recctx = repo['.'] | |
509 if ctx != recctx: | |
510 kwt.overwrite('.', True, None, recctx) | |
511 return ret | |
512 finally: | |
513 wlock.release() | |
514 | |
495 repo.__class__ = kwrepo | 515 repo.__class__ = kwrepo |
496 | 516 |
497 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) | 517 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
498 if not kwt.restrict: | 518 if not kwt.restrict: |
499 extensions.wrapfunction(patch, 'diff', kw_diff) | 519 extensions.wrapfunction(patch, 'diff', kw_diff) |
500 for c in 'annotate changeset rev filediff diff'.split(): | 520 for c in 'annotate changeset rev filediff diff'.split(): |
501 extensions.wrapfunction(webcommands, c, kwweb_skip) | 521 extensions.wrapfunction(webcommands, c, kwweb_skip) |
522 try: | |
523 record = extensions.find('record') | |
524 extensions.wrapfunction(record, 'dorecord', kw_dorecord) | |
525 except KeyError: | |
526 pass | |
502 | 527 |
503 cmdtable = { | 528 cmdtable = { |
504 'kwdemo': | 529 'kwdemo': |
505 (demo, | 530 (demo, |
506 [('d', 'default', None, _('show default keyword template maps')), | 531 [('d', 'default', None, _('show default keyword template maps')), |