~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/changeset.py

- ignore .swp files from upload

Show diffs side-by-side

added added

removed removed

Lines of Context:
18
18
import patch
19
19
import stat
20
20
from bzrlib.trace import mutter
 
21
from bzrlib.osutils import rename
 
22
import bzrlib
21
23
 
22
24
# XXX: mbp: I'm not totally convinced that we should handle conflicts
23
25
# as part of changeset application, rather than only in the merge
42
44
        newdict[value] = key
43
45
    return newdict
44
46
 
45
 
 
46
47
       
47
 
class ChangeUnixPermissions(object):
 
48
class ChangeExecFlag(object):
48
49
    """This is two-way change, suitable for file modification, creation,
49
50
    deletion"""
50
 
    def __init__(self, old_mode, new_mode):
51
 
        self.old_mode = old_mode
52
 
        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
53
54
 
54
55
    def apply(self, filename, conflict_handler, reverse=False):
55
56
        if not reverse:
56
 
            from_mode = self.old_mode
57
 
            to_mode = self.new_mode
 
57
            from_exec_flag = self.old_exec_flag
 
58
            to_exec_flag = self.new_exec_flag
58
59
        else:
59
 
            from_mode = self.new_mode
60
 
            to_mode = self.old_mode
 
60
            from_exec_flag = self.new_exec_flag
 
61
            to_exec_flag = self.old_exec_flag
61
62
        try:
62
 
            current_mode = os.stat(filename).st_mode &0777
 
63
            current_exec_flag = bool(os.stat(filename).st_mode & 0111)
63
64
        except OSError, e:
64
65
            if e.errno == errno.ENOENT:
65
 
                if conflict_handler.missing_for_chmod(filename) == "skip":
 
66
                if conflict_handler.missing_for_exec_flag(filename) == "skip":
66
67
                    return
67
68
                else:
68
 
                    current_mode = from_mode
 
69
                    current_exec_flag = from_exec_flag
69
70
 
70
 
        if from_mode is not None and current_mode != from_mode:
71
 
            if conflict_handler.wrong_old_perms(filename, from_mode, 
72
 
                                                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":
73
74
                return
74
75
 
75
 
        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
76
89
            try:
77
90
                os.chmod(filename, to_mode)
78
91
            except IOError, e:
79
92
                if e.errno == errno.ENOENT:
80
 
                    conflict_handler.missing_for_chmod(filename)
 
93
                    conflict_handler.missing_for_exec_flag(filename)
81
94
 
82
95
    def __eq__(self, other):
83
 
        if not isinstance(other, ChangeUnixPermissions):
84
 
            return False
85
 
        elif self.old_mode != other.old_mode:
86
 
            return False
87
 
        elif self.new_mode != other.new_mode:
88
 
            return False
89
 
        else:
90
 
            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)
91
99
 
92
100
    def __ne__(self, other):
93
101
        return not (self == other)
94
102
 
 
103
 
95
104
def dir_create(filename, conflict_handler, reverse):
96
105
    """Creates the directory, or deletes it if reverse is true.  Intended to be
97
106
    used with ReplaceContents.
117
126
        try:
118
127
            os.rmdir(filename)
119
128
        except OSError, e:
120
 
            if e.errno != 39:
 
129
            if e.errno != errno.ENOTEMPTY:
121
130
                raise
122
131
            if conflict_handler.rmdir_non_empty(filename) == "skip":
123
132
                return
124
133
            os.rmdir(filename)
125
134
 
126
 
                
127
 
            
128
135
 
129
136
class SymlinkCreate(object):
130
137
    """Creates or deletes a symlink (for use with ReplaceContents)"""
353
360
        status = patch.diff3(new_file, filename, base, other)
354
361
        if status == 0:
355
362
            os.chmod(new_file, os.stat(filename).st_mode)
356
 
            os.rename(new_file, filename)
 
363
            rename(new_file, filename)
357
364
            return
358
365
        else:
359
366
            assert(status == 1)
360
367
            def get_lines(filename):
361
 
                my_file = file(base, "rb")
 
368
                my_file = file(filename, "rb")
362
369
                lines = my_file.readlines()
363
370
                my_file.close()
 
371
                return lines
364
372
            base_lines = get_lines(base)
365
373
            other_lines = get_lines(other)
366
374
            conflict_handler.merge_conflict(new_file, filename, base_lines, 
626
634
                return None
627
635
            return self.path
628
636
 
629
 
    def summarize_name(self, changeset, reverse=False):
 
637
    def summarize_name(self, reverse=False):
630
638
        """Produce a one-line summary of the filename.  Indicates renames as
631
639
        old => new, indicates creation as None => new, indicates deletion as
632
640
        old => None.
663
671
        :type reverse: bool
664
672
        :rtype: str
665
673
        """
666
 
        mutter("Finding new path for %s" % self.summarize_name(changeset))
 
674
        mutter("Finding new path for %s" % self.summarize_name())
667
675
        if reverse:
668
676
            parent = self.parent
669
677
            to_dir = self.dir
831
839
            if src_path is not None:
832
840
                src_path = os.path.join(dir, src_path)
833
841
                try:
834
 
                    os.rename(src_path, to_name)
 
842
                    rename(src_path, to_name)
835
843
                    temp_name[entry.id] = to_name
836
844
                except OSError, e:
837
845
                    if e.errno != errno.ENOENT:
863
871
            continue
864
872
        new_path = os.path.join(dir, new_tree_path)
865
873
        old_path = changed_inventory.get(entry.id)
866
 
        if os.path.exists(new_path):
 
874
        if bzrlib.osutils.lexists(new_path):
867
875
            if conflict_handler.target_exists(entry, new_path, old_path) == \
868
876
                "skip":
869
877
                continue
874
882
            if old_path is None:
875
883
                continue
876
884
            try:
877
 
                os.rename(old_path, new_path)
 
885
                rename(old_path, new_path)
878
886
                changed_inventory[entry.id] = new_tree_path
879
887
            except OSError, e:
880
888
                raise Exception ("%s is missing" % new_path)
915
923
        Exception.__init__(self, "Conflict applying changes to %s" % this_path)
916
924
        self.this_path = this_path
917
925
 
918
 
class MergePermissionConflict(Exception):
919
 
    def __init__(self, this_path, base_path, other_path):
920
 
        this_perms = os.stat(this_path).st_mode & 0755
921
 
        base_perms = os.stat(base_path).st_mode & 0755
922
 
        other_perms = os.stat(other_path).st_mode & 0755
923
 
        msg = """Conflicting permission for %s
924
 
this: %o
925
 
base: %o
926
 
other: %o
927
 
        """ % (this_path, this_perms, base_perms, other_perms)
928
 
        self.this_path = this_path
929
 
        self.base_path = base_path
930
 
        self.other_path = other_path
931
 
        Exception.__init__(self, msg)
932
 
 
933
926
class WrongOldContents(Exception):
934
927
    def __init__(self, filename):
935
928
        msg = "Contents mismatch deleting %s" % filename
936
929
        self.filename = filename
937
930
        Exception.__init__(self, msg)
938
931
 
939
 
class WrongOldPermissions(Exception):
940
 
    def __init__(self, filename, old_perms, new_perms):
941
 
        msg = "Permission missmatch on %s:\n" \
942
 
        "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)
943
936
        self.filename = filename
944
937
        Exception.__init__(self, msg)
945
938
 
963
956
        Exception.__init__(self, msg)
964
957
        self.filename = filename
965
958
 
966
 
class MissingPermsFile(Exception):
 
959
class MissingForSetExec(Exception):
967
960
    def __init__(self, filename):
968
961
        msg = "Attempt to change permissions on  %s, which does not exist" %\
969
962
            filename
1003
996
    descend from this class if they have a better way to handle some or
1004
997
    all types of conflict.
1005
998
    """
1006
 
    def __init__(self, dir):
1007
 
        self.dir = dir
1008
 
    
1009
999
    def missing_parent(self, pathname):
1010
1000
        parent = os.path.dirname(pathname)
1011
1001
        raise Exception("Parent directory missing for %s" % pathname)
1029
1019
        os.unlink(new_file)
1030
1020
        raise MergeConflict(this_path)
1031
1021
 
1032
 
    def permission_conflict(self, this_path, base_path, other_path):
1033
 
        raise MergePermissionConflict(this_path, base_path, other_path)
1034
 
 
1035
1022
    def wrong_old_contents(self, filename, expected_contents):
1036
1023
        raise WrongOldContents(filename)
1037
1024
 
1038
1025
    def rem_contents_conflict(self, filename, this_contents, base_contents):
1039
1026
        raise RemoveContentsConflict(filename)
1040
1027
 
1041
 
    def wrong_old_perms(self, filename, old_perms, new_perms):
1042
 
        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)
1043
1030
 
1044
1031
    def rmdir_non_empty(self, filename):
1045
1032
        raise DeletingNonEmptyDirectory(filename)
1050
1037
    def patch_target_missing(self, filename, contents):
1051
1038
        raise PatchTargetMissing(filename)
1052
1039
 
1053
 
    def missing_for_chmod(self, filename):
1054
 
        raise MissingPermsFile(filename)
 
1040
    def missing_for_exec_flag(self, filename):
 
1041
        raise MissingForExecFlag(filename)
1055
1042
 
1056
1043
    def missing_for_rm(self, filename, change):
1057
1044
        raise MissingForRm(filename)
1065
1052
    def new_contents_conflict(self, filename, other_contents):
1066
1053
        raise NewContentsConflict(filename)
1067
1054
 
1068
 
    def finalize():
 
1055
    def finalize(self):
1069
1056
        pass
1070
1057
 
1071
1058
def apply_changeset(changeset, inventory, dir, conflict_handler=None, 
1084
1071
    :rtype: Dictionary
1085
1072
    """
1086
1073
    if conflict_handler is None:
1087
 
        conflict_handler = ExceptionConflictHandler(dir)
 
1074
        conflict_handler = ExceptionConflictHandler()
1088
1075
    temp_dir = os.path.join(dir, "bzr-tree-change")
1089
1076
    try:
1090
1077
        os.mkdir(temp_dir)
1267
1254
        return new_meta
1268
1255
    elif new_meta is None:
1269
1256
        return old_meta
1270
 
    elif isinstance(old_meta, ChangeUnixPermissions) and \
1271
 
        isinstance(new_meta, ChangeUnixPermissions):
1272
 
        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)
1273
1260
    else:
1274
1261
        return ApplySequence(old_meta, new_meta)
1275
1262
 
1290
1277
def generate_changeset(tree_a, tree_b, interesting_ids=None):
1291
1278
    return ChangesetGenerator(tree_a, tree_b, interesting_ids)()
1292
1279
 
 
1280
 
1293
1281
class ChangesetGenerator(object):
1294
1282
    def __init__(self, tree_a, tree_b, interesting_ids=None):
1295
1283
        object.__init__(self)
1329
1317
            yield self.get_entry(file_id, tree)
1330
1318
 
1331
1319
    def get_entry(self, file_id, tree):
1332
 
        if file_id not in tree:
 
1320
        if not tree.has_or_had_id(file_id):
1333
1321
            return None
1334
1322
        return tree.tree.inventory[file_id]
1335
1323
 
1339
1327
        return entry.parent_id
1340
1328
 
1341
1329
    def get_path(self, file_id, tree):
1342
 
        if not tree.has_id(file_id):
 
1330
        if not tree.has_or_had_id(file_id):
1343
1331
            return None
1344
1332
        path = tree.id2path(file_id)
1345
1333
        if path == '':
1387
1375
 
1388
1376
        if cs_entry is None:
1389
1377
            return None
 
1378
 
 
1379
        full_path_a = self.tree_a.readonly_path(id)
 
1380
        full_path_b = self.tree_b.readonly_path(id)
 
1381
        stat_a = self.lstat(full_path_a)
 
1382
        stat_b = self.lstat(full_path_b)
 
1383
 
 
1384
        cs_entry.metadata_change = self.make_exec_flag_change(stat_a, stat_b)
 
1385
 
1390
1386
        if id in self.tree_a and id in self.tree_b:
1391
1387
            a_sha1 = self.tree_a.get_file_sha1(id)
1392
1388
            b_sha1 = self.tree_b.get_file_sha1(id)
1393
1389
            if None not in (a_sha1, b_sha1) and a_sha1 == b_sha1:
1394
1390
                return cs_entry
1395
1391
 
1396
 
        full_path_a = self.tree_a.readonly_path(id)
1397
 
        full_path_b = self.tree_b.readonly_path(id)
1398
 
        stat_a = self.lstat(full_path_a)
1399
 
        stat_b = self.lstat(full_path_b)
1400
 
        if stat_b is None:
1401
 
            cs_entry.new_parent = None
1402
 
            cs_entry.new_path = None
1403
 
        
1404
 
        cs_entry.metadata_change = self.make_mode_change(stat_a, stat_b)
1405
1392
        cs_entry.contents_change = self.make_contents_change(full_path_a,
1406
1393
                                                             stat_a, 
1407
1394
                                                             full_path_b, 
1408
1395
                                                             stat_b)
1409
1396
        return cs_entry
1410
1397
 
1411
 
    def make_mode_change(self, stat_a, stat_b):
1412
 
        mode_a = None
 
1398
    def make_exec_flag_change(self, stat_a, stat_b):
 
1399
        exec_flag_a = exec_flag_b = None
1413
1400
        if stat_a is not None and not stat.S_ISLNK(stat_a.st_mode):
1414
 
            mode_a = stat_a.st_mode & 0777
1415
 
        mode_b = None
 
1401
            exec_flag_a = bool(stat_a.st_mode & 0111)
1416
1402
        if stat_b is not None and not stat.S_ISLNK(stat_b.st_mode):
1417
 
            mode_b = stat_b.st_mode & 0777
1418
 
        if mode_a == mode_b:
 
1403
            exec_flag_b = bool(stat_b.st_mode & 0111)
 
1404
        if exec_flag_a == exec_flag_b:
1419
1405
            return None
1420
 
        return ChangeUnixPermissions(mode_a, mode_b)
 
1406
        return ChangeExecFlag(exec_flag_a, exec_flag_b)
1421
1407
 
1422
1408
    def make_contents_change(self, full_path_a, stat_a, full_path_b, stat_b):
1423
1409
        if stat_a is None and stat_b is None: