~bzr-pqm/bzr/bzr.dev

« back to all changes in this revision

Viewing changes to bzrlib/branch.py

  • Committer: Martin Pool
  • Date: 2005-08-26 02:31:37 UTC
  • Revision ID: mbp@sourcefrog.net-20050826023137-eb4b101cc92f9792
- ignore tags files

Show diffs side-by-side

added added

removed removed

Lines of Context:
219
219
            self._lock.unlock()
220
220
 
221
221
 
 
222
 
222
223
    def lock_write(self):
223
224
        if self._lock_mode:
224
225
            if self._lock_mode != 'w':
234
235
            self._lock_count = 1
235
236
 
236
237
 
 
238
 
237
239
    def lock_read(self):
238
240
        if self._lock_mode:
239
241
            assert self._lock_mode in ('r', 'w'), \
246
248
            self._lock_mode = 'r'
247
249
            self._lock_count = 1
248
250
                        
 
251
 
 
252
            
249
253
    def unlock(self):
250
254
        if not self._lock_mode:
251
255
            from errors import LockError
258
262
            self._lock = None
259
263
            self._lock_mode = self._lock_count = None
260
264
 
 
265
 
261
266
    def abspath(self, name):
262
267
        """Return absolute filename for something in the branch"""
263
268
        return os.path.join(self.base, name)
264
269
 
 
270
 
265
271
    def relpath(self, path):
266
272
        """Return path relative to this branch of something inside it.
267
273
 
268
274
        Raises an error if path is not in this branch."""
269
275
        return _relpath(self.base, path)
270
276
 
 
277
 
271
278
    def controlfilename(self, file_or_path):
272
279
        """Return location relative to branch."""
273
280
        if isinstance(file_or_path, basestring):
300
307
        else:
301
308
            raise BzrError("invalid controlfile mode %r" % mode)
302
309
 
 
310
 
 
311
 
303
312
    def _make_control(self):
304
313
        from bzrlib.inventory import Inventory
305
314
        from bzrlib.xml import pack_xml
323
332
        # simplicity.
324
333
        pack_xml(Inventory(), self.controlfile('inventory','w'))
325
334
 
 
335
 
326
336
    def _check_format(self):
327
337
        """Check this branch format is supported.
328
338
 
404
414
                         """Inventory for the working copy.""")
405
415
 
406
416
 
407
 
    def add(self, files, ids=None):
 
417
    def add(self, files, verbose=False, ids=None):
408
418
        """Make files versioned.
409
419
 
410
 
        Note that the command line normally calls smart_add instead,
411
 
        which can automatically recurse.
 
420
        Note that the command line normally calls smart_add instead.
412
421
 
413
422
        This puts the files in the Added state, so that they will be
414
423
        recorded by the next commit.
424
433
        TODO: Perhaps have an option to add the ids even if the files do
425
434
              not (yet) exist.
426
435
 
427
 
        TODO: Perhaps yield the ids and paths as they're added.
 
436
        TODO: Perhaps return the ids of the files?  But then again it
 
437
              is easy to retrieve them if they're needed.
 
438
 
 
439
        TODO: Adding a directory should optionally recurse down and
 
440
              add all non-ignored children.  Perhaps do that in a
 
441
              higher-level method.
428
442
        """
429
443
        # TODO: Re-adding a file that is removed in the working copy
430
444
        # should probably put it back with the previous ID.
466
480
                    file_id = gen_file_id(f)
467
481
                inv.add_path(f, kind=kind, file_id=file_id)
468
482
 
 
483
                if verbose:
 
484
                    print 'added', quotefn(f)
 
485
 
469
486
                mutter("add file %s file_id:{%s} kind=%r" % (f, file_id, kind))
470
487
 
471
488
            self._write_inventory(inv)
819
836
        ## note("Added %d revisions." % count)
820
837
        pb.clear()
821
838
 
 
839
        
 
840
        
822
841
    def install_revisions(self, other, revision_ids, pb):
823
842
        if hasattr(other.revision_store, "prefetch"):
824
843
            other.revision_store.prefetch(revision_ids)
876
895
 
877
896
    def lookup_revision(self, revision):
878
897
        """Return the revision identifier for a given revision information."""
879
 
        revno, info = self._get_revision_info(revision)
 
898
        revno, info = self.get_revision_info(revision)
880
899
        return info
881
900
 
882
901
 
897
916
        revision can also be a string, in which case it is parsed for something like
898
917
            'date:' or 'revid:' etc.
899
918
        """
900
 
        revno, rev_id = self._get_revision_info(revision)
901
 
        if revno is None:
902
 
            raise bzrlib.errors.NoSuchRevision(self, revision)
903
 
        return revno, rev_id
904
 
 
905
 
    def get_rev_id(self, revno, history=None):
906
 
        """Find the revision id of the specified revno."""
907
 
        if revno == 0:
908
 
            return None
909
 
        if history is None:
910
 
            history = self.revision_history()
911
 
        elif revno <= 0 or revno > len(history):
912
 
            raise bzrlib.errors.NoSuchRevision(self, revno)
913
 
        return history[revno - 1]
914
 
 
915
 
    def _get_revision_info(self, revision):
916
 
        """Return (revno, revision id) for revision specifier.
917
 
 
918
 
        revision can be an integer, in which case it is assumed to be revno
919
 
        (though this will translate negative values into positive ones)
920
 
        revision can also be a string, in which case it is parsed for something
921
 
        like 'date:' or 'revid:' etc.
922
 
 
923
 
        A revid is always returned.  If it is None, the specifier referred to
924
 
        the null revision.  If the revid does not occur in the revision
925
 
        history, revno will be None.
926
 
        """
927
 
        
928
919
        if revision is None:
929
920
            return 0, None
930
921
        revno = None
934
925
            pass
935
926
        revs = self.revision_history()
936
927
        if isinstance(revision, int):
 
928
            if revision == 0:
 
929
                return 0, None
 
930
            # Mabye we should do this first, but we don't need it if revision == 0
937
931
            if revision < 0:
938
932
                revno = len(revs) + revision + 1
939
933
            else:
940
934
                revno = revision
941
 
            rev_id = self.get_rev_id(revno, revs)
942
935
        elif isinstance(revision, basestring):
943
936
            for prefix, func in Branch.REVISION_NAMESPACES.iteritems():
944
937
                if revision.startswith(prefix):
945
 
                    result = func(self, revs, revision)
946
 
                    if len(result) > 1:
947
 
                        revno, rev_id = result
948
 
                    else:
949
 
                        revno = result[0]
950
 
                        rev_id = self.get_rev_id(revno, revs)
 
938
                    revno = func(self, revs, revision)
951
939
                    break
952
940
            else:
953
 
                raise BzrError('No namespace registered for string: %r' %
954
 
                               revision)
955
 
        else:
956
 
            raise TypeError('Unhandled revision type %s' % revision)
 
941
                raise BzrError('No namespace registered for string: %r' % revision)
957
942
 
958
 
        if revno is None:
959
 
            if rev_id is None:
960
 
                raise bzrlib.errors.NoSuchRevision(self, revision)
961
 
        return revno, rev_id
 
943
        if revno is None or revno <= 0 or revno > len(revs):
 
944
            raise BzrError("no such revision %s" % revision)
 
945
        return revno, revs[revno-1]
962
946
 
963
947
    def _namespace_revno(self, revs, revision):
964
948
        """Lookup a revision by revision number"""
965
949
        assert revision.startswith('revno:')
966
950
        try:
967
 
            return (int(revision[6:]),)
 
951
            return int(revision[6:])
968
952
        except ValueError:
969
953
            return None
970
954
    REVISION_NAMESPACES['revno:'] = _namespace_revno
971
955
 
972
956
    def _namespace_revid(self, revs, revision):
973
957
        assert revision.startswith('revid:')
974
 
        rev_id = revision[len('revid:'):]
975
958
        try:
976
 
            return revs.index(rev_id) + 1, rev_id
 
959
            return revs.index(revision[6:]) + 1
977
960
        except ValueError:
978
 
            return None, rev_id
 
961
            return None
979
962
    REVISION_NAMESPACES['revid:'] = _namespace_revid
980
963
 
981
964
    def _namespace_last(self, revs, revision):
983
966
        try:
984
967
            offset = int(revision[5:])
985
968
        except ValueError:
986
 
            return (None,)
 
969
            return None
987
970
        else:
988
971
            if offset <= 0:
989
972
                raise BzrError('You must supply a positive value for --revision last:XXX')
990
 
            return (len(revs) - offset + 1,)
 
973
            return len(revs) - offset + 1
991
974
    REVISION_NAMESPACES['last:'] = _namespace_last
992
975
 
993
976
    def _namespace_tag(self, revs, revision):
1068
1051
                # TODO: Handle timezone.
1069
1052
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1070
1053
                if first >= dt and (last is None or dt >= last):
1071
 
                    return (i+1,)
 
1054
                    return i+1
1072
1055
        else:
1073
1056
            for i in range(len(revs)):
1074
1057
                r = self.get_revision(revs[i])
1075
1058
                # TODO: Handle timezone.
1076
1059
                dt = datetime.datetime.fromtimestamp(r.timestamp)
1077
1060
                if first <= dt and (last is None or dt <= last):
1078
 
                    return (i+1,)
 
1061
                    return i+1
1079
1062
    REVISION_NAMESPACES['date:'] = _namespace_date
1080
1063
 
1081
1064
    def revision_tree(self, revision_id):
1146
1129
 
1147
1130
            inv.rename(file_id, to_dir_id, to_tail)
1148
1131
 
 
1132
            print "%s => %s" % (from_rel, to_rel)
 
1133
 
1149
1134
            from_abs = self.abspath(from_rel)
1150
1135
            to_abs = self.abspath(to_rel)
1151
1136
            try:
1170
1155
 
1171
1156
        Note that to_name is only the last component of the new name;
1172
1157
        this doesn't change the directory.
1173
 
 
1174
 
        This returns a list of (from_path, to_path) pairs for each
1175
 
        entry that is moved.
1176
1158
        """
1177
 
        result = []
1178
1159
        self.lock_write()
1179
1160
        try:
1180
1161
            ## TODO: Option to move IDs only
1215
1196
            for f in from_paths:
1216
1197
                name_tail = splitpath(f)[-1]
1217
1198
                dest_path = appendpath(to_name, name_tail)
1218
 
                result.append((f, dest_path))
 
1199
                print "%s => %s" % (f, dest_path)
1219
1200
                inv.rename(inv.path2id(f), to_dir_id, name_tail)
1220
1201
                try:
1221
1202
                    os.rename(self.abspath(f), self.abspath(dest_path))
1227
1208
        finally:
1228
1209
            self.unlock()
1229
1210
 
1230
 
        return result
1231
 
 
1232
1211
 
1233
1212
    def revert(self, filenames, old_tree=None, backups=True):
1234
1213
        """Restore selected files to the versions from a previous tree.
1316
1295
            self.unlock()
1317
1296
 
1318
1297
 
1319
 
    def get_parent(self):
1320
 
        """Return the parent location of the branch.
1321
 
 
1322
 
        This is the default location for push/pull/missing.  The usual
1323
 
        pattern is that the user can override it by specifying a
1324
 
        location.
1325
 
        """
1326
 
        import errno
1327
 
        _locs = ['parent', 'pull', 'x-pull']
1328
 
        for l in _locs:
1329
 
            try:
1330
 
                return self.controlfile(l, 'r').read().strip('\n')
1331
 
            except IOError, e:
1332
 
                if e.errno != errno.ENOENT:
1333
 
                    raise
1334
 
        return None
1335
 
 
1336
 
 
1337
 
    def set_parent(self, url):
1338
 
        # TODO: Maybe delete old location files?
1339
 
        from bzrlib.atomicfile import AtomicFile
1340
 
        self.lock_write()
1341
 
        try:
1342
 
            f = AtomicFile(self.controlfilename('parent'))
1343
 
            try:
1344
 
                f.write(url + '\n')
1345
 
                f.commit()
1346
 
            finally:
1347
 
                f.close()
1348
 
        finally:
1349
 
            self.unlock()
1350
 
 
1351
 
    def check_revno(self, revno):
1352
 
        """\
1353
 
        Check whether a revno corresponds to any revision.
1354
 
        Zero (the NULL revision) is considered valid.
1355
 
        """
1356
 
        if revno != 0:
1357
 
            self.check_real_revno(revno)
1358
 
            
1359
 
    def check_real_revno(self, revno):
1360
 
        """\
1361
 
        Check whether a revno corresponds to a real revision.
1362
 
        Zero (the NULL revision) is considered invalid
1363
 
        """
1364
 
        if revno < 1 or revno > self.revno():
1365
 
            raise InvalidRevisionNumber(revno)
1366
 
        
1367
 
        
1368
 
 
1369
1298
 
1370
1299
class ScratchBranch(Branch):
1371
1300
    """Special test class: a branch that cleans up after itself.
1413
1342
        os.rmdir(base)
1414
1343
        copytree(self.base, base, symlinks=True)
1415
1344
        return ScratchBranch(base=base)
1416
 
 
1417
 
 
1418
1345
        
1419
1346
    def __del__(self):
1420
1347
        self.destroy()
1490
1417
    """Return a new tree-root file id."""
1491
1418
    return gen_file_id('TREE_ROOT')
1492
1419
 
1493
 
 
1494
 
def pull_loc(branch):
1495
 
    # TODO: Should perhaps just make attribute be 'base' in
1496
 
    # RemoteBranch and Branch?
1497
 
    if hasattr(branch, "baseurl"):
1498
 
        return branch.baseurl
1499
 
    else:
1500
 
        return branch.base
1501
 
 
1502
 
 
1503
 
def copy_branch(branch_from, to_location, revision=None):
1504
 
    """Copy branch_from into the existing directory to_location.
1505
 
 
1506
 
    revision
1507
 
        If not None, only revisions up to this point will be copied.
1508
 
        The head of the new branch will be that revision.
1509
 
 
1510
 
    to_location
1511
 
        The name of a local directory that exists but is empty.
1512
 
    """
1513
 
    from bzrlib.merge import merge
1514
 
    from bzrlib.branch import Branch
1515
 
 
1516
 
    assert isinstance(branch_from, Branch)
1517
 
    assert isinstance(to_location, basestring)
1518
 
    
1519
 
    br_to = Branch(to_location, init=True)
1520
 
    br_to.set_root_id(branch_from.get_root_id())
1521
 
    if revision is None:
1522
 
        revno = branch_from.revno()
1523
 
    else:
1524
 
        revno, rev_id = branch_from.get_revision_info(revision)
1525
 
    br_to.update_revisions(branch_from, stop_revision=revno)
1526
 
    merge((to_location, -1), (to_location, 0), this_dir=to_location,
1527
 
          check_clean=False, ignore_zero=True)
1528
 
    
1529
 
    from_location = pull_loc(branch_from)
1530
 
    br_to.set_parent(pull_loc(branch_from))
1531