~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/changeset.py

[merge] from robert

 - fix handling of symlinks in tree

 - improved executable bits

 - cache pull over http and test for this

Show diffs side-by-side

added added

removed removed

Lines of Context:
45
45
    return newdict
46
46
 
47
47
       
48
 
class ChangeUnixPermissions(object):
 
48
class ChangeExecFlag(object):
49
49
    """This is two-way change, suitable for file modification, creation,
50
50
    deletion"""
51
 
    def __init__(self, old_mode, new_mode):
52
 
        self.old_mode = old_mode
53
 
        self.new_mode = new_mode
 
51
    def __init__(self, old_exec_flag, new_exec_flag):
 
52
        self.old_exec_flag = old_exec_flag
 
53
        self.new_exec_flag = new_exec_flag
54
54
 
55
55
    def apply(self, filename, conflict_handler, reverse=False):
56
56
        if not reverse:
57
 
            from_mode = self.old_mode
58
 
            to_mode = self.new_mode
 
57
            from_exec_flag = self.old_exec_flag
 
58
            to_exec_flag = self.new_exec_flag
59
59
        else:
60
 
            from_mode = self.new_mode
61
 
            to_mode = self.old_mode
 
60
            from_exec_flag = self.new_exec_flag
 
61
            to_exec_flag = self.old_exec_flag
62
62
        try:
63
 
            current_mode = os.stat(filename).st_mode &0777
 
63
            current_exec_flag = bool(os.stat(filename).st_mode & 0111)
64
64
        except OSError, e:
65
65
            if e.errno == errno.ENOENT:
66
 
                if conflict_handler.missing_for_chmod(filename) == "skip":
 
66
                if conflict_handler.missing_for_exec_flag(filename) == "skip":
67
67
                    return
68
68
                else:
69
 
                    current_mode = from_mode
 
69
                    current_exec_flag = from_exec_flag
70
70
 
71
 
        if from_mode is not None and current_mode != from_mode:
72
 
            if conflict_handler.wrong_old_perms(filename, from_mode, 
73
 
                                                current_mode) != "continue":
 
71
        if from_exec_flag is not None and current_exec_flag != from_exec_flag:
 
72
            if conflict_handler.wrong_old_exec_flag(filename,
 
73
                        from_exec_flag, current_exec_flag) != "continue":
74
74
                return
75
75
 
76
 
        if to_mode is not None:
 
76
        if to_exec_flag is not None:
 
77
            current_mode = os.stat(filename).st_mode
 
78
            if to_exec_flag:
 
79
                umask = os.umask(0)
 
80
                os.umask(umask)
 
81
                to_mode = current_mode | (0100 & ~umask)
 
82
                # Enable x-bit for others only if they can read it.
 
83
                if current_mode & 0004:
 
84
                    to_mode |= 0001 & ~umask
 
85
                if current_mode & 0040:
 
86
                    to_mode |= 0010 & ~umask
 
87
            else:
 
88
                to_mode = current_mode & ~0111
77
89
            try:
78
90
                os.chmod(filename, to_mode)
79
91
            except IOError, e:
80
92
                if e.errno == errno.ENOENT:
81
 
                    conflict_handler.missing_for_chmod(filename)
 
93
                    conflict_handler.missing_for_exec_flag(filename)
82
94
 
83
95
    def __eq__(self, other):
84
 
        if not isinstance(other, ChangeUnixPermissions):
85
 
            return False
86
 
        elif self.old_mode != other.old_mode:
87
 
            return False
88
 
        elif self.new_mode != other.new_mode:
89
 
            return False
90
 
        else:
91
 
            return True
 
96
        return (isinstance(other, ChangeExecFlag) and
 
97
                self.old_exec_flag == other.old_exec_flag and
 
98
                self.new_exec_flag == other.new_exec_flag)
92
99
 
93
100
    def __ne__(self, other):
94
101
        return not (self == other)
916
923
        Exception.__init__(self, "Conflict applying changes to %s" % this_path)
917
924
        self.this_path = this_path
918
925
 
919
 
class MergePermissionConflict(Exception):
920
 
    def __init__(self, this_path, base_path, other_path):
921
 
        this_perms = os.stat(this_path).st_mode & 0755
922
 
        base_perms = os.stat(base_path).st_mode & 0755
923
 
        other_perms = os.stat(other_path).st_mode & 0755
924
 
        msg = """Conflicting permission for %s
925
 
this: %o
926
 
base: %o
927
 
other: %o
928
 
        """ % (this_path, this_perms, base_perms, other_perms)
929
 
        self.this_path = this_path
930
 
        self.base_path = base_path
931
 
        self.other_path = other_path
932
 
        Exception.__init__(self, msg)
933
 
 
934
926
class WrongOldContents(Exception):
935
927
    def __init__(self, filename):
936
928
        msg = "Contents mismatch deleting %s" % filename
937
929
        self.filename = filename
938
930
        Exception.__init__(self, msg)
939
931
 
940
 
class WrongOldPermissions(Exception):
941
 
    def __init__(self, filename, old_perms, new_perms):
942
 
        msg = "Permission missmatch on %s:\n" \
943
 
        "Expected 0%o, got 0%o." % (filename, old_perms, new_perms)
 
932
class WrongOldExecFlag(Exception):
 
933
    def __init__(self, filename, old_exec_flag, new_exec_flag):
 
934
        msg = "Executable flag missmatch on %s:\n" \
 
935
        "Expected %s, got %s." % (filename, old_exec_flag, new_exec_flag)
944
936
        self.filename = filename
945
937
        Exception.__init__(self, msg)
946
938
 
964
956
        Exception.__init__(self, msg)
965
957
        self.filename = filename
966
958
 
967
 
class MissingPermsFile(Exception):
 
959
class MissingForSetExec(Exception):
968
960
    def __init__(self, filename):
969
961
        msg = "Attempt to change permissions on  %s, which does not exist" %\
970
962
            filename
1027
1019
        os.unlink(new_file)
1028
1020
        raise MergeConflict(this_path)
1029
1021
 
1030
 
    def permission_conflict(self, this_path, base_path, other_path):
1031
 
        raise MergePermissionConflict(this_path, base_path, other_path)
1032
 
 
1033
1022
    def wrong_old_contents(self, filename, expected_contents):
1034
1023
        raise WrongOldContents(filename)
1035
1024
 
1036
1025
    def rem_contents_conflict(self, filename, this_contents, base_contents):
1037
1026
        raise RemoveContentsConflict(filename)
1038
1027
 
1039
 
    def wrong_old_perms(self, filename, old_perms, new_perms):
1040
 
        raise WrongOldPermissions(filename, old_perms, new_perms)
 
1028
    def wrong_old_exec_flag(self, filename, old_exec_flag, new_exec_flag):
 
1029
        raise WrongOldExecFlag(filename, old_exec_flag, new_exec_flag)
1041
1030
 
1042
1031
    def rmdir_non_empty(self, filename):
1043
1032
        raise DeletingNonEmptyDirectory(filename)
1048
1037
    def patch_target_missing(self, filename, contents):
1049
1038
        raise PatchTargetMissing(filename)
1050
1039
 
1051
 
    def missing_for_chmod(self, filename):
1052
 
        raise MissingPermsFile(filename)
 
1040
    def missing_for_exec_flag(self, filename):
 
1041
        raise MissingForExecFlag(filename)
1053
1042
 
1054
1043
    def missing_for_rm(self, filename, change):
1055
1044
        raise MissingForRm(filename)
1265
1254
        return new_meta
1266
1255
    elif new_meta is None:
1267
1256
        return old_meta
1268
 
    elif isinstance(old_meta, ChangeUnixPermissions) and \
1269
 
        isinstance(new_meta, ChangeUnixPermissions):
1270
 
        return ChangeUnixPermissions(old_meta.old_mode, new_meta.new_mode)
 
1257
    elif (isinstance(old_meta, ChangeExecFlag) and
 
1258
          isinstance(new_meta, ChangeExecFlag)):
 
1259
        return ChangeExecFlag(old_meta.old_exec_flag, new_meta.new_exec_flag)
1271
1260
    else:
1272
1261
        return ApplySequence(old_meta, new_meta)
1273
1262
 
1391
1380
        stat_a = self.lstat(full_path_a)
1392
1381
        stat_b = self.lstat(full_path_b)
1393
1382
 
1394
 
        cs_entry.metadata_change = self.make_mode_change(stat_a, stat_b)
 
1383
        cs_entry.metadata_change = self.make_exec_flag_change(stat_a, stat_b)
1395
1384
 
1396
1385
        if id in self.tree_a and id in self.tree_b:
1397
1386
            a_sha1 = self.tree_a.get_file_sha1(id)
1405
1394
                                                             stat_b)
1406
1395
        return cs_entry
1407
1396
 
1408
 
    def make_mode_change(self, stat_a, stat_b):
1409
 
        mode_a = None
 
1397
    def make_exec_flag_change(self, stat_a, stat_b):
 
1398
        exec_flag_a = exec_flag_b = None
1410
1399
        if stat_a is not None and not stat.S_ISLNK(stat_a.st_mode):
1411
 
            mode_a = stat_a.st_mode & 0777
1412
 
        mode_b = None
 
1400
            exec_flag_a = bool(stat_a.st_mode & 0111)
1413
1401
        if stat_b is not None and not stat.S_ISLNK(stat_b.st_mode):
1414
 
            mode_b = stat_b.st_mode & 0777
1415
 
        if mode_a == mode_b:
 
1402
            exec_flag_b = bool(stat_b.st_mode & 0111)
 
1403
        if exec_flag_a == exec_flag_b:
1416
1404
            return None
1417
 
        return ChangeUnixPermissions(mode_a, mode_b)
 
1405
        return ChangeExecFlag(exec_flag_a, exec_flag_b)
1418
1406
 
1419
1407
    def make_contents_change(self, full_path_a, stat_a, full_path_b, stat_b):
1420
1408
        if stat_a is None and stat_b is None: