90 |
90 |
91 commands.optionalrepo += ' kwdemo' |
91 commands.optionalrepo += ' kwdemo' |
92 |
92 |
93 # hg commands that do not act on keywords |
93 # hg commands that do not act on keywords |
94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init' |
94 nokwcommands = ('add addremove annotate bundle copy export grep incoming init' |
95 ' log outgoing push rename rollback tip verify' |
95 ' log outgoing push rename tip verify convert email glog') |
96 ' convert email glog') |
|
97 |
96 |
98 # hg commands that trigger expansion only when writing to working dir, |
97 # hg commands that trigger expansion only when writing to working dir, |
99 # not when reading filelog, and unexpand when reading from working dir |
98 # not when reading filelog, and unexpand when reading from working dir |
100 restricted = 'merge record qrecord resolve transplant' |
99 restricted = 'merge record qrecord resolve transplant' |
101 |
100 |
190 '''Returns true if path matches [keyword] pattern |
189 '''Returns true if path matches [keyword] pattern |
191 and is not a symbolic link. |
190 and is not a symbolic link. |
192 Caveat: localrepository._link fails on Windows.''' |
191 Caveat: localrepository._link fails on Windows.''' |
193 return self.match(path) and not 'l' in flagfunc(path) |
192 return self.match(path) and not 'l' in flagfunc(path) |
194 |
193 |
195 def overwrite(self, ctx, candidates, iswctx, expand): |
194 def overwrite(self, ctx, candidates, iswctx, expand, cfiles): |
196 '''Overwrites selected files expanding/shrinking keywords.''' |
195 '''Overwrites selected files expanding/shrinking keywords.''' |
197 if self.record: |
196 if cfiles is not None: |
198 candidates = [f for f in ctx.files() if f in ctx] |
197 candidates = [f for f in candidates if f in cfiles] |
199 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)] |
198 candidates = [f for f in candidates if self.iskwfile(f, ctx.flags)] |
200 if candidates: |
199 if candidates: |
|
200 restrict = self.restrict |
201 self.restrict = True # do not expand when reading |
201 self.restrict = True # do not expand when reading |
|
202 rollback = kwtools['hgcmd'] == 'rollback' |
202 mf = ctx.manifest() |
203 mf = ctx.manifest() |
203 msg = (expand and _('overwriting %s expanding keywords\n') |
204 msg = (expand and _('overwriting %s expanding keywords\n') |
204 or _('overwriting %s shrinking keywords\n')) |
205 or _('overwriting %s shrinking keywords\n')) |
205 for f in candidates: |
206 for f in candidates: |
206 if not self.record: |
207 if not self.record and not rollback: |
207 data = self.repo.file(f).read(mf[f]) |
208 data = self.repo.file(f).read(mf[f]) |
208 else: |
209 else: |
209 data = self.repo.wread(f) |
210 data = self.repo.wread(f) |
210 if util.binary(data): |
211 if util.binary(data): |
211 continue |
212 continue |
217 else: |
218 else: |
218 found = self.re_kw.search(data) |
219 found = self.re_kw.search(data) |
219 if found: |
220 if found: |
220 self.ui.note(msg % f) |
221 self.ui.note(msg % f) |
221 self.repo.wwrite(f, data, mf.flags(f)) |
222 self.repo.wwrite(f, data, mf.flags(f)) |
222 if iswctx: |
223 if iswctx and not rollback: |
223 self.repo.dirstate.normal(f) |
224 self.repo.dirstate.normal(f) |
224 elif self.record: |
225 elif self.record: |
225 self.repo.dirstate.normallookup(f) |
226 self.repo.dirstate.normallookup(f) |
226 self.restrict = False |
227 self.restrict = restrict |
227 |
228 |
228 def shrinktext(self, text): |
229 def shrinktext(self, text): |
229 '''Unconditionally removes all keyword substitutions from text.''' |
230 '''Unconditionally removes all keyword substitutions from text.''' |
230 return self.re_kw.sub(r'$\1$', text) |
231 return self.re_kw.sub(r'$\1$', text) |
231 |
232 |
296 try: |
297 try: |
297 status = _status(ui, repo, kwt, *pats, **opts) |
298 status = _status(ui, repo, kwt, *pats, **opts) |
298 modified, added, removed, deleted, unknown, ignored, clean = status |
299 modified, added, removed, deleted, unknown, ignored, clean = status |
299 if modified or added or removed or deleted: |
300 if modified or added or removed or deleted: |
300 raise util.Abort(_('outstanding uncommitted changes')) |
301 raise util.Abort(_('outstanding uncommitted changes')) |
301 kwt.overwrite(wctx, clean, True, expand) |
302 kwt.overwrite(wctx, clean, True, expand, None) |
302 finally: |
303 finally: |
303 wlock.release() |
304 wlock.release() |
304 |
305 |
305 def demo(ui, repo, *args, **opts): |
306 def demo(ui, repo, *args, **opts): |
306 '''print [keywordmaps] configuration and an expansion example |
307 '''print [keywordmaps] configuration and an expansion example |
500 def kwcommitctx(self, ctx, error=False): |
501 def kwcommitctx(self, ctx, error=False): |
501 n = super(kwrepo, self).commitctx(ctx, error) |
502 n = super(kwrepo, self).commitctx(ctx, error) |
502 # no lock needed, only called from repo.commit() which already locks |
503 # no lock needed, only called from repo.commit() which already locks |
503 if not kwt.record: |
504 if not kwt.record: |
504 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()), |
505 kwt.overwrite(self[n], sorted(ctx.added() + ctx.modified()), |
505 False, True) |
506 False, True, None) |
506 return n |
507 return n |
|
508 |
|
509 def rollback(self, dryrun=False): |
|
510 wlock = repo.wlock() |
|
511 try: |
|
512 if not dryrun: |
|
513 cfiles = self['.'].files() |
|
514 ret = super(kwrepo, self).rollback(dryrun) |
|
515 if not dryrun: |
|
516 ctx = self['.'] |
|
517 modified, added = super(kwrepo, self).status()[:2] |
|
518 kwt.overwrite(ctx, added, True, False, cfiles) |
|
519 kwt.overwrite(ctx, modified, True, True, cfiles) |
|
520 return ret |
|
521 finally: |
|
522 wlock.release() |
507 |
523 |
508 # monkeypatches |
524 # monkeypatches |
509 def kwpatchfile_init(orig, self, ui, fname, opener, |
525 def kwpatchfile_init(orig, self, ui, fname, opener, |
510 missing=False, eolmode=None): |
526 missing=False, eolmode=None): |
511 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
527 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
514 # shrink keywords read from working dir |
530 # shrink keywords read from working dir |
515 self.lines = kwt.shrinklines(self.fname, self.lines) |
531 self.lines = kwt.shrinklines(self.fname, self.lines) |
516 |
532 |
517 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None, |
533 def kw_diff(orig, repo, node1=None, node2=None, match=None, changes=None, |
518 opts=None, prefix=''): |
534 opts=None, prefix=''): |
519 '''Monkeypatch patch.diff to avoid expansion except when |
535 '''Monkeypatch patch.diff to avoid expansion.''' |
520 comparing against working dir.''' |
536 kwt.restrict = True |
521 if node2 is not None: |
|
522 kwt.match = util.never |
|
523 elif node1 is not None and node1 != repo['.'].node(): |
|
524 kwt.restrict = True |
|
525 return orig(repo, node1, node2, match, changes, opts, prefix) |
537 return orig(repo, node1, node2, match, changes, opts, prefix) |
526 |
538 |
527 def kwweb_skip(orig, web, req, tmpl): |
539 def kwweb_skip(orig, web, req, tmpl): |
528 '''Wraps webcommands.x turning off keyword expansion.''' |
540 '''Wraps webcommands.x turning off keyword expansion.''' |
529 kwt.match = util.never |
541 kwt.match = util.never |
537 # therefore compare nodes before and after |
549 # therefore compare nodes before and after |
538 ctx = repo['.'] |
550 ctx = repo['.'] |
539 ret = orig(ui, repo, commitfunc, *pats, **opts) |
551 ret = orig(ui, repo, commitfunc, *pats, **opts) |
540 recordctx = repo['.'] |
552 recordctx = repo['.'] |
541 if ctx != recordctx: |
553 if ctx != recordctx: |
542 kwt.overwrite(recordctx, None, False, True) |
554 kwt.overwrite(recordctx, recordctx.files(), |
|
555 False, True, recordctx) |
543 return ret |
556 return ret |
544 finally: |
557 finally: |
545 wlock.release() |
558 wlock.release() |
546 |
559 |
547 repo.__class__ = kwrepo |
560 repo.__class__ = kwrepo |
548 |
561 |
549 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
562 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
550 if not kwt.restrict: |
563 extensions.wrapfunction(patch, 'diff', kw_diff) |
551 extensions.wrapfunction(patch, 'diff', kw_diff) |
|
552 for c in 'annotate changeset rev filediff diff'.split(): |
564 for c in 'annotate changeset rev filediff diff'.split(): |
553 extensions.wrapfunction(webcommands, c, kwweb_skip) |
565 extensions.wrapfunction(webcommands, c, kwweb_skip) |
554 for name in recordextensions.split(): |
566 for name in recordextensions.split(): |
555 try: |
567 try: |
556 record = extensions.find(name) |
568 record = extensions.find(name) |