~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

cherrypick support for tree-references in dirstate

Show diffs side-by-side

added added

removed removed

Lines of Context:
20
20
lines by NL. The field delimiters are ommitted in the grammar, line delimiters
21
21
are not - this is done for clarity of reading. All string data is in utf8.
22
22
 
23
 
MINIKIND = "f" | "d" | "l" | "a" | "r";
 
23
MINIKIND = "f" | "d" | "l" | "a" | "r" | "t";
24
24
NL = "\n";
25
25
NULL = "\0";
26
26
WHOLE_NUMBER = {digit}, digit;
86
86
    sha1 value.
87
87
'l' is a symlink entry: As for directory, but a symlink. The fingerprint is the
88
88
    link target.
89
 
 
 
89
't' is a reference to a nested subtree; the fingerprint is the referenced
 
90
    revision.
 
91
 
 
92
Ordering:
 
93
 
 
94
The entries on disk and in memory are ordered according to the following keys:
 
95
 
 
96
    directory, as a list of components
 
97
    filename
 
98
    file-id
90
99
 
91
100
--- Format 1 had the following different definition: ---
92
101
rows = dirname, NULL, basename, NULL, MINIKIND, NULL, fileid_utf8, NULL,
220
229
    A dirstate is a specialised data structure for managing local working
221
230
    tree state information. Its not yet well defined whether it is platform
222
231
    specific, and if it is how we detect/parameterise that.
 
232
 
 
233
    Dirstates use the usual lock_write, lock_read and unlock mechanisms.
 
234
    Unlike most bzr disk formats, DirStates must be locked for reading, using
 
235
    lock_read.  (This is an os file lock internally.)  This is necessary
 
236
    because the file can be rewritten in place.
 
237
 
 
238
    DirStates must be explicitly written with save() to commit changes; just
 
239
    unlocking them does not write the changes to disk.
223
240
    """
224
241
 
225
 
    _kind_to_minikind = {'absent':'a', 'file':'f', 'directory':'d', 'relocated':'r', 'symlink':'l'}
226
 
    _minikind_to_kind = {'a':'absent', 'f':'file', 'd':'directory', 'l':'symlink', 'r':'relocated'}
 
242
    _kind_to_minikind = {
 
243
            'absent': 'a',
 
244
            'file': 'f',
 
245
            'directory': 'd',
 
246
            'relocated': 'r',
 
247
            'symlink': 'l',
 
248
            'tree-reference': 't',
 
249
        }
 
250
    _minikind_to_kind = {
 
251
            'a': 'absent',
 
252
            'f': 'file',
 
253
            'd': 'directory',
 
254
            'l':'symlink',
 
255
            'r': 'relocated',
 
256
            't': 'tree-reference',
 
257
        }
227
258
    _to_yesno = {True:'y', False: 'n'} # TODO profile the performance gain
228
259
     # of using int conversion rather than a dict here. AND BLAME ANDREW IF
229
260
     # it is faster.
287
318
        self._split_path_cache = {}
288
319
        self._bisect_page_size = DirState.BISECT_PAGE_SIZE
289
320
 
290
 
    def add(self, path, file_id, kind, stat, link_or_sha1):
 
321
    def add(self, path, file_id, kind, stat, fingerprint):
291
322
        """Add a path to be tracked.
292
323
 
293
324
        :param path: The path within the dirstate - '' is the root, 'foo' is the
294
325
            path foo within the root, 'foo/bar' is the path bar within foo 
295
326
            within the root.
296
327
        :param file_id: The file id of the path being added.
297
 
        :param kind: The kind of the path.
 
328
        :param kind: The kind of the path, as a string like 'file', 
 
329
            'directory', etc.
298
330
        :param stat: The output of os.lstat for the path.
299
 
        :param link_or_sha1: The sha value of the file, or the target of a
300
 
            symlink. '' for directories.
 
331
        :param fingerprint: The sha value of the file,
 
332
            or the target of a symlink,
 
333
            or the referenced revision id for tree-references,
 
334
            or '' for directories.
301
335
        """
302
336
        # adding a file:
303
337
        # find the block its in. 
363
397
        minikind = DirState._kind_to_minikind[kind]
364
398
        if kind == 'file':
365
399
            entry_data = entry_key, [
366
 
                (minikind, link_or_sha1, size, False, packed_stat),
 
400
                (minikind, fingerprint, size, False, packed_stat),
367
401
                ] + parent_info
368
402
        elif kind == 'directory':
369
403
            entry_data = entry_key, [
371
405
                ] + parent_info
372
406
        elif kind == 'symlink':
373
407
            entry_data = entry_key, [
374
 
                (minikind, link_or_sha1, size, False, packed_stat),
 
408
                (minikind, fingerprint, size, False, packed_stat),
 
409
                ] + parent_info
 
410
        elif kind == 'tree-reference':
 
411
            entry_data = entry_key, [
 
412
                (minikind, fingerprint, 0, False, packed_stat),
375
413
                ] + parent_info
376
414
        else:
377
415
            raise errors.BzrError('unknown kind %r' % kind)
850
888
        """Load new_entries into self.dirblocks.
851
889
 
852
890
        Process new_entries into the current state object, making them the active
853
 
        state.
 
891
        state.  The entries are grouped together by directory to form dirblocks.
854
892
 
855
893
        :param new_entries: A sorted list of entries. This function does not sort
856
894
            to prevent unneeded overhead when callers have a sorted list already.
1295
1333
        :param tree_index: The index of the tree we wish to locate this path
1296
1334
            in. If the path is present in that tree, the entry containing its
1297
1335
            details is returned, otherwise (None, None) is returned
 
1336
            0 is the working tree, higher indexes are successive parent
 
1337
            trees.
1298
1338
        :param fileid_utf8: A utf8 file_id to look up.
1299
1339
        :param path_utf8: An utf8 path to be looked up.
1300
1340
        :return: The dirstate entry tuple for path, or (None, None)
1334
1374
                entry_index, present = self._find_entry_index(key, block)
1335
1375
                if present:
1336
1376
                    entry = self._dirblocks[block_index][1][entry_index]
1337
 
                    if entry[1][tree_index][0] in 'fdl':
 
1377
                    if entry[1][tree_index][0] in 'fdlt':
1338
1378
                        # this is the result we are looking for: the  
1339
1379
                        # real home of this file_id in this tree.
1340
1380
                        return entry
1341
1381
                    if entry[1][tree_index][0] == 'a':
1342
1382
                        # there is no home for this entry in this tree
1343
1383
                        return None, None
1344
 
                    assert entry[1][tree_index][0] == 'r'
 
1384
                    assert entry[1][tree_index][0] == 'r', \
 
1385
                        "entry %r has invalid minikind %r for tree %r" \
 
1386
                        % (entry,
 
1387
                           entry[1][tree_index][0],
 
1388
                           tree_index)
1345
1389
                    real_path = entry[1][tree_index][1]
1346
1390
                    return self._get_entry(tree_index, fileid_utf8=fileid_utf8,
1347
1391
                        path_utf8=real_path)
1407
1451
            fingerprint = inv_entry.text_sha1 or ''
1408
1452
            size = inv_entry.text_size or 0
1409
1453
            executable = inv_entry.executable
 
1454
        elif kind == 'tree-reference':
 
1455
            fingerprint = inv_entry.reference_revision or ''
 
1456
            size = 0
 
1457
            executable = False
1410
1458
        else:
1411
 
            raise Exception
 
1459
            raise Exception("can't pack %s" % inv_entry)
1412
1460
        return (minikind, fingerprint, size, executable, tree_data)
1413
1461
 
1414
1462
    def _iter_entries(self):
1726
1774
        :param ghosts: A list of the revision_ids that are ghosts at the time
1727
1775
            of setting.
1728
1776
        """ 
 
1777
        ## self._validate()
1729
1778
        # TODO: generate a list of parent indexes to preserve to save 
1730
1779
        # processing specific parent trees. In the common case one tree will
1731
1780
        # be preserved - the left most parent.
1849
1898
        # --- end generation of full tree mappings
1850
1899
 
1851
1900
        # sort and output all the entries
1852
 
        new_entries = sorted(by_path.items(),
1853
 
                        key=lambda entry:(entry[0][0].split('/'), entry[0][1]))
 
1901
        new_entries = self._sort_entries(by_path.items())
1854
1902
        self._entries_to_current_state(new_entries)
1855
1903
        self._parents = [rev_id for rev_id, tree in trees]
1856
1904
        self._ghosts = list(ghosts)
1857
1905
        self._header_state = DirState.IN_MEMORY_MODIFIED
1858
1906
        self._dirblock_state = DirState.IN_MEMORY_MODIFIED
1859
1907
        self._id_index = id_index
 
1908
        self._validate()
 
1909
 
 
1910
    def _sort_entries(self, entry_list):
 
1911
        """Given a list of entries, sort them into the right order.
 
1912
 
 
1913
        This is done when constructing a new dirstate from trees - normally we
 
1914
        try to keep everything in sorted blocks all the time, but sometimes
 
1915
        it's easier to sort after the fact.
 
1916
        """
 
1917
        # TODO: Might be faster to do a scwartzian transform?
 
1918
        def _key(entry):
 
1919
            # sort by: directory parts, file name, file id
 
1920
            return entry[0][0].split('/'), entry[0][1], entry[0][2]
 
1921
        return sorted(entry_list, key=_key)
1860
1922
 
1861
1923
    def set_state_from_inventory(self, new_inv):
1862
1924
        """Set new_inv as the current state. 
2116
2178
            assert self._dirblocks[1][0] == '', \
2117
2179
                    "dirblocks missing root directory:\n" + \
2118
2180
                    pformat(dirblocks)
2119
 
        assert self._dirblocks[1:] == sorted(self._dirblocks[1:]), \
2120
 
                "dirblocks are not in sorted order:\n" + \
2121
 
                pformat(self._dirblocks)
 
2181
        # the dirblocks are sorted by their path components, name, and dir id
 
2182
        dir_names = [d[0].split('/')
 
2183
                for d in self._dirblocks[1:]]
 
2184
        if dir_names != sorted(dir_names):
 
2185
            raise AssertionError(
 
2186
                "dir names are not in sorted order:\n" + \
 
2187
                pformat(self._dirblocks) + \
 
2188
                "\nkeys:\n" +
 
2189
                pformat(dir_names))
2122
2190
        for dirblock in self._dirblocks:
 
2191
            # within each dirblock, the entries are sorted by filename and
 
2192
            # then by id.
2123
2193
            assert dirblock[1] == sorted(dirblock[1]), \
2124
2194
                "dirblock for %r is not sorted:\n%s" % \
2125
2195
                (dirblock[0], pformat(dirblock))