Mercurial > public > mercurial-scm > hg-stable
comparison mercurial/context.py @ 15337:cf5f9df6406b stable
windows: recompute flags when committing a merge (issue1802)
Before this patch, Windows always did the wrong thing with exec bits
when committing a merge: consult the flags in first parent.
Now we manually recompute the result of merging flags at commit time,
which almost always does the right thing (except when there are
conflicts between symlink and exec flags).
To do this, we:
- pull flag synthesis out into its own function
- delay building this function unless it's needed
- add a merge case that compares flags in local and other against the ancestor
This has been tested in multiple ways on Linux:
- running the whole test suite with both old and new code in place,
checking for differences in each flags() result
- running the whole test suite while comparing real on-disk flags
against synthetic ones for merges
- test-issue1802 (from Martin Geisler) which disables exec bit
checking on Unix
author | Matt Mackall <mpm@selenic.com> |
---|---|
date | Sat, 22 Oct 2011 16:12:33 -0500 |
parents | 1c151b963254 |
children | 405ca90df2b1 012b285cf643 |
comparison
equal
deleted
inserted
replaced
15336:83debcd7064b | 15337:cf5f9df6406b |
---|---|
630 return True | 630 return True |
631 | 631 |
632 def __contains__(self, key): | 632 def __contains__(self, key): |
633 return self._repo.dirstate[key] not in "?r" | 633 return self._repo.dirstate[key] not in "?r" |
634 | 634 |
635 def _buildflagfunc(self): | |
636 # Create a fallback function for getting file flags when the | |
637 # filesystem doesn't support them | |
638 | |
639 copiesget = self._repo.dirstate.copies().get | |
640 | |
641 if len(self._parents) < 2: | |
642 # when we have one parent, it's easy: copy from parent | |
643 man = self._parents[0].manifest() | |
644 def func(f): | |
645 f = copiesget(f, f) | |
646 return man.flags(f) | |
647 else: | |
648 # merges are tricky: we try to reconstruct the unstored | |
649 # result from the merge (issue1802) | |
650 p1, p2 = self._parents | |
651 pa = p1.ancestor(p2) | |
652 m1, m2, ma = p1.manifest(), p2.manifest(), pa.manifest() | |
653 | |
654 def func(f): | |
655 f = copiesget(f, f) # may be wrong for merges with copies | |
656 fl1, fl2, fla = m1.flags(f), m2.flags(f), ma.flags(f) | |
657 if fl1 == fl2: | |
658 return fl1 | |
659 if fl1 == fla: | |
660 return fl2 | |
661 if fl2 == fla: | |
662 return fl1 | |
663 return '' # punt for conflicts | |
664 | |
665 return func | |
666 | |
667 @propertycache | |
668 def _flagfunc(self): | |
669 return self._repo.dirstate.flagfunc(self._buildflagfunc) | |
670 | |
635 @propertycache | 671 @propertycache |
636 def _manifest(self): | 672 def _manifest(self): |
637 """generate a manifest corresponding to the working directory""" | 673 """generate a manifest corresponding to the working directory""" |
638 | 674 |
639 if self._unknown is None: | 675 if self._unknown is None: |
640 self.status(unknown=True) | 676 self.status(unknown=True) |
641 | 677 |
642 man = self._parents[0].manifest().copy() | 678 man = self._parents[0].manifest().copy() |
643 copied = self._repo.dirstate.copies() | |
644 if len(self._parents) > 1: | 679 if len(self._parents) > 1: |
645 man2 = self.p2().manifest() | 680 man2 = self.p2().manifest() |
646 def getman(f): | 681 def getman(f): |
647 if f in man: | 682 if f in man: |
648 return man | 683 return man |
649 return man2 | 684 return man2 |
650 else: | 685 else: |
651 getman = lambda f: man | 686 getman = lambda f: man |
652 def cf(f): | 687 |
653 f = copied.get(f, f) | 688 copied = self._repo.dirstate.copies() |
654 return getman(f).flags(f) | 689 ff = self._flagfunc |
655 ff = self._repo.dirstate.flagfunc(cf) | |
656 modified, added, removed, deleted = self._status | 690 modified, added, removed, deleted = self._status |
657 unknown = self._unknown | 691 unknown = self._unknown |
658 for i, l in (("a", added), ("m", modified), ("u", unknown)): | 692 for i, l in (("a", added), ("m", modified), ("u", unknown)): |
659 for f in l: | 693 for f in l: |
660 orig = copied.get(f, f) | 694 orig = copied.get(f, f) |
765 try: | 799 try: |
766 return self._manifest.flags(path) | 800 return self._manifest.flags(path) |
767 except KeyError: | 801 except KeyError: |
768 return '' | 802 return '' |
769 | 803 |
770 orig = self._repo.dirstate.copies().get(path, path) | 804 try: |
771 | 805 return self._flagfunc(path) |
772 def findflag(ctx): | 806 except OSError: |
773 mnode = ctx.changeset()[0] | |
774 node, flag = self._repo.manifest.find(mnode, orig) | |
775 ff = self._repo.dirstate.flagfunc(lambda x: flag or '') | |
776 try: | |
777 return ff(path) | |
778 except OSError: | |
779 pass | |
780 | |
781 flag = findflag(self._parents[0]) | |
782 if flag is None and len(self.parents()) > 1: | |
783 flag = findflag(self._parents[1]) | |
784 if flag is None or self._repo.dirstate[path] == 'r': | |
785 return '' | 807 return '' |
786 return flag | |
787 | 808 |
788 def filectx(self, path, filelog=None): | 809 def filectx(self, path, filelog=None): |
789 """get a file context from the working directory""" | 810 """get a file context from the working directory""" |
790 return workingfilectx(self._repo, path, workingctx=self, | 811 return workingfilectx(self._repo, path, workingctx=self, |
791 filelog=filelog) | 812 filelog=filelog) |