~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/osutils.py

merge some of dirstate, update comparison tests to keep tree roots the same unless they're meant to differ

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright (C) 2005, 2006 Canonical Ltd
 
1
# Copyright (C) 2005, 2006, 2007 Canonical Ltd
2
2
#
3
3
# This program is free software; you can redistribute it and/or modify
4
4
# it under the terms of the GNU General Public License as published by
1055
1055
    
1056
1056
    The data yielded is of the form:
1057
1057
    ((directory-relpath, directory-path-from-top),
1058
 
    [(directory-relpath, basename, kind, lstat), ...]),
 
1058
    [(directory-relpath, basename, kind, lstat, path-from-top), ...]),
1059
1059
     - directory-relpath is the relative path of the directory being returned
1060
1060
       with respect to top. prefix is prepended to this.
1061
1061
     - directory-path-from-root is the path including top for this directory. 
1079
1079
    # depending on top and prefix - i.e. ./foo and foo as a pair leads to
1080
1080
    # potentially confusing output. We should make this more robust - but
1081
1081
    # not at a speed cost. RBC 20060731
1082
 
    lstat = os.lstat
1083
 
    pending = []
 
1082
    _lstat = os.lstat
1084
1083
    _directory = _directory_kind
1085
1084
    _listdir = os.listdir
1086
 
    pending = [(prefix, "", _directory, None, top)]
 
1085
    _kind_from_mode = _formats.get
 
1086
    pending = [(safe_unicode(prefix), "", _directory, None, safe_unicode(top))]
1087
1087
    while pending:
1088
 
        dirblock = []
1089
 
        currentdir = pending.pop()
1090
1088
        # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
1091
 
        top = currentdir[4]
1092
 
        if currentdir[0]:
1093
 
            relroot = currentdir[0] + '/'
1094
 
        else:
1095
 
            relroot = ""
1096
 
        for name in sorted(_listdir(top)):
1097
 
            abspath = top + '/' + name
1098
 
            statvalue = lstat(abspath)
1099
 
            dirblock.append((relroot + name, name,
1100
 
                file_kind_from_stat_mode(statvalue.st_mode),
1101
 
                statvalue, abspath))
1102
 
        yield (currentdir[0], top), dirblock
1103
 
        # push the user specified dirs from dirblock
1104
 
        for dir in reversed(dirblock):
1105
 
            if dir[2] == _directory:
1106
 
                pending.append(dir)
 
1089
        relroot, _, _, _, top = pending.pop()
 
1090
        if relroot:
 
1091
            relprefix = relroot + u'/'
 
1092
        else:
 
1093
            relprefix = ''
 
1094
        top_slash = top + u'/'
 
1095
 
 
1096
        dirblock = []
 
1097
        append = dirblock.append
 
1098
        for name in sorted(_listdir(top)):
 
1099
            abspath = top_slash + name
 
1100
            statvalue = _lstat(abspath)
 
1101
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1102
            append((relprefix + name, name, kind, statvalue, abspath))
 
1103
        yield (relroot, top), dirblock
 
1104
 
 
1105
        # push the user specified dirs from dirblock
 
1106
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1107
 
 
1108
 
 
1109
def _walkdirs_utf8(top, prefix=""):
 
1110
    """Yield data about all the directories in a tree.
 
1111
 
 
1112
    This yields the same information as walkdirs() only each entry is yielded
 
1113
    in utf-8. On platforms which have a filesystem encoding of utf8 the paths
 
1114
    are returned as exact byte-strings.
 
1115
 
 
1116
    :return: yields a tuple of (dir_info, [file_info])
 
1117
        dir_info is (utf8_relpath, path-from-top)
 
1118
        file_info is (utf8_relpath, utf8_name, kind, lstat, path-from-top)
 
1119
        if top is an absolute path, path-from-top is also an absolute path.
 
1120
        path-from-top might be unicode or utf8, but it is the correct path to
 
1121
        pass to os functions to affect the file in question. (such as os.lstat)
 
1122
    """
 
1123
    fs_encoding = sys.getfilesystemencoding()
 
1124
    if (sys.platform == 'win32' or
 
1125
        fs_encoding not in ('UTF-8', 'US-ASCII', 'ANSI_X3.4-1968')): # ascii
 
1126
        return _walkdirs_unicode_to_utf8(top, prefix=prefix)
 
1127
    else:
 
1128
        return _walkdirs_fs_utf8(top, prefix=prefix)
 
1129
 
 
1130
 
 
1131
def _walkdirs_fs_utf8(top, prefix=""):
 
1132
    """See _walkdirs_utf8.
 
1133
 
 
1134
    This sub-function is called when we know the filesystem is already in utf8
 
1135
    encoding. So we don't need to transcode filenames.
 
1136
    """
 
1137
    _lstat = os.lstat
 
1138
    _directory = _directory_kind
 
1139
    _listdir = os.listdir
 
1140
    _kind_from_mode = _formats.get
 
1141
 
 
1142
    # 0 - relpath, 1- basename, 2- kind, 3- stat, 4-toppath
 
1143
    # But we don't actually uses 1-3 in pending, so set them to None
 
1144
    pending = [(safe_utf8(prefix), None, None, None, safe_utf8(top))]
 
1145
    while pending:
 
1146
        relroot, _, _, _, top = pending.pop()
 
1147
        if relroot:
 
1148
            relprefix = relroot + '/'
 
1149
        else:
 
1150
            relprefix = ''
 
1151
        top_slash = top + '/'
 
1152
 
 
1153
        dirblock = []
 
1154
        append = dirblock.append
 
1155
        for name in sorted(_listdir(top)):
 
1156
            abspath = top_slash + name
 
1157
            statvalue = _lstat(abspath)
 
1158
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1159
            append((relprefix + name, name, kind, statvalue, abspath))
 
1160
        yield (relroot, top), dirblock
 
1161
 
 
1162
        # push the user specified dirs from dirblock
 
1163
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
 
1164
 
 
1165
 
 
1166
def _walkdirs_unicode_to_utf8(top, prefix=""):
 
1167
    """See _walkdirs_utf8
 
1168
 
 
1169
    Because Win32 has a Unicode api, all of the 'path-from-top' entries will be
 
1170
    Unicode paths.
 
1171
    This is currently the fallback code path when the filesystem encoding is
 
1172
    not UTF-8. It may be better to implement an alternative so that we can
 
1173
    safely handle paths that are not properly decodable in the current
 
1174
    encoding.
 
1175
    """
 
1176
    _utf8_encode = codecs.getencoder('utf8')
 
1177
    _lstat = os.lstat
 
1178
    _directory = _directory_kind
 
1179
    _listdir = os.listdir
 
1180
    _kind_from_mode = _formats.get
 
1181
 
 
1182
    pending = [(safe_utf8(prefix), None, None, None, safe_unicode(top))]
 
1183
    while pending:
 
1184
        relroot, _, _, _, top = pending.pop()
 
1185
        if relroot:
 
1186
            relprefix = relroot + '/'
 
1187
        else:
 
1188
            relprefix = ''
 
1189
        top_slash = top + u'/'
 
1190
 
 
1191
        dirblock = []
 
1192
        append = dirblock.append
 
1193
        for name in sorted(_listdir(top)):
 
1194
            name_utf8 = _utf8_encode(name)[0]
 
1195
            abspath = top_slash + name
 
1196
            statvalue = _lstat(abspath)
 
1197
            kind = _kind_from_mode(statvalue.st_mode & 0170000, 'unknown')
 
1198
            append((relprefix + name_utf8, name_utf8, kind, statvalue, abspath))
 
1199
        yield (relroot, top), dirblock
 
1200
 
 
1201
        # push the user specified dirs from dirblock
 
1202
        pending.extend(d for d in reversed(dirblock) if d[2] == _directory)
1107
1203
 
1108
1204
 
1109
1205
def copy_tree(from_path, to_path, handlers={}):