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')), |