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)