/brz/remove-bazaar

To get this branch, use:
bzr branch http://gegoxaren.bato24.eu/bzr/brz/remove-bazaar

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-05-30 01:37:52 UTC
  • Revision ID: mbp@sourcefrog.net-20050530013751-650874ded00ae1a1
- Use explicit lock methods on a branch, rather than doing it
  implicitly from the Branch constructor and relying on destroying the
  branch to release the lock.

- New with_readlock, _with_writelock decorators for branch methods.

- Branch locks can now be taken several times by a single caller, but
  they forbid upgrading or downgrading.

- Don't need assertions about whether the branch is locked anymore.

Show diffs side-by-side

added added

removed removed

Lines of Context:
43
43
        return remotebranch.RemoteBranch(f, **args)
44
44
    else:
45
45
        return Branch(f, **args)
 
46
 
 
47
 
 
48
 
 
49
def with_writelock(method):
 
50
    """Method decorator for functions run with the branch locked."""
 
51
    def d(self, *a, **k):
 
52
        # called with self set to the branch
 
53
        self.lock('w')
 
54
        try:
 
55
            return method(self, *a, **k)
 
56
        finally:
 
57
            self.unlock()
 
58
    return d
 
59
 
 
60
 
 
61
def with_readlock(method):
 
62
    def d(self, *a, **k):
 
63
        self.lock('r')
 
64
        try:
 
65
            return method(self, *a, **k)
 
66
        finally:
 
67
            self.unlock()
 
68
    return d
46
69
        
47
70
 
48
71
def find_branch_root(f=None):
87
110
        Base directory of the branch.
88
111
 
89
112
    _lock_mode
90
 
        None, or a duple with 'r' or 'w' for the first element and a positive
91
 
        count for the second.
 
113
        None, or 'r' or 'w'
 
114
 
 
115
    _lock_count
 
116
        If _lock_mode is true, a positive count of the number of times the
 
117
        lock has been taken.
92
118
 
93
119
    _lockfile
94
120
        Open file used for locking.
95
121
    """
96
122
    base = None
97
123
    _lock_mode = None
 
124
    _lock_count = None
98
125
    
99
 
    def __init__(self, base, init=False, find_root=True, lock_mode='w'):
 
126
    def __init__(self, base, init=False, find_root=True):
100
127
        """Create new branch object at a particular location.
101
128
 
102
129
        base -- Base directory for the branch.
125
152
                                      'current bzr can only operate from top-of-tree'])
126
153
        self._check_format()
127
154
        self._lockfile = self.controlfile('branch-lock', 'wb')
128
 
        self.lock(lock_mode)
129
155
 
130
156
        self.text_store = ImmutableStore(self.controlfilename('text-store'))
131
157
        self.revision_store = ImmutableStore(self.controlfilename('revision-store'))
148
174
 
149
175
    def lock(self, mode):
150
176
        if self._lock_mode:
151
 
            raise BzrError('branch %r is already locked: %r' % (self, self._lock_mode))
152
 
 
153
 
        from bzrlib.lock import lock, LOCK_SH, LOCK_EX
154
 
        if mode == 'r':
155
 
            m = LOCK_SH
156
 
        elif mode == 'w':
157
 
            m = LOCK_EX
 
177
            if mode == 'w' and cur_lm == 'r':
 
178
                raise BzrError("can't upgrade to a write lock")
 
179
            
 
180
            assert self._lock_count >= 1
 
181
            self._lock_count += 1
158
182
        else:
159
 
            raise ValueError('invalid lock mode %r' % mode)
160
 
        
161
 
        lock(self._lockfile, m)
162
 
        self._lock_mode = (mode, 1)
 
183
            from bzrlib.lock import lock, LOCK_SH, LOCK_EX
 
184
            if mode == 'r':
 
185
                m = LOCK_SH
 
186
            elif mode == 'w':
 
187
                m = LOCK_EX
 
188
            else:
 
189
                raise ValueError('invalid lock mode %r' % mode)
 
190
 
 
191
            lock(self._lockfile, m)
 
192
            self._lock_mode = mode
 
193
            self._lock_count = 1
163
194
 
164
195
 
165
196
    def unlock(self):
166
197
        if not self._lock_mode:
167
198
            raise BzrError('branch %r is not locked' % (self))
168
 
        from bzrlib.lock import unlock
169
 
        unlock(self._lockfile)
170
 
        self._lock_mode = None
171
 
 
172
 
 
173
 
    def _need_readlock(self):
174
 
        if not self._lock_mode:
175
 
            raise BzrError('need read lock on branch, only have %r' % self._lockmode)
176
 
 
177
 
 
178
 
    def _need_writelock(self):
179
 
        if (self._lock_mode == None) or (self._lock_mode[0] != 'w'):
180
 
            raise BzrError('need write lock on branch, only have %r' % self._lockmode)
 
199
 
 
200
        if self._lock_count > 1:
 
201
            self._lock_count -= 1
 
202
        else:
 
203
            assert self._lock_count == 1
 
204
            from bzrlib.lock import unlock
 
205
            unlock(self._lockfile)
 
206
            self._lock_mode = self._lock_count = None
181
207
 
182
208
 
183
209
    def abspath(self, name):
268
294
                            'or remove the .bzr directory and "bzr init" again'])
269
295
 
270
296
 
 
297
 
 
298
    @with_readlock
271
299
    def read_working_inventory(self):
272
300
        """Read the working inventory."""
273
 
        self._need_readlock()
274
301
        before = time.time()
275
302
        # ElementTree does its own conversion from UTF-8, so open in
276
303
        # binary.
278
305
        mutter("loaded inventory of %d items in %f"
279
306
               % (len(inv), time.time() - before))
280
307
        return inv
281
 
 
 
308
            
282
309
 
283
310
    def _write_inventory(self, inv):
284
311
        """Update the working inventory.
286
313
        That is to say, the inventory describing changes underway, that
287
314
        will be committed to the next revision.
288
315
        """
289
 
        self._need_writelock()
290
316
        ## TODO: factor out to atomicfile?  is rename safe on windows?
291
317
        ## TODO: Maybe some kind of clean/dirty marker on inventory?
292
318
        tmpfname = self.controlfilename('inventory.tmp')
298
324
            os.remove(inv_fname)
299
325
        os.rename(tmpfname, inv_fname)
300
326
        mutter('wrote working inventory')
301
 
 
 
327
            
302
328
 
303
329
    inventory = property(read_working_inventory, _write_inventory, None,
304
330
                         """Inventory for the working copy.""")
305
331
 
306
332
 
 
333
    @with_writelock
307
334
    def add(self, files, verbose=False, ids=None):
308
335
        """Make files versioned.
309
336
 
324
351
               add all non-ignored children.  Perhaps do that in a
325
352
               higher-level method.
326
353
        """
327
 
        self._need_writelock()
328
 
 
329
354
        # TODO: Re-adding a file that is removed in the working copy
330
355
        # should probably put it back with the previous ID.
331
356
        if isinstance(files, types.StringTypes):
338
363
            ids = [None] * len(files)
339
364
        else:
340
365
            assert(len(ids) == len(files))
341
 
        
 
366
 
342
367
        inv = self.read_working_inventory()
343
368
        for f,file_id in zip(files, ids):
344
369
            if is_control_file(f):
348
373
 
349
374
            if len(fp) == 0:
350
375
                raise BzrError("cannot add top-level %r" % f)
351
 
                
 
376
 
352
377
            fullpath = os.path.normpath(self.abspath(f))
353
378
 
354
379
            try:
356
381
            except OSError:
357
382
                # maybe something better?
358
383
                raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
359
 
            
 
384
 
360
385
            if kind != 'file' and kind != 'directory':
361
386
                raise BzrError('cannot add: not a regular file or directory: %s' % quotefn(f))
362
387
 
366
391
 
367
392
            if verbose:
368
393
                show_status('A', kind, quotefn(f))
369
 
                
 
394
 
370
395
            mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
 
396
 
 
397
        self._write_inventory(inv)
371
398
            
372
 
        self._write_inventory(inv)
373
 
 
374
399
 
375
400
    def print_file(self, file, revno):
376
401
        """Print `file` to stdout."""
377
 
        self._need_readlock()
378
402
        tree = self.revision_tree(self.lookup_revision(revno))
379
403
        # use inventory as it was in that revision
380
404
        file_id = tree.inventory.path2id(file)
381
405
        if not file_id:
382
406
            raise BzrError("%r is not present in revision %d" % (file, revno))
383
407
        tree.print_file(file_id)
384
 
        
385
 
 
 
408
 
 
409
 
 
410
    @with_writelock
386
411
    def remove(self, files, verbose=False):
387
412
        """Mark nominated files for removal from the inventory.
388
413
 
399
424
        """
400
425
        ## TODO: Normalize names
401
426
        ## TODO: Remove nested loops; better scalability
402
 
        self._need_writelock()
403
 
 
404
427
        if isinstance(files, types.StringTypes):
405
428
            files = [files]
406
 
        
 
429
 
407
430
        tree = self.working_tree()
408
431
        inv = tree.inventory
409
432
 
424
447
 
425
448
        self._write_inventory(inv)
426
449
 
 
450
 
427
451
    def set_inventory(self, new_inventory_list):
428
452
        inv = Inventory()
429
453
        for path, file_id, parent, kind in new_inventory_list:
474
498
 
475
499
    def get_revision(self, revision_id):
476
500
        """Return the Revision object for a named revision"""
477
 
        self._need_readlock()
478
501
        r = Revision.read_xml(self.revision_store[revision_id])
479
502
        assert r.revision_id == revision_id
480
503
        return r
486
509
        TODO: Perhaps for this and similar methods, take a revision
487
510
               parameter which can be either an integer revno or a
488
511
               string hash."""
489
 
        self._need_readlock()
490
512
        i = Inventory.read_xml(self.inventory_store[inventory_id])
491
513
        return i
492
514
 
493
515
 
494
516
    def get_revision_inventory(self, revision_id):
495
517
        """Return inventory of a past revision."""
496
 
        self._need_readlock()
497
518
        if revision_id == None:
498
519
            return Inventory()
499
520
        else:
500
521
            return self.get_inventory(self.get_revision(revision_id).inventory_id)
501
522
 
502
523
 
 
524
    @with_readlock
503
525
    def revision_history(self):
504
526
        """Return sequence of revision hashes on to this branch.
505
527
 
506
528
        >>> ScratchBranch().revision_history()
507
529
        []
508
530
        """
509
 
        self._need_readlock()
510
531
        return [l.rstrip('\r\n') for l in self.controlfile('revision-history', 'r').readlines()]
511
532
 
512
533
 
576
597
        an `EmptyTree` is returned."""
577
598
        # TODO: refactor this to use an existing revision object
578
599
        # so we don't need to read it in twice.
579
 
        self._need_readlock()
580
600
        if revision_id == None:
581
601
            return EmptyTree()
582
602
        else:
603
623
 
604
624
 
605
625
 
 
626
    @with_writelock
606
627
    def rename_one(self, from_rel, to_rel):
607
628
        """Rename one file.
608
629
 
609
630
        This can change the directory or the filename or both.
610
631
        """
611
 
        self._need_writelock()
612
632
        tree = self.working_tree()
613
633
        inv = tree.inventory
614
634
        if not tree.has_filename(from_rel):
615
635
            raise BzrError("can't rename: old working file %r does not exist" % from_rel)
616
636
        if tree.has_filename(to_rel):
617
637
            raise BzrError("can't rename: new working file %r already exists" % to_rel)
618
 
            
 
638
 
619
639
        file_id = inv.path2id(from_rel)
620
640
        if file_id == None:
621
641
            raise BzrError("can't rename: old name %r is not versioned" % from_rel)
634
654
        mutter("  to_rel     %r" % to_rel)
635
655
        mutter("  to_dir     %r" % to_dir)
636
656
        mutter("  to_dir_id  {%s}" % to_dir_id)
637
 
            
 
657
 
638
658
        inv.rename(file_id, to_dir_id, to_tail)
639
659
 
640
660
        print "%s => %s" % (from_rel, to_rel)
641
 
        
 
661
 
642
662
        from_abs = self.abspath(from_rel)
643
663
        to_abs = self.abspath(to_rel)
644
664
        try:
649
669
                    ["rename rolled back"])
650
670
 
651
671
        self._write_inventory(inv)
652
 
            
653
 
 
654
 
 
 
672
 
 
673
 
 
674
 
 
675
    @with_writelock
655
676
    def move(self, from_paths, to_name):
656
677
        """Rename files.
657
678
 
663
684
        Note that to_name is only the last component of the new name;
664
685
        this doesn't change the directory.
665
686
        """
666
 
        self._need_writelock()
667
687
        ## TODO: Option to move IDs only
668
688
        assert not isinstance(from_paths, basestring)
669
689
        tree = self.working_tree()