575 kwshrink refuses to run if given files contain local changes. |
575 kwshrink refuses to run if given files contain local changes. |
576 ''' |
576 ''' |
577 # 3rd argument sets expansion to False |
577 # 3rd argument sets expansion to False |
578 _kwfwrite(ui, repo, False, *pats, **opts) |
578 _kwfwrite(ui, repo, False, *pats, **opts) |
579 |
579 |
|
580 # monkeypatches |
|
581 |
|
582 def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None): |
|
583 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
|
584 rejects or conflicts due to expanded keywords in working dir.''' |
|
585 orig(self, ui, gp, backend, store, eolmode) |
|
586 kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None) |
|
587 if kwt: |
|
588 # shrink keywords read from working dir |
|
589 self.lines = kwt.shrinklines(self.fname, self.lines) |
|
590 |
|
591 def kwdiff(orig, repo, *args, **kwargs): |
|
592 '''Monkeypatch patch.diff to avoid expansion.''' |
|
593 kwt = getattr(repo, '_keywordkwt', None) |
|
594 if kwt: |
|
595 restrict = kwt.restrict |
|
596 kwt.restrict = True |
|
597 try: |
|
598 for chunk in orig(repo, *args, **kwargs): |
|
599 yield chunk |
|
600 finally: |
|
601 if kwt: |
|
602 kwt.restrict = restrict |
|
603 |
|
604 def kwweb_skip(orig, web, req, tmpl): |
|
605 '''Wraps webcommands.x turning off keyword expansion.''' |
|
606 kwt = getattr(web.repo, '_keywordkwt', None) |
|
607 if kwt: |
|
608 origmatch = kwt.match |
|
609 kwt.match = util.never |
|
610 try: |
|
611 for chunk in orig(web, req, tmpl): |
|
612 yield chunk |
|
613 finally: |
|
614 if kwt: |
|
615 kwt.match = origmatch |
|
616 |
|
617 def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts): |
|
618 '''Wraps cmdutil.amend expanding keywords after amend.''' |
|
619 kwt = getattr(repo, '_keywordkwt', None) |
|
620 if kwt is None: |
|
621 return orig(ui, repo, commitfunc, old, extra, pats, opts) |
|
622 with repo.wlock(): |
|
623 kwt.postcommit = True |
|
624 newid = orig(ui, repo, commitfunc, old, extra, pats, opts) |
|
625 if newid != old.node(): |
|
626 ctx = repo[newid] |
|
627 kwt.restrict = True |
|
628 kwt.overwrite(ctx, ctx.files(), False, True) |
|
629 kwt.restrict = False |
|
630 return newid |
|
631 |
|
632 def kw_copy(orig, ui, repo, pats, opts, rename=False): |
|
633 '''Wraps cmdutil.copy so that copy/rename destinations do not |
|
634 contain expanded keywords. |
|
635 Note that the source of a regular file destination may also be a |
|
636 symlink: |
|
637 hg cp sym x -> x is symlink |
|
638 cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords) |
|
639 For the latter we have to follow the symlink to find out whether its |
|
640 target is configured for expansion and we therefore must unexpand the |
|
641 keywords in the destination.''' |
|
642 kwt = getattr(repo, '_keywordkwt', None) |
|
643 if kwt is None: |
|
644 return orig(ui, repo, pats, opts, rename) |
|
645 with repo.wlock(): |
|
646 orig(ui, repo, pats, opts, rename) |
|
647 if opts.get('dry_run'): |
|
648 return |
|
649 wctx = repo[None] |
|
650 cwd = repo.getcwd() |
|
651 |
|
652 def haskwsource(dest): |
|
653 '''Returns true if dest is a regular file and configured for |
|
654 expansion or a symlink which points to a file configured for |
|
655 expansion. ''' |
|
656 source = repo.dirstate.copied(dest) |
|
657 if 'l' in wctx.flags(source): |
|
658 source = pathutil.canonpath(repo.root, cwd, |
|
659 os.path.realpath(source)) |
|
660 return kwt.match(source) |
|
661 |
|
662 candidates = [f for f in repo.dirstate.copies() if |
|
663 'l' not in wctx.flags(f) and haskwsource(f)] |
|
664 kwt.overwrite(wctx, candidates, False, False) |
|
665 |
|
666 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): |
|
667 '''Wraps record.dorecord expanding keywords after recording.''' |
|
668 kwt = getattr(repo, '_keywordkwt', None) |
|
669 if kwt is None: |
|
670 return orig(ui, repo, commitfunc, *pats, **opts) |
|
671 with repo.wlock(): |
|
672 # record returns 0 even when nothing has changed |
|
673 # therefore compare nodes before and after |
|
674 kwt.postcommit = True |
|
675 ctx = repo['.'] |
|
676 wstatus = ctx.status() |
|
677 ret = orig(ui, repo, commitfunc, *pats, **opts) |
|
678 recctx = repo['.'] |
|
679 if ctx != recctx: |
|
680 modified, added = _preselect(wstatus, recctx.files()) |
|
681 kwt.restrict = False |
|
682 kwt.overwrite(recctx, modified, False, True) |
|
683 kwt.overwrite(recctx, added, False, True, True) |
|
684 kwt.restrict = True |
|
685 return ret |
|
686 |
|
687 def kwfilectx_cmp(orig, self, fctx): |
|
688 if fctx._customcmp: |
|
689 return fctx.cmp(self) |
|
690 kwt = getattr(self._repo, '_keywordkwt', None) |
|
691 if kwt is None: |
|
692 return orig(self, fctx) |
|
693 # keyword affects data size, comparing wdir and filelog size does |
|
694 # not make sense |
|
695 if (fctx._filenode is None and |
|
696 (self._repo._encodefilterpats or |
|
697 kwt.match(fctx.path()) and 'l' not in fctx.flags() or |
|
698 self.size() - 4 == fctx.size()) or |
|
699 self.size() == fctx.size()): |
|
700 return self._filelog.cmp(self._filenode, fctx.data()) |
|
701 return True |
580 |
702 |
581 def uisetup(ui): |
703 def uisetup(ui): |
582 ''' Monkeypatches dispatch._parse to retrieve user command.''' |
704 ''' Monkeypatches dispatch._parse to retrieve user command. |
|
705 Overrides file method to return kwfilelog instead of filelog |
|
706 if file matches user configuration. |
|
707 Wraps commit to overwrite configured files with updated |
|
708 keyword substitutions. |
|
709 Monkeypatches patch and webcommands.''' |
583 |
710 |
584 def kwdispatch_parse(orig, ui, args): |
711 def kwdispatch_parse(orig, ui, args): |
585 '''Monkeypatch dispatch._parse to obtain running hg command.''' |
712 '''Monkeypatch dispatch._parse to obtain running hg command.''' |
586 cmd, func, args, options, cmdoptions = orig(ui, args) |
713 cmd, func, args, options, cmdoptions = orig(ui, args) |
587 kwtools['hgcmd'] = cmd |
714 kwtools['hgcmd'] = cmd |
588 return cmd, func, args, options, cmdoptions |
715 return cmd, func, args, options, cmdoptions |
589 |
716 |
590 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse) |
717 extensions.wrapfunction(dispatch, '_parse', kwdispatch_parse) |
591 |
718 |
|
719 extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp) |
|
720 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
|
721 extensions.wrapfunction(patch, 'diff', kwdiff) |
|
722 extensions.wrapfunction(cmdutil, 'amend', kw_amend) |
|
723 extensions.wrapfunction(cmdutil, 'copy', kw_copy) |
|
724 extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord) |
|
725 for c in nokwwebcommands.split(): |
|
726 extensions.wrapfunction(webcommands, c, kwweb_skip) |
|
727 |
592 def reposetup(ui, repo): |
728 def reposetup(ui, repo): |
593 '''Sets up repo as kwrepo for keyword substitution. |
729 '''Sets up repo as kwrepo for keyword substitution.''' |
594 Overrides file method to return kwfilelog instead of filelog |
|
595 if file matches user configuration. |
|
596 Wraps commit to overwrite configured files with updated |
|
597 keyword substitutions. |
|
598 Monkeypatches patch and webcommands.''' |
|
599 |
730 |
600 try: |
731 try: |
601 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split() |
732 if (not repo.local() or kwtools['hgcmd'] in nokwcommands.split() |
602 or '.hg' in util.splitpath(repo.root) |
733 or '.hg' in util.splitpath(repo.root) |
603 or repo._url.startswith('bundle:')): |
734 or repo._url.startswith('bundle:')): |
663 finally: |
794 finally: |
664 kwt.restrict = origrestrict |
795 kwt.restrict = origrestrict |
665 |
796 |
666 repo.__class__ = kwrepo |
797 repo.__class__ = kwrepo |
667 repo._keywordkwt = kwt |
798 repo._keywordkwt = kwt |
668 |
|
669 # monkeypatches |
|
670 def kwpatchfile_init(orig, self, ui, gp, backend, store, eolmode=None): |
|
671 '''Monkeypatch/wrap patch.patchfile.__init__ to avoid |
|
672 rejects or conflicts due to expanded keywords in working dir.''' |
|
673 orig(self, ui, gp, backend, store, eolmode) |
|
674 kwt = getattr(getattr(backend, 'repo', None), '_keywordkwt', None) |
|
675 if kwt: |
|
676 # shrink keywords read from working dir |
|
677 self.lines = kwt.shrinklines(self.fname, self.lines) |
|
678 |
|
679 def kwdiff(orig, repo, *args, **kwargs): |
|
680 '''Monkeypatch patch.diff to avoid expansion.''' |
|
681 kwt = getattr(repo, '_keywordkwt', None) |
|
682 if kwt: |
|
683 restrict = kwt.restrict |
|
684 kwt.restrict = True |
|
685 try: |
|
686 for chunk in orig(repo, *args, **kwargs): |
|
687 yield chunk |
|
688 finally: |
|
689 if kwt: |
|
690 kwt.restrict = restrict |
|
691 |
|
692 def kwweb_skip(orig, web, req, tmpl): |
|
693 '''Wraps webcommands.x turning off keyword expansion.''' |
|
694 kwt = getattr(web.repo, '_keywordkwt', None) |
|
695 if kwt: |
|
696 origmatch = kwt.match |
|
697 kwt.match = util.never |
|
698 try: |
|
699 for chunk in orig(web, req, tmpl): |
|
700 yield chunk |
|
701 finally: |
|
702 if kwt: |
|
703 kwt.match = origmatch |
|
704 |
|
705 def kw_amend(orig, ui, repo, commitfunc, old, extra, pats, opts): |
|
706 '''Wraps cmdutil.amend expanding keywords after amend.''' |
|
707 kwt = getattr(repo, '_keywordkwt', None) |
|
708 if kwt is None: |
|
709 return orig(ui, repo, commitfunc, old, extra, pats, opts) |
|
710 with repo.wlock(): |
|
711 kwt.postcommit = True |
|
712 newid = orig(ui, repo, commitfunc, old, extra, pats, opts) |
|
713 if newid != old.node(): |
|
714 ctx = repo[newid] |
|
715 kwt.restrict = True |
|
716 kwt.overwrite(ctx, ctx.files(), False, True) |
|
717 kwt.restrict = False |
|
718 return newid |
|
719 |
|
720 def kw_copy(orig, ui, repo, pats, opts, rename=False): |
|
721 '''Wraps cmdutil.copy so that copy/rename destinations do not |
|
722 contain expanded keywords. |
|
723 Note that the source of a regular file destination may also be a |
|
724 symlink: |
|
725 hg cp sym x -> x is symlink |
|
726 cp sym x; hg cp -A sym x -> x is file (maybe expanded keywords) |
|
727 For the latter we have to follow the symlink to find out whether its |
|
728 target is configured for expansion and we therefore must unexpand the |
|
729 keywords in the destination.''' |
|
730 kwt = getattr(repo, '_keywordkwt', None) |
|
731 if kwt is None: |
|
732 return orig(ui, repo, pats, opts, rename) |
|
733 with repo.wlock(): |
|
734 orig(ui, repo, pats, opts, rename) |
|
735 if opts.get('dry_run'): |
|
736 return |
|
737 wctx = repo[None] |
|
738 cwd = repo.getcwd() |
|
739 |
|
740 def haskwsource(dest): |
|
741 '''Returns true if dest is a regular file and configured for |
|
742 expansion or a symlink which points to a file configured for |
|
743 expansion. ''' |
|
744 source = repo.dirstate.copied(dest) |
|
745 if 'l' in wctx.flags(source): |
|
746 source = pathutil.canonpath(repo.root, cwd, |
|
747 os.path.realpath(source)) |
|
748 return kwt.match(source) |
|
749 |
|
750 candidates = [f for f in repo.dirstate.copies() if |
|
751 'l' not in wctx.flags(f) and haskwsource(f)] |
|
752 kwt.overwrite(wctx, candidates, False, False) |
|
753 |
|
754 def kw_dorecord(orig, ui, repo, commitfunc, *pats, **opts): |
|
755 '''Wraps record.dorecord expanding keywords after recording.''' |
|
756 kwt = getattr(repo, '_keywordkwt', None) |
|
757 if kwt is None: |
|
758 return orig(ui, repo, commitfunc, *pats, **opts) |
|
759 with repo.wlock(): |
|
760 # record returns 0 even when nothing has changed |
|
761 # therefore compare nodes before and after |
|
762 kwt.postcommit = True |
|
763 ctx = repo['.'] |
|
764 wstatus = ctx.status() |
|
765 ret = orig(ui, repo, commitfunc, *pats, **opts) |
|
766 recctx = repo['.'] |
|
767 if ctx != recctx: |
|
768 modified, added = _preselect(wstatus, recctx.files()) |
|
769 kwt.restrict = False |
|
770 kwt.overwrite(recctx, modified, False, True) |
|
771 kwt.overwrite(recctx, added, False, True, True) |
|
772 kwt.restrict = True |
|
773 return ret |
|
774 |
|
775 def kwfilectx_cmp(orig, self, fctx): |
|
776 if fctx._customcmp: |
|
777 return fctx.cmp(self) |
|
778 kwt = getattr(self._repo, '_keywordkwt', None) |
|
779 if kwt is None: |
|
780 return orig(self, fctx) |
|
781 # keyword affects data size, comparing wdir and filelog size does |
|
782 # not make sense |
|
783 if (fctx._filenode is None and |
|
784 (self._repo._encodefilterpats or |
|
785 kwt.match(fctx.path()) and 'l' not in fctx.flags() or |
|
786 self.size() - 4 == fctx.size()) or |
|
787 self.size() == fctx.size()): |
|
788 return self._filelog.cmp(self._filenode, fctx.data()) |
|
789 return True |
|
790 |
|
791 extensions.wrapfunction(context.filectx, 'cmp', kwfilectx_cmp) |
|
792 extensions.wrapfunction(patch.patchfile, '__init__', kwpatchfile_init) |
|
793 extensions.wrapfunction(patch, 'diff', kwdiff) |
|
794 extensions.wrapfunction(cmdutil, 'amend', kw_amend) |
|
795 extensions.wrapfunction(cmdutil, 'copy', kw_copy) |
|
796 extensions.wrapfunction(cmdutil, 'dorecord', kw_dorecord) |
|
797 for c in nokwwebcommands.split(): |
|
798 extensions.wrapfunction(webcommands, c, kwweb_skip) |
|