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 ''' |
90 ' log outgoing push rename rollback tip verify' |
85 ' log outgoing push rename rollback tip verify' |
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 resolve qfold qimport qnew qpush qrefresh qrecord' |
90 restricted = 'merge record qrecord resolve transplant' |
96 ' transplant') |
91 |
|
92 # commands using dorecord |
|
93 recordcommands = 'record qrecord' |
97 |
94 |
98 # provide cvs-like UTC date filter |
95 # provide cvs-like UTC date filter |
99 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') |
100 |
97 |
101 # make keyword tools accessible |
98 # make keyword tools accessible |
123 self.ui = ui |
120 self.ui = ui |
124 self.repo = repo |
121 self.repo = repo |
125 self.match = match.match(repo.root, '', [], |
122 self.match = match.match(repo.root, '', [], |
126 kwtools['inc'], kwtools['exc']) |
123 kwtools['inc'], kwtools['exc']) |
127 self.restrict = kwtools['hgcmd'] in restricted.split() |
124 self.restrict = kwtools['hgcmd'] in restricted.split() |
|
125 self.record = kwtools['hgcmd'] in recordcommands.split() |
128 |
126 |
129 kwmaps = self.ui.configitems('keywordmaps') |
127 kwmaps = self.ui.configitems('keywordmaps') |
130 if kwmaps: # override default templates |
128 if kwmaps: # override default templates |
131 self.templates = dict((k, templater.parsestring(v, False)) |
129 self.templates = dict((k, templater.parsestring(v, False)) |
132 for k, v in kwmaps) |
130 for k, v in kwmaps) |
160 '''Returns true if path matches [keyword] pattern |
158 '''Returns true if path matches [keyword] pattern |
161 and is not a symbolic link. |
159 and is not a symbolic link. |
162 Caveat: localrepository._link fails on Windows.''' |
160 Caveat: localrepository._link fails on Windows.''' |
163 return self.match(path) and not 'l' in flagfunc(path) |
161 return self.match(path) and not 'l' in flagfunc(path) |
164 |
162 |
165 def overwrite(self, node, expand, candidates): |
163 def overwrite(self, node, expand, candidates, recctx=None): |
166 '''Overwrites selected files expanding/shrinking keywords.''' |
164 '''Overwrites selected files expanding/shrinking keywords.''' |
167 ctx = self.repo[node] |
165 if recctx is None: |
|
166 ctx = self.repo[node] |
|
167 else: |
|
168 ctx = recctx |
168 mf = ctx.manifest() |
169 mf = ctx.manifest() |
169 if node is not None: # commit |
170 if node is not None: # commit, record |
170 candidates = [f for f in ctx.files() if f in mf] |
171 candidates = [f for f in ctx.files() if f in mf] |
171 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)] |
172 if candidates: |
173 if candidates: |
173 self.restrict = True # do not expand when reading |
174 self.restrict = True # do not expand when reading |
174 msg = (expand and _('overwriting %s expanding keywords\n') |
175 msg = (expand and _('overwriting %s expanding keywords\n') |
175 or _('overwriting %s shrinking keywords\n')) |
176 or _('overwriting %s shrinking keywords\n')) |
176 for f in candidates: |
177 for f in candidates: |
177 fp = self.repo.file(f) |
178 if recctx is None: |
178 data = fp.read(mf[f]) |
179 data = self.repo.file(f).read(mf[f]) |
|
180 else: |
|
181 data = self.repo.wread(f) |
179 if util.binary(data): |
182 if util.binary(data): |
180 continue |
183 continue |
181 if expand: |
184 if expand: |
182 if node is None: |
185 if node is None: |
183 ctx = self.repo.filectx(f, fileid=mf[f]).changectx() |
186 ctx = self.repo.filectx(f, fileid=mf[f]).changectx() |
464 del self.commitctx |
467 del self.commitctx |
465 |
468 |
466 def kwcommitctx(self, ctx, error=False): |
469 def kwcommitctx(self, ctx, error=False): |
467 n = super(kwrepo, self).commitctx(ctx, error) |
470 n = super(kwrepo, self).commitctx(ctx, error) |
468 # no lock needed, only called from repo.commit() which already locks |
471 # no lock needed, only called from repo.commit() which already locks |
469 kwt.overwrite(n, True, None) |
472 if not kwt.record: |
|
473 kwt.overwrite(n, True, None) |
470 return n |
474 return n |
471 |
475 |
472 # monkeypatches |
476 # monkeypatches |
473 def kwpatchfile_init(orig, self, ui, fname, opener, |
477 def kwpatchfile_init(orig, self, ui, fname, opener, |
474 missing=False, eolmode=None): |
478 missing=False, eolmode=None): |
491 def kwweb_skip(orig, web, req, tmpl): |
495 def kwweb_skip(orig, web, req, tmpl): |
492 '''Wraps webcommands.x turning off keyword expansion.''' |
496 '''Wraps webcommands.x turning off keyword expansion.''' |
493 kwt.match = util.never |
497 kwt.match = util.never |
494 return orig(web, req, tmpl) |
498 return orig(web, req, tmpl) |
495 |
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 |
496 repo.__class__ = kwrepo |
515 repo.__class__ = kwrepo |
497 |
516 |
498 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
517 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
499 if not kwt.restrict: |
518 if not kwt.restrict: |
500 extensions.wrapfunction(patch, 'diff', kw_diff) |
519 extensions.wrapfunction(patch, 'diff', kw_diff) |
501 for c in 'annotate changeset rev filediff diff'.split(): |
520 for c in 'annotate changeset rev filediff diff'.split(): |
502 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 |
503 |
527 |
504 cmdtable = { |
528 cmdtable = { |
505 'kwdemo': |
529 'kwdemo': |
506 (demo, |
530 (demo, |
507 [('d', 'default', None, _('show default keyword template maps')), |
531 [('d', 'default', None, _('show default keyword template maps')), |