~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/dirstate.py

  • Committer: Vincent Ladeuil
  • Date: 2011-06-15 11:36:05 UTC
  • mto: This revision was merged to the branch mainline in revision 5975.
  • Revision ID: v.ladeuil+lp@free.fr-20110615113605-p7zyyfry9wy1hquc
Make ContentConflict resolution more robust

Show diffs side-by-side

added added

removed removed

Lines of Context:
219
219
"""
220
220
 
221
221
import bisect
 
222
import binascii
222
223
import errno
223
224
import operator
224
225
import os
225
226
from stat import S_IEXEC
226
227
import stat
 
228
import struct
227
229
import sys
228
230
import time
229
231
import zlib
230
232
 
231
233
from bzrlib import (
232
234
    cache_utf8,
233
 
    config,
234
235
    debug,
235
236
    errors,
236
237
    inventory,
238
239
    osutils,
239
240
    static_tuple,
240
241
    trace,
241
 
    urlutils,
242
242
    )
243
243
 
244
244
 
249
249
ERROR_DIRECTORY = 267
250
250
 
251
251
 
 
252
if not getattr(struct, '_compile', None):
 
253
    # Cannot pre-compile the dirstate pack_stat
 
254
    def pack_stat(st, _encode=binascii.b2a_base64, _pack=struct.pack):
 
255
        """Convert stat values into a packed representation."""
 
256
        return _encode(_pack('>LLLLLL', st.st_size, int(st.st_mtime),
 
257
            int(st.st_ctime), st.st_dev, st.st_ino & 0xFFFFFFFF,
 
258
            st.st_mode))[:-1]
 
259
else:
 
260
    # compile the struct compiler we need, so as to only do it once
 
261
    from _struct import Struct
 
262
    _compiled_pack = Struct('>LLLLLL').pack
 
263
    def pack_stat(st, _encode=binascii.b2a_base64, _pack=_compiled_pack):
 
264
        """Convert stat values into a packed representation."""
 
265
        # jam 20060614 it isn't really worth removing more entries if we
 
266
        # are going to leave it in packed form.
 
267
        # With only st_mtime and st_mode filesize is 5.5M and read time is 275ms
 
268
        # With all entries, filesize is 5.9M and read time is maybe 280ms
 
269
        # well within the noise margin
 
270
 
 
271
        # base64 encoding always adds a final newline, so strip it off
 
272
        # The current version
 
273
        return _encode(_pack(st.st_size, int(st.st_mtime), int(st.st_ctime),
 
274
            st.st_dev, st.st_ino & 0xFFFFFFFF, st.st_mode))[:-1]
 
275
        # This is 0.060s / 1.520s faster by not encoding as much information
 
276
        # return _encode(_pack('>LL', int(st.st_mtime), st.st_mode))[:-1]
 
277
        # This is not strictly faster than _encode(_pack())[:-1]
 
278
        # return '%X.%X.%X.%X.%X.%X' % (
 
279
        #      st.st_size, int(st.st_mtime), int(st.st_ctime),
 
280
        #      st.st_dev, st.st_ino, st.st_mode)
 
281
        # Similar to the _encode(_pack('>LL'))
 
282
        # return '%X.%X' % (int(st.st_mtime), st.st_mode)
 
283
 
 
284
 
 
285
def _unpack_stat(packed_stat):
 
286
    """Turn a packed_stat back into the stat fields.
 
287
 
 
288
    This is meant as a debugging tool, should not be used in real code.
 
289
    """
 
290
    (st_size, st_mtime, st_ctime, st_dev, st_ino,
 
291
     st_mode) = struct.unpack('>LLLLLL', binascii.a2b_base64(packed_stat))
 
292
    return dict(st_size=st_size, st_mtime=st_mtime, st_ctime=st_ctime,
 
293
                st_dev=st_dev, st_ino=st_ino, st_mode=st_mode)
 
294
 
 
295
 
252
296
class SHA1Provider(object):
253
297
    """An interface for getting sha1s of a file."""
254
298
 
404
448
        self._known_hash_changes = set()
405
449
        # How many hash changed entries can we have without saving
406
450
        self._worth_saving_limit = worth_saving_limit
407
 
        self._config_stack = config.LocationStack(urlutils.local_path_to_url(
408
 
            path))
409
451
 
410
452
    def __repr__(self):
411
453
        return "%s(%r)" % \
1850
1892
                    file_id, "This parent is not a directory.")
1851
1893
 
1852
1894
    def _observed_sha1(self, entry, sha1, stat_value,
1853
 
        _stat_to_minikind=_stat_to_minikind):
 
1895
        _stat_to_minikind=_stat_to_minikind, _pack_stat=pack_stat):
1854
1896
        """Note the sha1 of a file.
1855
1897
 
1856
1898
        :param entry: The entry the sha1 is for.
1862
1904
        except KeyError:
1863
1905
            # Unhandled kind
1864
1906
            return None
 
1907
        packed_stat = _pack_stat(stat_value)
1865
1908
        if minikind == 'f':
1866
1909
            if self._cutoff_time is None:
1867
1910
                self._sha_cutoff_time()
1868
1911
            if (stat_value.st_mtime < self._cutoff_time
1869
1912
                and stat_value.st_ctime < self._cutoff_time):
1870
1913
                entry[1][0] = ('f', sha1, stat_value.st_size, entry[1][0][3],
1871
 
                               pack_stat(stat_value))
 
1914
                               packed_stat)
1872
1915
                self._mark_modified([entry])
1873
1916
 
1874
1917
    def _sha_cutoff_time(self):
2427
2470
            raise errors.BzrError('missing num_entries line')
2428
2471
        self._num_entries = int(num_entries_line[len('num_entries: '):-1])
2429
2472
 
2430
 
    def sha1_from_stat(self, path, stat_result):
 
2473
    def sha1_from_stat(self, path, stat_result, _pack_stat=pack_stat):
2431
2474
        """Find a sha1 given a stat lookup."""
2432
 
        return self._get_packed_stat_index().get(pack_stat(stat_result), None)
 
2475
        return self._get_packed_stat_index().get(_pack_stat(stat_result), None)
2433
2476
 
2434
2477
    def _get_packed_stat_index(self):
2435
2478
        """Get a packed_stat index of self._dirblocks."""
2465
2508
        #       IN_MEMORY_HASH_MODIFIED, we should only fail quietly if we fail
2466
2509
        #       to save an IN_MEMORY_HASH_MODIFIED, and fail *noisily* if we
2467
2510
        #       fail to save IN_MEMORY_MODIFIED
2468
 
        if not self._worth_saving():
2469
 
            return
2470
 
 
2471
 
        grabbed_write_lock = False
2472
 
        if self._lock_state != 'w':
2473
 
            grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
2474
 
            # Switch over to the new lock, as the old one may be closed.
2475
 
            # TODO: jam 20070315 We should validate the disk file has
2476
 
            #       not changed contents, since temporary_write_lock may
2477
 
            #       not be an atomic operation.
2478
 
            self._lock_token = new_lock
2479
 
            self._state_file = new_lock.f
2480
 
            if not grabbed_write_lock:
2481
 
                # We couldn't grab a write lock, so we switch back to a read one
2482
 
                return
2483
 
        try:
2484
 
            lines = self.get_lines()
2485
 
            self._state_file.seek(0)
2486
 
            self._state_file.writelines(lines)
2487
 
            self._state_file.truncate()
2488
 
            self._state_file.flush()
2489
 
            self._maybe_fdatasync()
2490
 
            self._mark_unmodified()
2491
 
        finally:
2492
 
            if grabbed_write_lock:
2493
 
                self._lock_token = self._lock_token.restore_read_lock()
2494
 
                self._state_file = self._lock_token.f
 
2511
        if self._worth_saving():
 
2512
            grabbed_write_lock = False
 
2513
            if self._lock_state != 'w':
 
2514
                grabbed_write_lock, new_lock = self._lock_token.temporary_write_lock()
 
2515
                # Switch over to the new lock, as the old one may be closed.
2495
2516
                # TODO: jam 20070315 We should validate the disk file has
2496
 
                #       not changed contents. Since restore_read_lock may
2497
 
                #       not be an atomic operation.                
2498
 
 
2499
 
    def _maybe_fdatasync(self):
2500
 
        """Flush to disk if possible and if not configured off."""
2501
 
        if self._config_stack.get('dirstate.fdatasync'):
2502
 
            osutils.fdatasync(self._state_file.fileno())
 
2517
                #       not changed contents. Since temporary_write_lock may
 
2518
                #       not be an atomic operation.
 
2519
                self._lock_token = new_lock
 
2520
                self._state_file = new_lock.f
 
2521
                if not grabbed_write_lock:
 
2522
                    # We couldn't grab a write lock, so we switch back to a read one
 
2523
                    return
 
2524
            try:
 
2525
                lines = self.get_lines()
 
2526
                self._state_file.seek(0)
 
2527
                self._state_file.writelines(lines)
 
2528
                self._state_file.truncate()
 
2529
                self._state_file.flush()
 
2530
                self._mark_unmodified()
 
2531
            finally:
 
2532
                if grabbed_write_lock:
 
2533
                    self._lock_token = self._lock_token.restore_read_lock()
 
2534
                    self._state_file = self._lock_token.f
 
2535
                    # TODO: jam 20070315 We should validate the disk file has
 
2536
                    #       not changed contents. Since restore_read_lock may
 
2537
                    #       not be an atomic operation.
2503
2538
 
2504
2539
    def _worth_saving(self):
2505
2540
        """Is it worth saving the dirstate or not?"""
3350
3385
 
3351
3386
 
3352
3387
def py_update_entry(state, entry, abspath, stat_value,
3353
 
                 _stat_to_minikind=DirState._stat_to_minikind):
 
3388
                 _stat_to_minikind=DirState._stat_to_minikind,
 
3389
                 _pack_stat=pack_stat):
3354
3390
    """Update the entry based on what is actually on disk.
3355
3391
 
3356
3392
    This function only calculates the sha if it needs to - if the entry is
3369
3405
    except KeyError:
3370
3406
        # Unhandled kind
3371
3407
        return None
3372
 
    packed_stat = pack_stat(stat_value)
 
3408
    packed_stat = _pack_stat(stat_value)
3373
3409
    (saved_minikind, saved_link_or_sha1, saved_file_size,
3374
3410
     saved_executable, saved_packed_stat) = entry[1][0]
3375
3411
 
4248
4284
        _bisect_path_left,
4249
4285
        _bisect_path_right,
4250
4286
        cmp_by_dirs,
4251
 
        pack_stat,
4252
4287
        ProcessEntryC as _process_entry,
4253
4288
        update_entry as update_entry,
4254
4289
        )
4260
4295
        _bisect_path_left,
4261
4296
        _bisect_path_right,
4262
4297
        cmp_by_dirs,
4263
 
        pack_stat,
4264
4298
        )
4265
4299
    # FIXME: It would be nice to be able to track moved lines so that the
4266
4300
    # corresponding python code can be moved to the _dirstate_helpers_py